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 (context
->otp
&& context
->saved_username
!= username
)
1162 context
->saved_username
= strdup(username
);
1165 if (NULL
== context
->otp_credentials
)
1166 context
->password
= strdup(password
);
1168 context
->password
= _isds_astrcat(password
,
1169 context
->otp_credentials
->otp_code
);
1171 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1173 if ((NULL
!= username
&& NULL
== context
->username
) ||
1174 (NULL
!= password
&& NULL
== context
->password
) ||
1175 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1176 (context
->otp
&& NULL
!= context
->username
&&
1177 NULL
== context
->saved_username
)) {
1186 /* Connect and log into ISDS server.
1187 * All required arguments will be copied, you do not have to keep them after
1189 * ISDS supports six different authentication methods. Exact method is
1190 * selected on @username, @password, @pki_credentials, and @otp arguments:
1191 * - If @pki_credentials == NULL, @username and @password must be supplied
1193 * - If @otp == NULL, simple authentication by username and password will
1195 * - If @otp != NULL, authentication by username and password and OTP
1197 * - If @pki_credentials != NULL, then
1198 * - If @username == NULL, only certificate will be used
1199 * - If @username != NULL, then
1200 * - If @password == NULL, then certificate will be used and
1201 * @username shifts meaning to box ID. This is used for hosted
1203 * - Otherwise all three arguments will be used.
1204 * Please note, that different cases require different certificate type
1205 * (system qualified one or commercial non qualified one). This library
1206 * does not check such political issues. Please see ISDS Specification
1208 * @url is base address of ISDS web service. Pass extern isds_locator
1209 * variable to use production ISDS instance without client certificate
1210 * authentication (or extern isds_cert_locator with client certificate
1211 * authentication or extern isds_otp_locators with OTP authentication).
1212 * Passing NULL has the same effect, autoselection between isds_locator,
1213 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1214 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1215 * isds_otp_testing_locator) variable to select testing instance.
1216 * @username is user name of ISDS user or box ID
1217 * @password is user's secret password
1218 * @pki_credentials defines public key cryptographic material to use in client
1220 * @otp selects one-time password authentication method to use, defines OTP
1221 * code (if known) and returns fine grade resolution of OTP procedure.
1223 * IE_SUCCESS if authentication succeeds
1224 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1225 * requested, fine grade reason will be set into @otp->resolution. Error
1226 * message from server can be obtained by isds_long_message() call.
1227 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1228 * server has sent OTP code through side channel. Application is expected to
1229 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1230 * this call to complete second phase of TOTP authentication;
1231 * or other appropriate error. */
1232 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1233 const char *username
, const char *password
,
1234 const struct isds_pki_credentials
*pki_credentials
,
1235 struct isds_otp
*otp
) {
1237 isds_error err
= IE_NOT_LOGGED_IN
;
1238 isds_error soap_err
;
1239 xmlNsPtr isds_ns
= NULL
;
1240 xmlNodePtr request
= NULL
;
1241 xmlNodePtr response
= NULL
;
1242 #endif /* HAVE_LIBCURL */
1244 if (!context
) return IE_INVALID_CONTEXT
;
1245 zfree(context
->long_message
);
1248 /* Close connection if already logged in */
1249 if (context
->curl
) {
1250 _isds_close_connection(context
);
1253 /* Store configuration */
1254 context
->type
= CTX_TYPE_ISDS
;
1255 zfree(context
->url
);
1257 /* Mangle base URI according to requested authentication method */
1258 if (NULL
== pki_credentials
) {
1259 isds_log(ILF_SEC
, ILL_INFO
,
1260 _("Selected authentication method: no certificate, "
1261 "username and password\n"));
1262 if (!username
|| !password
) {
1263 isds_log_message(context
,
1264 _("Both username and password must be supplied"));
1267 context
->otp_credentials
= otp
;
1268 context
->otp
= (NULL
!= context
->otp_credentials
);
1270 if (!context
->otp
) {
1271 /* Default locator is official system (without certificate or
1273 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1275 const char *authenticator_uri
= NULL
;
1276 if (!url
) url
= isds_otp_locator
;
1277 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1278 switch (context
->otp_credentials
->method
) {
1280 isds_log(ILF_SEC
, ILL_INFO
,
1281 _("Selected authentication method: "
1282 "HMAC-based one-time password\n"));
1284 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1287 isds_log(ILF_SEC
, ILL_INFO
,
1288 _("Selected authentication method: "
1289 "Time-based one-time password\n"));
1290 if (context
->otp_credentials
->otp_code
== NULL
) {
1291 isds_log(ILF_SEC
, ILL_INFO
,
1292 _("OTP code has not been provided by "
1293 "application, requesting server for "
1296 "%1$sas/processLogin?type=totp&sendSms=true&"
1299 isds_log(ILF_SEC
, ILL_INFO
,
1300 _("OTP code has been provided by "
1301 "application, not requesting server "
1304 "%1$sas/processLogin?type=totp&"
1309 isds_log_message(context
,
1310 _("Unknown one-time password authentication "
1311 "method requested by application"));
1314 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1318 /* Default locator is official system (with client certificate) */
1320 context
->otp_credentials
= NULL
;
1321 if (!url
) url
= isds_cert_locator
;
1324 isds_log(ILF_SEC
, ILL_INFO
,
1325 _("Selected authentication method: system certificate, "
1326 "no username and no password\n"));
1328 context
->url
= _isds_astrcat(url
, "cert/");
1331 isds_log(ILF_SEC
, ILL_INFO
,
1332 _("Selected authentication method: system certificate, "
1333 "box ID and no password\n"));
1334 context
->url
= _isds_astrcat(url
, "hspis/");
1336 isds_log(ILF_SEC
, ILL_INFO
,
1337 _("Selected authentication method: commercial "
1338 "certificate, username and password\n"));
1339 context
->url
= _isds_astrcat(url
, "certds/");
1343 if (!(context
->url
))
1346 /* Prepare CURL handle */
1347 context
->curl
= curl_easy_init();
1348 if (!(context
->curl
))
1351 /* Build log-in request */
1352 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1354 isds_log_message(context
, _("Could not build ISDS log-in request"));
1357 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1359 isds_log_message(context
, _("Could not create ISDS name space"));
1360 xmlFreeNode(request
);
1363 xmlSetNs(request
, isds_ns
);
1365 /* Store credentials */
1366 _isds_discard_credentials(context
, 1);
1367 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1368 _isds_discard_credentials(context
, 1);
1369 xmlFreeNode(request
);
1373 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1376 /* Send log-in request */
1377 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1380 /* Revert context URL from OTP authentication service URL to OTP web
1381 * service base URL for subsequent calls. Potenial isds_login() retry
1382 * will re-set context URL again. */
1383 zfree(context
->url
);
1384 context
->url
= _isds_astrcat(url
, "apps/");
1385 if (context
->url
== NULL
) {
1386 soap_err
= IE_NOMEM
;
1388 /* Detach pointer to OTP credentials from context */
1389 context
->otp_credentials
= NULL
;
1392 /* Remove credentials */
1393 _isds_discard_credentials(context
, 0);
1395 /* Destroy log-in request */
1396 xmlFreeNode(request
);
1399 xmlFreeNodeList(response
);
1400 _isds_close_connection(context
);
1404 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1405 * authentication succeeded if soap_err == IE_SUCCESS */
1408 xmlFreeNodeList(response
);
1411 isds_log(ILF_ISDS
, ILL_DEBUG
,
1412 _("User %s has been logged into server %s successfully\n"),
1415 #else /* not HAVE_LIBCURL */
1421 /* Log out from ISDS server discards credentials and connection configuration. */
1422 isds_error
isds_logout(struct isds_ctx
*context
) {
1423 if (!context
) return IE_INVALID_CONTEXT
;
1424 zfree(context
->long_message
);
1427 if (context
->curl
) {
1429 isds_error err
= _isds_invalidate_otp_cookie(context
);
1430 if (err
) return err
;
1433 /* Close connection */
1434 _isds_close_connection(context
);
1436 /* Discard credentials for sure. They should not survive isds_login(),
1437 * even successful .*/
1438 _isds_discard_credentials(context
, 1);
1439 zfree(context
->url
);
1441 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1443 _isds_discard_credentials(context
, 1);
1446 #else /* not HAVE_LIBCURL */
1452 /* Verify connection to ISDS is alive and server is responding.
1453 * Sent dummy request to ISDS and expect dummy response. */
1454 isds_error
isds_ping(struct isds_ctx
*context
) {
1456 isds_error soap_err
;
1457 xmlNsPtr isds_ns
= NULL
;
1458 xmlNodePtr request
= NULL
;
1459 xmlNodePtr response
= NULL
;
1460 #endif /* HAVE_LIBCURL */
1462 if (!context
) return IE_INVALID_CONTEXT
;
1463 zfree(context
->long_message
);
1466 /* Check if connection is established */
1467 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1470 /* Build dummy request */
1471 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1473 isds_log_message(context
, _("Could build ISDS dummy request"));
1476 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1478 isds_log_message(context
, _("Could not create ISDS name space"));
1479 xmlFreeNode(request
);
1482 xmlSetNs(request
, isds_ns
);
1484 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1486 /* Sent dummy request */
1487 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1489 /* Destroy log-in request */
1490 xmlFreeNode(request
);
1493 isds_log(ILF_ISDS
, ILL_DEBUG
,
1494 _("ISDS server could not be contacted\n"));
1495 xmlFreeNodeList(response
);
1499 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1500 * authentication succeeded if soap_err == IE_SUCCESS */
1501 /* TODO: ISDS documentation does not specify response body.
1502 * However real server sends back DummyOperationResponse */
1505 xmlFreeNodeList(response
);
1507 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1510 #else /* not HAVE_LIBCURL */
1516 /* Send bogus request to ISDS.
1517 * Just for test purposes */
1518 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1521 xmlNsPtr isds_ns
= NULL
;
1522 xmlNodePtr request
= NULL
;
1523 xmlDocPtr response
= NULL
;
1524 xmlChar
*code
= NULL
, *message
= NULL
;
1527 if (!context
) return IE_INVALID_CONTEXT
;
1528 zfree(context
->long_message
);
1531 /* Check if connection is established */
1532 if (!context
->curl
) {
1533 /* Testing printf message */
1534 isds_printf_message(context
, "%s", _("I said connection closed"));
1535 return IE_CONNECTION_CLOSED
;
1539 /* Build dummy request */
1540 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1542 isds_log_message(context
, _("Could build ISDS bogus request"));
1545 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1547 isds_log_message(context
, _("Could not create ISDS name space"));
1548 xmlFreeNode(request
);
1551 xmlSetNs(request
, isds_ns
);
1553 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1555 /* Sent bogus request */
1556 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1558 /* Destroy request */
1559 xmlFreeNode(request
);
1562 isds_log(ILF_ISDS
, ILL_DEBUG
,
1563 _("Processing ISDS response on bogus request failed\n"));
1564 xmlFreeDoc(response
);
1568 /* Check for response status */
1569 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1570 &code
, &message
, NULL
);
1572 isds_log(ILF_ISDS
, ILL_DEBUG
,
1573 _("ISDS response on bogus request is missing status\n"));
1576 xmlFreeDoc(response
);
1579 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1580 char *code_locale
= _isds_utf82locale((char*)code
);
1581 char *message_locale
= _isds_utf82locale((char*)message
);
1582 isds_log(ILF_ISDS
, ILL_DEBUG
,
1583 _("Server refused bogus request (code=%s, message=%s)\n"),
1584 code_locale
, message_locale
);
1585 /* XXX: Literal error messages from ISDS are Czech messages
1586 * (English sometimes) in UTF-8. It's hard to catch them for
1587 * translation. Successfully gettextized would return in locale
1588 * encoding, unsuccessfully translated would pass in UTF-8. */
1589 isds_log_message(context
, message_locale
);
1591 free(message_locale
);
1594 xmlFreeDoc(response
);
1601 xmlFreeDoc(response
);
1603 isds_log(ILF_ISDS
, ILL_DEBUG
,
1604 _("Bogus message accepted by server. This should not happen.\n"));
1607 #else /* not HAVE_LIBCURL */
1614 /* Serialize XML subtree to buffer preserving XML indentation.
1615 * @context is session context
1616 * @subtree is XML element to be serialized (with children)
1617 * @buffer is automatically reallocated buffer where serialize to
1618 * @length is size of serialized stream in bytes
1619 * @return standard error code, free @buffer in case of error */
1620 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1621 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1622 isds_error err
= IE_SUCCESS
;
1623 xmlBufferPtr xml_buffer
= NULL
;
1624 xmlSaveCtxtPtr save_ctx
= NULL
;
1625 xmlDocPtr subtree_doc
= NULL
;
1626 xmlNodePtr subtree_copy
;
1630 if (!context
) return IE_INVALID_CONTEXT
;
1631 if (!buffer
) return IE_INVAL
;
1633 if (!subtree
|| !length
) return IE_INVAL
;
1635 /* Make temporary XML document with @subtree root element */
1636 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1637 * It can result in not well-formed on invalid XML tree (e.g. name space
1638 * prefix definition can miss. */
1641 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1643 isds_log_message(context
, _("Could not build temporary document"));
1648 /* XXX: Copy subtree and attach the copy to document.
1649 * One node can not bee attached into more document at the same time.
1650 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1652 * XXX: Check xmlSaveTree() too. */
1653 subtree_copy
= xmlCopyNodeList(subtree
);
1654 if (!subtree_copy
) {
1655 isds_log_message(context
, _("Could not copy subtree"));
1659 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1661 /* Only this way we get namespace definition as @xmlns:isds,
1662 * otherwise we get namespace prefix without definition */
1663 /* FIXME: Don't overwrite original default namespace */
1664 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1666 isds_log_message(context
, _("Could not create ISDS name space"));
1670 xmlSetNs(subtree_copy
, isds_ns
);
1673 /* Serialize the document into buffer */
1674 xml_buffer
= xmlBufferCreate();
1676 isds_log_message(context
, _("Could not create xmlBuffer"));
1680 /* Last argument 0 means to not format the XML tree */
1681 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1683 isds_log_message(context
, _("Could not create XML serializer"));
1687 /* XXX: According LibXML documentation, this function does not return
1688 * meaningful value yet */
1689 xmlSaveDoc(save_ctx
, subtree_doc
);
1690 if (-1 == xmlSaveFlush(save_ctx
)) {
1691 isds_log_message(context
,
1692 _("Could not serialize XML subtree"));
1696 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1697 * even after xmlSaveFlush(). Thus close it here */
1698 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1701 /* Store and detach buffer from xml_buffer */
1702 *buffer
= xml_buffer
->content
;
1703 *length
= xml_buffer
->use
;
1704 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1707 new_buffer
= realloc(*buffer
, *length
);
1708 if (new_buffer
) *buffer
= new_buffer
;
1716 xmlSaveClose(save_ctx
);
1717 xmlBufferFree(xml_buffer
);
1718 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1721 #endif /* HAVE_LIBCURL */
1725 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1726 * @context is session context
1727 * @document is original document where @nodeset points to
1728 * @nodeset is XPath node set to dump (recursively)
1729 * @buffer is automatically reallocated buffer where serialize to
1730 * @length is size of serialized stream in bytes
1731 * @return standard error code, free @buffer in case of error */
1732 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1733 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1734 void **buffer
, size_t *length
) {
1735 isds_error err
= IE_SUCCESS
;
1736 xmlBufferPtr xml_buffer
= NULL
;
1739 if (!context
) return IE_INVALID_CONTEXT
;
1740 if (!buffer
) return IE_INVAL
;
1742 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1745 /* Empty node set results into NULL buffer */
1746 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1750 /* Resulting the document into buffer */
1751 xml_buffer
= xmlBufferCreate();
1753 isds_log_message(context
, _("Could not create xmlBuffer"));
1758 /* Iterate over all nodes */
1759 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1761 * XXX: xmlNodeDump() appends to xml_buffer. */
1763 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1764 isds_log_message(context
, _("Could not dump XML node"));
1770 /* Store and detach buffer from xml_buffer */
1771 *buffer
= xml_buffer
->content
;
1772 *length
= xml_buffer
->use
;
1773 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1776 new_buffer
= realloc(*buffer
, *length
);
1777 if (new_buffer
) *buffer
= new_buffer
;
1786 xmlBufferFree(xml_buffer
);
1792 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1793 * @context is session context
1794 * @document is original document where @nodeset points to
1795 * @nodeset is XPath node set to dump (recursively)
1796 * @buffer is automatically reallocated buffer where serialize to
1797 * @length is size of serialized stream in bytes
1798 * @return standard error code, free @buffer in case of error */
1799 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1800 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1801 void **buffer
, size_t *length
) {
1802 isds_error err
= IE_SUCCESS
;
1803 xmlBufferPtr xml_buffer
= NULL
;
1804 xmlSaveCtxtPtr save_ctx
= NULL
;
1807 if (!context
) return IE_INVALID_CONTEXT
;
1808 if (!buffer
) return IE_INVAL
;
1810 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1813 /* Empty node set results into NULL buffer */
1814 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1818 /* Resulting the document into buffer */
1819 xml_buffer
= xmlBufferCreate();
1821 isds_log_message(context
, _("Could not create xmlBuffer"));
1825 if (xmlSubstituteEntitiesDefault(1)) {
1826 isds_log_message(context
, _("Could not disable attribute escaping"));
1830 /* Last argument means:
1831 * 0 to not format the XML tree
1832 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1833 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1834 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1836 isds_log_message(context
, _("Could not create XML serializer"));
1840 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1841 isds_log_message(context, _("Could not disable attribute escaping"));
1847 /* Iterate over all nodes */
1848 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1850 * XXX: xmlNodeDump() appends to xml_buffer. */
1852 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1854 /* XXX: According LibXML documentation, this function does not return
1855 * meaningful value yet */
1856 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1857 if (-1 == xmlSaveFlush(save_ctx
)) {
1858 isds_log_message(context
,
1859 _("Could not serialize XML subtree"));
1865 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1866 * even after xmlSaveFlush(). Thus close it here */
1867 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1869 /* Store and detach buffer from xml_buffer */
1870 *buffer
= xml_buffer
->content
;
1871 *length
= xml_buffer
->use
;
1872 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1875 new_buffer
= realloc(*buffer
, *length
);
1876 if (new_buffer
) *buffer
= new_buffer
;
1884 xmlSaveClose(save_ctx
);
1885 xmlBufferFree(xml_buffer
);
1892 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1893 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1894 if (!string
|| !type
) return IE_INVAL
;
1896 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1898 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1900 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1901 *type
= DBTYPE_PFO_ADVOK
;
1902 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1903 *type
= DBTYPE_PFO_DANPOR
;
1904 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1905 *type
= DBTYPE_PFO_INSSPR
;
1906 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1908 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1909 *type
= DBTYPE_PO_ZAK
;
1910 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1911 *type
= DBTYPE_PO_REQ
;
1912 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1914 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1915 *type
= DBTYPE_OVM_NOTAR
;
1916 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1917 *type
= DBTYPE_OVM_EXEKUT
;
1918 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1919 *type
= DBTYPE_OVM_REQ
;
1926 /* Convert ISDS dbType enum @type to UTF-8 string.
1927 * @Return pointer to static string, or NULL if unknown enum value */
1928 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1930 /* DBTYPE_SYSTEM is invalid value from point of view of public
1931 * SOAP interface. */
1932 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1933 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1934 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1935 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1936 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1937 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1938 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1939 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1940 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1941 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1942 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1943 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1944 default: return NULL
; break;
1949 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1950 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
1951 if (!string
|| !type
) return IE_INVAL
;
1953 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
1954 *type
= USERTYPE_PRIMARY
;
1955 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
1956 *type
= USERTYPE_ENTRUSTED
;
1957 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
1958 *type
= USERTYPE_ADMINISTRATOR
;
1959 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
1960 *type
= USERTYPE_OFFICIAL
;
1961 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
1962 *type
= USERTYPE_OFFICIAL_CERT
;
1963 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
1964 *type
= USERTYPE_LIQUIDATOR
;
1971 /* Convert ISDS userType enum @type to UTF-8 string.
1972 * @Return pointer to static string, or NULL if unknown enum value */
1973 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
1975 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
1976 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
1977 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
1978 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
1979 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
1980 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
1981 default: return NULL
; break;
1986 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1987 static isds_error
string2isds_sender_type(const xmlChar
*string
,
1988 isds_sender_type
*type
) {
1989 if (!string
|| !type
) return IE_INVAL
;
1991 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
1992 *type
= SENDERTYPE_PRIMARY
;
1993 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
1994 *type
= SENDERTYPE_ENTRUSTED
;
1995 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
1996 *type
= SENDERTYPE_ADMINISTRATOR
;
1997 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
1998 *type
= SENDERTYPE_OFFICIAL
;
1999 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2000 *type
= SENDERTYPE_VIRTUAL
;
2001 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2002 *type
= SENDERTYPE_OFFICIAL_CERT
;
2003 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2004 *type
= SENDERTYPE_LIQUIDATOR
;
2011 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2012 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2013 isds_payment_type
*type
) {
2014 if (!string
|| !type
) return IE_INVAL
;
2016 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2017 *type
= PAYMENT_SENDER
;
2018 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2019 *type
= PAYMENT_RESPONSE
;
2020 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2021 *type
= PAYMENT_SPONSOR
;
2022 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2023 *type
= PAYMENT_SPONSOR_LIMITED
;
2024 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2025 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2026 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2027 *type
= PAYMENT_STAMP
;
2034 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2035 * @Return pointer to static string, or NULL if unknown enum value */
2036 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2038 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2039 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2040 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2041 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2042 default: return NULL
; break;
2045 #endif /* HAVE_LIBCURL */
2048 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2049 * @Return IE_ENUM if @string is not valid enum member */
2050 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2051 isds_FileMetaType
*type
) {
2052 if (!string
|| !type
) return IE_INVAL
;
2054 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2055 *type
= FILEMETATYPE_MAIN
;
2056 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2057 *type
= FILEMETATYPE_ENCLOSURE
;
2058 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2059 *type
= FILEMETATYPE_SIGNATURE
;
2060 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2061 *type
= FILEMETATYPE_META
;
2068 /* Convert UTF-8 @string to ISDS hash @algorithm.
2069 * @Return IE_ENUM if @string is not valid enum member */
2070 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2071 isds_hash_algorithm
*algorithm
) {
2072 if (!string
|| !algorithm
) return IE_INVAL
;
2074 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2075 *algorithm
= HASH_ALGORITHM_MD5
;
2076 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2077 *algorithm
= HASH_ALGORITHM_SHA_1
;
2078 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2079 *algorithm
= HASH_ALGORITHM_SHA_224
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2081 *algorithm
= HASH_ALGORITHM_SHA_256
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2083 *algorithm
= HASH_ALGORITHM_SHA_384
;
2084 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2085 *algorithm
= HASH_ALGORITHM_SHA_512
;
2093 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
2094 * XXX: Not all ISO formats are supported */
2095 static isds_error
datestring2tm(const xmlChar
*string
, struct tm
*time
) {
2097 if (!string
|| !time
) return IE_INVAL
;
2099 /* xsd:date is ISO 8601 string, thus ASCII */
2100 offset
= strptime((char*)string
, "%Y-%m-%d", time
);
2101 if (offset
&& *offset
== '\0')
2104 offset
= strptime((char*)string
, "%Y%m%d", time
);
2105 if (offset
&& *offset
== '\0')
2108 offset
= strptime((char*)string
, "%Y-%j", time
);
2109 if (offset
&& *offset
== '\0')
2116 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2117 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2118 if (!time
|| !string
) return IE_INVAL
;
2120 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2121 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2128 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2129 * respects the @time microseconds too. */
2130 static isds_error
timeval2timestring(const struct timeval
*time
,
2134 if (!time
|| !string
) return IE_INVAL
;
2136 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2137 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2139 /* TODO: small negative year should be formatted as "-0012". This is not
2140 * true for glibc "%04d". We should implement it.
2141 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2142 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2143 if (-1 == isds_asprintf((char **) string
,
2144 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2145 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2146 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2152 #endif /* HAVE_LIBCURL */
2155 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2156 * It respects microseconds too.
2157 * In case of error, @time will be freed. */
2158 static isds_error
timestring2timeval(const xmlChar
*string
,
2159 struct timeval
**time
) {
2161 char *offset
, *delim
, *endptr
;
2163 int offset_hours
, offset_minutes
;
2166 if (!time
) return IE_INVAL
;
2172 memset(&broken
, 0, sizeof(broken
));
2175 *time
= calloc(1, sizeof(**time
));
2176 if (!*time
) return IE_NOMEM
;
2178 memset(*time
, 0, sizeof(**time
));
2182 /* xsd:date is ISO 8601 string, thus ASCII */
2183 /*TODO: negative year */
2185 /* Parse date and time without subseconds and offset */
2186 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2192 /* Get subseconds */
2193 if (*offset
== '.' ) {
2196 /* Copy first 6 digits, pad it with zeros.
2197 * XXX: It truncates longer number, no round.
2198 * Current server implementation uses only millisecond resolution. */
2199 /* TODO: isdigit() is locale sensitive */
2201 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2203 subseconds
[i
] = *offset
;
2205 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2206 subseconds
[i
] = '0';
2208 subseconds
[6] = '\0';
2210 /* Convert it into integer */
2211 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2212 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2213 (*time
)->tv_usec
== LONG_MAX
) {
2218 /* move to the zone offset delimiter or signal NULL*/
2219 delim
= strchr(offset
, '-');
2221 delim
= strchr(offset
, '+');
2223 delim
= strchr(offset
, 'Z');
2227 /* Get zone offset */
2228 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2229 * "" equals to "Z" and it means UTC zone. */
2230 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2231 * colon separator */
2232 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2233 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2237 if (*offset
== '+') {
2238 broken
.tm_hour
-= offset_hours
;
2239 broken
.tm_min
-= offset_minutes
;
2241 broken
.tm_hour
+= offset_hours
;
2242 broken
.tm_min
+= offset_minutes
;
2246 /* Convert to time_t */
2247 (*time
)->tv_sec
= _isds_timegm(&broken
);
2248 if ((*time
)->tv_sec
== (time_t) -1) {
2257 /* Convert unsigned int into isds_message_status.
2258 * @context is session context
2259 * @number is pointer to number value. NULL will be treated as invalid value.
2260 * @status is automatically reallocated status
2261 * @return IE_SUCCESS, or error code and free status */
2262 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2263 const unsigned long int *number
, isds_message_status
**status
) {
2264 if (!context
) return IE_INVALID_CONTEXT
;
2265 if (!status
) return IE_INVAL
;
2267 free(*status
); *status
= NULL
;
2268 if (!number
) return IE_INVAL
;
2270 if (*number
< 1 || *number
> 10) {
2271 isds_printf_message(context
, _("Invalid message status value: %lu"),
2276 *status
= malloc(sizeof(**status
));
2277 if (!*status
) return IE_NOMEM
;
2279 **status
= 1 << *number
;
2284 /* Convert event description string into isds_event members type and
2286 * @string is raw event description starting with event prefix
2287 * @event is structure where to store type and stripped description to
2288 * @return standard error code, unknown prefix is not classified as an error.
2290 static isds_error
eventstring2event(const xmlChar
*string
,
2291 struct isds_event
* event
) {
2292 const xmlChar
*known_prefixes
[] = {
2303 const isds_event_type types
[] = {
2304 EVENT_ENTERED_SYSTEM
,
2305 EVENT_ACCEPTED_BY_RECIPIENT
,
2306 EVENT_ACCEPTED_BY_FICTION
,
2307 EVENT_UNDELIVERABLE
,
2308 EVENT_COMMERCIAL_ACCEPTED
,
2310 EVENT_PRIMARY_LOGIN
,
2311 EVENT_ENTRUSTED_LOGIN
,
2317 if (!string
|| !event
) return IE_INVAL
;
2320 event
->type
= malloc(sizeof(*event
->type
));
2321 if (!(event
->type
)) return IE_NOMEM
;
2323 zfree(event
->description
);
2325 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2327 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2329 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2330 /* Prefix is known */
2331 *event
->type
= types
[index
];
2333 /* Strip prefix from description and spaces */
2334 /* TODO: Recognize all white spaces from UCS blank class and
2335 * operate on UTF-8 chars. */
2336 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2337 event
->description
= strdup((char *) (string
+ length
));
2338 if (!(event
->description
)) return IE_NOMEM
;
2344 /* Unknown event prefix.
2345 * XSD allows any string */
2346 char *string_locale
= _isds_utf82locale((char *) string
);
2347 isds_log(ILF_ISDS
, ILL_WARNING
,
2348 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2349 free(string_locale
);
2351 *event
->type
= EVENT_UKNOWN
;
2352 event
->description
= strdup((char *) string
);
2353 if (!(event
->description
)) return IE_NOMEM
;
2359 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2360 * and leave label */
2361 #define EXTRACT_STRING(element, string) { \
2362 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2367 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2368 if (result->nodesetval->nodeNr > 1) { \
2369 isds_printf_message(context, _("Multiple %s element"), element); \
2373 (string) = (char *) \
2374 xmlXPathCastNodeSetToString(result->nodesetval); \
2382 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2384 char *string = NULL; \
2385 EXTRACT_STRING(element, string); \
2388 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2389 if (!(booleanPtr)) { \
2395 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2396 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2397 *(booleanPtr) = 1; \
2398 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2399 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2400 *(booleanPtr) = 0; \
2402 char *string_locale = _isds_utf82locale((char*)string); \
2403 isds_printf_message(context, \
2404 _("%s value is not valid boolean: %s"), \
2405 element, string_locale); \
2406 free(string_locale); \
2416 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2418 char *string = NULL; \
2419 EXTRACT_STRING(element, string); \
2424 number = strtol((char*)string, &endptr, 10); \
2426 if (*endptr != '\0') { \
2427 char *string_locale = _isds_utf82locale((char *)string); \
2428 isds_printf_message(context, \
2429 _("%s is not valid integer: %s"), \
2430 element, string_locale); \
2431 free(string_locale); \
2437 if (number == LONG_MIN || number == LONG_MAX) { \
2438 char *string_locale = _isds_utf82locale((char *)string); \
2439 isds_printf_message(context, \
2440 _("%s value out of range of long int: %s"), \
2441 element, string_locale); \
2442 free(string_locale); \
2448 free(string); string = NULL; \
2450 if (!(preallocated)) { \
2451 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2452 if (!(longintPtr)) { \
2457 *(longintPtr) = number; \
2461 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2463 char *string = NULL; \
2464 EXTRACT_STRING(element, string); \
2469 number = strtol((char*)string, &endptr, 10); \
2471 if (*endptr != '\0') { \
2472 char *string_locale = _isds_utf82locale((char *)string); \
2473 isds_printf_message(context, \
2474 _("%s is not valid integer: %s"), \
2475 element, string_locale); \
2476 free(string_locale); \
2482 if (number == LONG_MIN || number == LONG_MAX) { \
2483 char *string_locale = _isds_utf82locale((char *)string); \
2484 isds_printf_message(context, \
2485 _("%s value out of range of long int: %s"), \
2486 element, string_locale); \
2487 free(string_locale); \
2493 free(string); string = NULL; \
2495 isds_printf_message(context, \
2496 _("%s value is negative: %ld"), element, number); \
2501 if (!(preallocated)) { \
2502 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2503 if (!(ulongintPtr)) { \
2508 *(ulongintPtr) = number; \
2512 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2513 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2515 if ((required) && (!string)) { \
2516 char *attribute_locale = _isds_utf82locale(attribute); \
2517 char *element_locale = \
2518 _isds_utf82locale((char *)xpath_ctx->node->name); \
2519 isds_printf_message(context, \
2520 _("Could not extract required %s attribute value from " \
2521 "%s element"), attribute_locale, element_locale); \
2522 free(element_locale); \
2523 free(attribute_locale); \
2530 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2532 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2533 (xmlChar *) (string)); \
2535 isds_printf_message(context, \
2536 _("Could not add %s child to %s element"), \
2537 element, (parent)->name); \
2543 #define INSERT_STRING(parent, element, string) \
2544 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2546 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2548 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2549 else { INSERT_STRING(parent, element, "false"); } \
2552 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2555 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2557 INSERT_STRING(parent, element, NULL); \
2561 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2562 if ((longintPtr)) { \
2563 /* FIXME: locale sensitive */ \
2564 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2568 INSERT_STRING(parent, element, buffer) \
2569 free(buffer); (buffer) = NULL; \
2570 } else { INSERT_STRING(parent, element, NULL) } \
2573 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2574 if ((ulongintPtr)) { \
2575 /* FIXME: locale sensitive */ \
2576 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2580 INSERT_STRING(parent, element, buffer) \
2581 free(buffer); (buffer) = NULL; \
2582 } else { INSERT_STRING(parent, element, NULL) } \
2585 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2587 /* FIXME: locale sensitive */ \
2588 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2592 INSERT_STRING(parent, element, buffer) \
2593 free(buffer); (buffer) = NULL; \
2596 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2598 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2600 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2601 (xmlChar *) (string)); \
2602 if (!attribute_node) { \
2603 isds_printf_message(context, _("Could not add %s " \
2604 "attribute to %s element"), \
2605 (attribute), (parent)->name); \
2611 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2613 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2614 if (length > (maximum)) { \
2615 isds_printf_message(context, \
2616 ngettext("%s has more than %d characters", \
2617 "%s has more than %d characters", (maximum)), \
2618 (name), (maximum)); \
2622 if (length < (minimum)) { \
2623 isds_printf_message(context, \
2624 ngettext("%s has less than %d characters", \
2625 "%s has less than %d characters", (minimum)), \
2626 (name), (minimum)); \
2633 #define INSERT_ELEMENT(child, parent, element) \
2635 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2637 isds_printf_message(context, \
2638 _("Could not add %s child to %s element"), \
2639 (element), (parent)->name); \
2646 /* Find child element by name in given XPath context and switch context onto
2647 * it. The child must be uniq and must exist. Otherwise fails.
2648 * @context is ISDS context
2649 * @child is child element name
2650 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2651 * into it child. In error case, the @xpath_ctx keeps original value. */
2652 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2653 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2654 isds_error err
= IE_SUCCESS
;
2655 xmlXPathObjectPtr result
= NULL
;
2657 if (!context
) return IE_INVALID_CONTEXT
;
2658 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2661 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2668 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2669 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2670 char *child_locale
= _isds_utf82locale((char*) child
);
2671 isds_printf_message(context
,
2672 _("%s element does not contain %s child"),
2673 parent_locale
, child_locale
);
2675 free(parent_locale
);
2681 if (result
->nodesetval
->nodeNr
> 1) {
2682 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2683 char *child_locale
= _isds_utf82locale((char*) child
);
2684 isds_printf_message(context
,
2685 _("%s element contains multiple %s children"),
2686 parent_locale
, child_locale
);
2688 free(parent_locale
);
2693 /* Switch context */
2694 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2697 xmlXPathFreeObject(result
);
2704 /* Find and convert XSD:gPersonName group in current node into structure
2705 * @context is ISDS context
2706 * @personName is automatically reallocated person name structure. If no member
2707 * value is found, will be freed.
2708 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2710 * In case of error @personName will be freed. */
2711 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2712 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2713 isds_error err
= IE_SUCCESS
;
2714 xmlXPathObjectPtr result
= NULL
;
2716 if (!context
) return IE_INVALID_CONTEXT
;
2717 if (!personName
) return IE_INVAL
;
2718 isds_PersonName_free(personName
);
2719 if (!xpath_ctx
) return IE_INVAL
;
2722 *personName
= calloc(1, sizeof(**personName
));
2728 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2729 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2730 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2731 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2733 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2734 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2735 isds_PersonName_free(personName
);
2738 if (err
) isds_PersonName_free(personName
);
2739 xmlXPathFreeObject(result
);
2744 /* Find and convert XSD:gAddress group in current node into structure
2745 * @context is ISDS context
2746 * @address is automatically reallocated address structure. If no member
2747 * value is found, will be freed.
2748 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2750 * In case of error @address will be freed. */
2751 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2752 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2753 isds_error err
= IE_SUCCESS
;
2754 xmlXPathObjectPtr result
= NULL
;
2756 if (!context
) return IE_INVALID_CONTEXT
;
2757 if (!address
) return IE_INVAL
;
2758 isds_Address_free(address
);
2759 if (!xpath_ctx
) return IE_INVAL
;
2762 *address
= calloc(1, sizeof(**address
));
2768 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2769 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2770 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2771 EXTRACT_STRING("isds:adNumberInMunicipality",
2772 (*address
)->adNumberInMunicipality
);
2773 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2774 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2776 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2777 !(*address
)->adNumberInStreet
&&
2778 !(*address
)->adNumberInMunicipality
&&
2779 !(*address
)->adZipCode
&& !(*address
)->adState
)
2780 isds_Address_free(address
);
2783 if (err
) isds_Address_free(address
);
2784 xmlXPathFreeObject(result
);
2789 /* Find and convert isds:biDate element in current node into structure
2790 * @context is ISDS context
2791 * @biDate is automatically reallocated birth date structure. If no member
2792 * value is found, will be freed.
2793 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2795 * In case of error @biDate will be freed. */
2796 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2797 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2798 isds_error err
= IE_SUCCESS
;
2799 xmlXPathObjectPtr result
= NULL
;
2800 char *string
= NULL
;
2802 if (!context
) return IE_INVALID_CONTEXT
;
2803 if (!biDate
) return IE_INVAL
;
2805 if (!xpath_ctx
) return IE_INVAL
;
2807 EXTRACT_STRING("isds:biDate", string
);
2809 *biDate
= calloc(1, sizeof(**biDate
));
2814 err
= datestring2tm((xmlChar
*)string
, *biDate
);
2816 if (err
== IE_NOTSUP
) {
2818 char *string_locale
= _isds_utf82locale(string
);
2819 isds_printf_message(context
,
2820 _("Invalid isds:biDate value: %s"), string_locale
);
2821 free(string_locale
);
2828 if (err
) zfree(*biDate
);
2830 xmlXPathFreeObject(result
);
2835 /* Convert isds:dBOwnerInfo XML tree into structure
2836 * @context is ISDS context
2837 * @db_owner_info is automatically reallocated box owner info structure
2838 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2839 * In case of error @db_owner_info will be freed. */
2840 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2841 struct isds_DbOwnerInfo
**db_owner_info
,
2842 xmlXPathContextPtr xpath_ctx
) {
2843 isds_error err
= IE_SUCCESS
;
2844 xmlXPathObjectPtr result
= NULL
;
2845 char *string
= NULL
;
2847 if (!context
) return IE_INVALID_CONTEXT
;
2848 if (!db_owner_info
) return IE_INVAL
;
2849 isds_DbOwnerInfo_free(db_owner_info
);
2850 if (!xpath_ctx
) return IE_INVAL
;
2853 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2854 if (!*db_owner_info
) {
2859 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2861 EXTRACT_STRING("isds:dbType", string
);
2863 (*db_owner_info
)->dbType
=
2864 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2865 if (!(*db_owner_info
)->dbType
) {
2869 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2871 zfree((*db_owner_info
)->dbType
);
2872 if (err
== IE_ENUM
) {
2874 char *string_locale
= _isds_utf82locale(string
);
2875 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2877 free(string_locale
);
2884 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2886 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2888 if (err
) goto leave
;
2890 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
2892 (*db_owner_info
)->birthInfo
=
2893 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
2894 if (!(*db_owner_info
)->birthInfo
) {
2898 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
2900 if (err
) goto leave
;
2901 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
2902 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
2903 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
2904 if (!(*db_owner_info
)->birthInfo
->biDate
&&
2905 !(*db_owner_info
)->birthInfo
->biCity
&&
2906 !(*db_owner_info
)->birthInfo
->biCounty
&&
2907 !(*db_owner_info
)->birthInfo
->biState
)
2908 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
2910 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
2911 if (err
) goto leave
;
2913 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
2914 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
2915 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
2916 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
2917 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
2919 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
2921 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
2922 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2923 (*db_owner_info
)->dbOpenAddressing
);
2926 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
2928 xmlXPathFreeObject(result
);
2933 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2934 * @context is session context
2935 * @owner is libisds structure with box description
2936 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2937 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
2938 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
2940 isds_error err
= IE_SUCCESS
;
2942 xmlChar
*string
= NULL
;
2944 if (!context
) return IE_INVALID_CONTEXT
;
2945 if (!owner
|| !db_owner_info
) return IE_INVAL
;
2948 /* Build XSD:tDbOwnerInfo */
2949 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
2950 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
2953 if (owner
->dbType
) {
2954 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
2956 isds_printf_message(context
, _("Invalid dbType value: %d"),
2961 INSERT_STRING(db_owner_info
, "dbType", type_string
);
2963 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
2964 if (owner
->personName
) {
2965 INSERT_STRING(db_owner_info
, "pnFirstName",
2966 owner
->personName
->pnFirstName
);
2967 INSERT_STRING(db_owner_info
, "pnMiddleName",
2968 owner
->personName
->pnMiddleName
);
2969 INSERT_STRING(db_owner_info
, "pnLastName",
2970 owner
->personName
->pnLastName
);
2971 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
2972 owner
->personName
->pnLastNameAtBirth
);
2974 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
2975 if (owner
->birthInfo
) {
2976 if (owner
->birthInfo
->biDate
) {
2977 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
2978 INSERT_STRING(db_owner_info
, "biDate", string
);
2979 free(string
); string
= NULL
;
2981 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
2982 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
2983 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
2985 if (owner
->address
) {
2986 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
2987 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
2988 INSERT_STRING(db_owner_info
, "adNumberInStreet",
2989 owner
->address
->adNumberInStreet
);
2990 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
2991 owner
->address
->adNumberInMunicipality
);
2992 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
2993 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
2995 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
2996 INSERT_STRING(db_owner_info
, "email", owner
->email
);
2997 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
2999 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3000 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3002 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3003 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3005 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3007 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3008 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3009 owner
->dbOpenAddressing
);
3017 /* Convert XSD:tDbUserInfo XML tree into structure
3018 * @context is ISDS context
3019 * @db_user_info is automatically reallocated user info structure
3020 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3021 * In case of error @db_user_info will be freed. */
3022 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3023 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3024 isds_error err
= IE_SUCCESS
;
3025 xmlXPathObjectPtr result
= NULL
;
3026 char *string
= NULL
;
3028 if (!context
) return IE_INVALID_CONTEXT
;
3029 if (!db_user_info
) return IE_INVAL
;
3030 isds_DbUserInfo_free(db_user_info
);
3031 if (!xpath_ctx
) return IE_INVAL
;
3034 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3035 if (!*db_user_info
) {
3040 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3042 EXTRACT_STRING("isds:userType", string
);
3044 (*db_user_info
)->userType
=
3045 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3046 if (!(*db_user_info
)->userType
) {
3050 err
= string2isds_UserType((xmlChar
*)string
,
3051 (*db_user_info
)->userType
);
3053 zfree((*db_user_info
)->userType
);
3054 if (err
== IE_ENUM
) {
3056 char *string_locale
= _isds_utf82locale(string
);
3057 isds_printf_message(context
,
3058 _("Unknown isds:userType value: %s"), string_locale
);
3059 free(string_locale
);
3066 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3068 (*db_user_info
)->personName
=
3069 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3070 if (!(*db_user_info
)->personName
) {
3075 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3077 if (err
) goto leave
;
3079 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3080 if (err
) goto leave
;
3082 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3083 if (err
) goto leave
;
3085 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3086 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3088 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3089 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3090 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3092 /* ???: Default value is "CZ" according specification. Should we provide
3094 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3097 if (err
) isds_DbUserInfo_free(db_user_info
);
3099 xmlXPathFreeObject(result
);
3104 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3105 * @context is session context
3106 * @user is libisds structure with user description
3107 * @db_user_info is XML element of XSD:tDbUserInfo */
3108 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3109 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3111 isds_error err
= IE_SUCCESS
;
3113 xmlChar
*string
= NULL
;
3115 if (!context
) return IE_INVALID_CONTEXT
;
3116 if (!user
|| !db_user_info
) return IE_INVAL
;
3118 /* Build XSD:tDbUserInfo */
3119 if (user
->personName
) {
3120 INSERT_STRING(db_user_info
, "pnFirstName",
3121 user
->personName
->pnFirstName
);
3122 INSERT_STRING(db_user_info
, "pnMiddleName",
3123 user
->personName
->pnMiddleName
);
3124 INSERT_STRING(db_user_info
, "pnLastName",
3125 user
->personName
->pnLastName
);
3126 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3127 user
->personName
->pnLastNameAtBirth
);
3129 if (user
->address
) {
3130 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3131 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3132 INSERT_STRING(db_user_info
, "adNumberInStreet",
3133 user
->address
->adNumberInStreet
);
3134 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3135 user
->address
->adNumberInMunicipality
);
3136 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3137 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3140 if (!tm2datestring(user
->biDate
, &string
))
3141 INSERT_STRING(db_user_info
, "biDate", string
);
3144 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3145 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3148 if (user
->userType
) {
3149 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3151 isds_printf_message(context
, _("Invalid userType value: %d"),
3156 INSERT_STRING(db_user_info
, "userType", type_string
);
3159 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3160 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3161 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3162 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3163 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3164 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3165 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3166 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3167 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3175 /* Convert XSD:tPDZRec XML tree into structure
3176 * @context is ISDS context
3177 * @permission is automatically reallocated commercial permission structure
3178 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3179 * In case of error @permission will be freed. */
3180 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3181 struct isds_commercial_permission
**permission
,
3182 xmlXPathContextPtr xpath_ctx
) {
3183 isds_error err
= IE_SUCCESS
;
3184 xmlXPathObjectPtr result
= NULL
;
3185 char *string
= NULL
;
3187 if (!context
) return IE_INVALID_CONTEXT
;
3188 if (!permission
) return IE_INVAL
;
3189 isds_commercial_permission_free(permission
);
3190 if (!xpath_ctx
) return IE_INVAL
;
3193 *permission
= calloc(1, sizeof(**permission
));
3199 EXTRACT_STRING("isds:PDZType", string
);
3201 err
= string2isds_payment_type((xmlChar
*)string
,
3202 &(*permission
)->type
);
3204 if (err
== IE_ENUM
) {
3206 char *string_locale
= _isds_utf82locale(string
);
3207 isds_printf_message(context
,
3208 _("Unknown isds:PDZType value: %s"), string_locale
);
3209 free(string_locale
);
3216 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3217 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3219 EXTRACT_STRING("isds:PDZExpire", string
);
3221 err
= timestring2timeval((xmlChar
*) string
,
3222 &((*permission
)->expiration
));
3224 char *string_locale
= _isds_utf82locale(string
);
3225 if (err
== IE_DATE
) err
= IE_ISDS
;
3226 isds_printf_message(context
,
3227 _("Could not convert PDZExpire as ISO time: %s"),
3229 free(string_locale
);
3235 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3236 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3239 if (err
) isds_commercial_permission_free(permission
);
3241 xmlXPathFreeObject(result
);
3246 #endif /* HAVE_LIBCURL */
3249 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3250 * isds_envelope structure. The envelope is automatically allocated but not
3251 * reallocated. The date are just appended into envelope structure.
3252 * @context is ISDS context
3253 * @envelope is automatically allocated message envelope structure
3254 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3255 * In case of error @envelope will be freed. */
3256 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3257 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3258 isds_error err
= IE_SUCCESS
;
3259 xmlXPathObjectPtr result
= NULL
;
3261 if (!context
) return IE_INVALID_CONTEXT
;
3262 if (!envelope
) return IE_INVAL
;
3263 if (!xpath_ctx
) return IE_INVAL
;
3267 /* Allocate envelope */
3268 *envelope
= calloc(1, sizeof(**envelope
));
3274 /* Else free former data */
3275 zfree((*envelope
)->dmSenderOrgUnit
);
3276 zfree((*envelope
)->dmSenderOrgUnitNum
);
3277 zfree((*envelope
)->dbIDRecipient
);
3278 zfree((*envelope
)->dmRecipientOrgUnit
);
3279 zfree((*envelope
)->dmSenderOrgUnitNum
);
3280 zfree((*envelope
)->dmToHands
);
3281 zfree((*envelope
)->dmAnnotation
);
3282 zfree((*envelope
)->dmRecipientRefNumber
);
3283 zfree((*envelope
)->dmSenderRefNumber
);
3284 zfree((*envelope
)->dmRecipientIdent
);
3285 zfree((*envelope
)->dmSenderIdent
);
3286 zfree((*envelope
)->dmLegalTitleLaw
);
3287 zfree((*envelope
)->dmLegalTitleYear
);
3288 zfree((*envelope
)->dmLegalTitleSect
);
3289 zfree((*envelope
)->dmLegalTitlePar
);
3290 zfree((*envelope
)->dmLegalTitlePoint
);
3291 zfree((*envelope
)->dmPersonalDelivery
);
3292 zfree((*envelope
)->dmAllowSubstDelivery
);
3295 /* Extract envelope elements added by sender or ISDS
3296 * (XSD: gMessageEnvelopeSub type) */
3297 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3298 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3299 (*envelope
)->dmSenderOrgUnitNum
, 0);
3300 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3301 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3302 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3303 (*envelope
)->dmSenderOrgUnitNum
, 0);
3304 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3305 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3306 EXTRACT_STRING("isds:dmRecipientRefNumber",
3307 (*envelope
)->dmRecipientRefNumber
);
3308 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3309 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3310 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3312 /* Extract envelope elements regarding law reference */
3313 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3314 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3315 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3316 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3317 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3319 /* Extract envelope other elements */
3320 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3321 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3322 (*envelope
)->dmAllowSubstDelivery
);
3325 if (err
) isds_envelope_free(envelope
);
3326 xmlXPathFreeObject(result
);
3332 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3333 * isds_envelope structure. The envelope is automatically allocated but not
3334 * reallocated. The date are just appended into envelope structure.
3335 * @context is ISDS context
3336 * @envelope is automatically allocated message envelope structure
3337 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3338 * In case of error @envelope will be freed. */
3339 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3340 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3341 isds_error err
= IE_SUCCESS
;
3342 xmlXPathObjectPtr result
= NULL
;
3344 if (!context
) return IE_INVALID_CONTEXT
;
3345 if (!envelope
) return IE_INVAL
;
3346 if (!xpath_ctx
) return IE_INVAL
;
3350 /* Allocate envelope */
3351 *envelope
= calloc(1, sizeof(**envelope
));
3357 /* Else free former data */
3358 zfree((*envelope
)->dmID
);
3359 zfree((*envelope
)->dbIDSender
);
3360 zfree((*envelope
)->dmSender
);
3361 zfree((*envelope
)->dmSenderAddress
);
3362 zfree((*envelope
)->dmSenderType
);
3363 zfree((*envelope
)->dmRecipient
);
3364 zfree((*envelope
)->dmRecipientAddress
);
3365 zfree((*envelope
)->dmAmbiguousRecipient
);
3368 /* Extract envelope elements added by ISDS
3369 * (XSD: gMessageEnvelope type) */
3370 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3371 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3372 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3373 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3374 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3375 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3376 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3377 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3378 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3379 (*envelope
)->dmAmbiguousRecipient
);
3381 /* Extract envelope elements added by sender and ISDS
3382 * (XSD: gMessageEnvelope type) */
3383 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3384 if (err
) goto leave
;
3387 if (err
) isds_envelope_free(envelope
);
3388 xmlXPathFreeObject(result
);
3393 /* Convert other envelope elements from XML tree into isds_envelope structure:
3394 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3395 * The envelope is automatically allocated but not reallocated.
3396 * The data are just appended into envelope structure.
3397 * @context is ISDS context
3398 * @envelope is automatically allocated message envelope structure
3399 * @xpath_ctx is XPath context with current node as parent desired elements
3400 * In case of error @envelope will be freed. */
3401 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3402 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3403 isds_error err
= IE_SUCCESS
;
3404 xmlXPathObjectPtr result
= NULL
;
3405 char *string
= NULL
;
3406 unsigned long int *unumber
= NULL
;
3408 if (!context
) return IE_INVALID_CONTEXT
;
3409 if (!envelope
) return IE_INVAL
;
3410 if (!xpath_ctx
) return IE_INVAL
;
3415 *envelope
= calloc(1, sizeof(**envelope
));
3422 zfree((*envelope
)->dmMessageStatus
);
3423 zfree((*envelope
)->dmAttachmentSize
);
3424 zfree((*envelope
)->dmDeliveryTime
);
3425 zfree((*envelope
)->dmAcceptanceTime
);
3429 /* dmMessageStatus element is mandatory */
3430 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3432 isds_log_message(context
,
3433 _("Missing mandatory sisds:dmMessageStatus integer"));
3437 err
= uint2isds_message_status(context
, unumber
,
3438 &((*envelope
)->dmMessageStatus
));
3440 if (err
== IE_ENUM
) err
= IE_ISDS
;
3443 free(unumber
); unumber
= NULL
;
3445 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3448 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3450 err
= timestring2timeval((xmlChar
*) string
,
3451 &((*envelope
)->dmDeliveryTime
));
3453 char *string_locale
= _isds_utf82locale(string
);
3454 if (err
== IE_DATE
) err
= IE_ISDS
;
3455 isds_printf_message(context
,
3456 _("Could not convert dmDeliveryTime as ISO time: %s"),
3458 free(string_locale
);
3464 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3466 err
= timestring2timeval((xmlChar
*) string
,
3467 &((*envelope
)->dmAcceptanceTime
));
3469 char *string_locale
= _isds_utf82locale(string
);
3470 if (err
== IE_DATE
) err
= IE_ISDS
;
3471 isds_printf_message(context
,
3472 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3474 free(string_locale
);
3481 if (err
) isds_envelope_free(envelope
);
3484 xmlXPathFreeObject(result
);
3489 /* Convert message type attribute of current element into isds_envelope
3491 * TODO: This function can be incorporated into append_status_size_times() as
3492 * they are called always together.
3493 * The envelope is automatically allocated but not reallocated.
3494 * The data are just appended into envelope structure.
3495 * @context is ISDS context
3496 * @envelope is automatically allocated message envelope structure
3497 * @xpath_ctx is XPath context with current node as parent of attribute
3498 * carrying message type
3499 * In case of error @envelope will be freed. */
3500 static isds_error
append_message_type(struct isds_ctx
*context
,
3501 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3502 isds_error err
= IE_SUCCESS
;
3504 if (!context
) return IE_INVALID_CONTEXT
;
3505 if (!envelope
) return IE_INVAL
;
3506 if (!xpath_ctx
) return IE_INVAL
;
3511 *envelope
= calloc(1, sizeof(**envelope
));
3518 zfree((*envelope
)->dmType
);
3522 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3524 if (!(*envelope
)->dmType
) {
3525 /* Use default value */
3526 (*envelope
)->dmType
= strdup("V");
3527 if (!(*envelope
)->dmType
) {
3531 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3532 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3533 isds_printf_message(context
,
3534 _("Message type in dmType attribute is not 1 character long: "
3543 if (err
) isds_envelope_free(envelope
);
3549 /* Convert dmType isds_envelope member into XML attribute and append it to
3551 * @context is ISDS context
3552 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3553 * @dm_envelope is XML element the resulting attribute will be appended to.
3554 * @return error code, in case of error context' message is filled. */
3555 static isds_error
insert_message_type(struct isds_ctx
*context
,
3556 const char *type
, xmlNodePtr dm_envelope
) {
3557 isds_error err
= IE_SUCCESS
;
3558 xmlAttrPtr attribute_node
;
3560 if (!context
) return IE_INVALID_CONTEXT
;
3561 if (!dm_envelope
) return IE_INVAL
;
3563 /* Insert optional message type */
3565 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3566 char *type_locale
= _isds_utf82locale(type
);
3567 isds_printf_message(context
,
3568 _("Message type in envelope is not 1 character long: %s"),
3574 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3580 #endif /* HAVE_LIBCURL */
3583 /* Extract message document into reallocated document structure
3584 * @context is ISDS context
3585 * @document is automatically reallocated message documents structure
3586 * @xpath_ctx is XPath context with current node as isds:dmFile
3587 * In case of error @document will be freed. */
3588 static isds_error
extract_document(struct isds_ctx
*context
,
3589 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3590 isds_error err
= IE_SUCCESS
;
3591 xmlXPathObjectPtr result
= NULL
;
3592 xmlNodePtr file_node
= xpath_ctx
->node
;
3593 char *string
= NULL
;
3595 if (!context
) return IE_INVALID_CONTEXT
;
3596 if (!document
) return IE_INVAL
;
3597 isds_document_free(document
);
3598 if (!xpath_ctx
) return IE_INVAL
;
3600 *document
= calloc(1, sizeof(**document
));
3606 /* Extract document meta data */
3607 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3608 if (context
->normalize_mime_type
) {
3609 char *normalized_type
=
3610 isds_normalize_mime_type((*document
)->dmMimeType
);
3611 if (normalized_type
&& normalized_type
!= (*document
)->dmMimeType
) {
3612 char *new_type
= strdup(normalized_type
);
3614 isds_printf_message(context
,
3615 _("Not enough memory to normalize document MIME type"));
3619 free((*document
)->dmMimeType
);
3620 (*document
)->dmMimeType
= new_type
;
3624 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3625 err
= string2isds_FileMetaType((xmlChar
*)string
,
3626 &((*document
)->dmFileMetaType
));
3628 char *meta_type_locale
= _isds_utf82locale(string
);
3629 isds_printf_message(context
,
3630 _("Document has invalid dmFileMetaType attribute value: %s"),
3632 free(meta_type_locale
);
3638 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3639 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3640 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3641 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3644 /* Extract document data.
3645 * Base64 encoded blob or XML subtree must be presented. */
3647 /* Check for dmEncodedContent */
3648 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3655 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3656 /* Here we have Base64 blob */
3657 (*document
)->is_xml
= 0;
3659 if (result
->nodesetval
->nodeNr
> 1) {
3660 isds_printf_message(context
,
3661 _("Document has more dmEncodedContent elements"));
3666 xmlXPathFreeObject(result
); result
= NULL
;
3667 EXTRACT_STRING("isds:dmEncodedContent", string
);
3669 /* Decode non-empty document */
3670 if (string
&& string
[0] != '\0') {
3671 (*document
)->data_length
=
3672 _isds_b64decode(string
, &((*document
)->data
));
3673 if ((*document
)->data_length
== (size_t) -1) {
3674 isds_printf_message(context
,
3675 _("Error while Base64-decoding document content"));
3681 /* No Base64 blob, try XML document */
3682 xmlXPathFreeObject(result
); result
= NULL
;
3683 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3690 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3691 /* Here we have XML document */
3692 (*document
)->is_xml
= 1;
3694 if (result
->nodesetval
->nodeNr
> 1) {
3695 isds_printf_message(context
,
3696 _("Document has more dmXMLContent elements"));
3701 /* XXX: We cannot serialize the content simply because:
3702 * - XML document may point out of its scope (e.g. to message
3704 * - isds:dmXMLContent can contain more elements, no element,
3706 * - it's not the XML way
3707 * Thus we provide the only right solution: XML DOM. Let's
3708 * application to cope with this hot potato :) */
3709 (*document
)->xml_node_list
=
3710 result
->nodesetval
->nodeTab
[0]->children
;
3712 /* No base64 blob, nor XML document */
3713 isds_printf_message(context
,
3714 _("Document has no dmEncodedContent, nor dmXMLContent "
3723 if (err
) isds_document_free(document
);
3725 xmlXPathFreeObject(result
);
3726 xpath_ctx
->node
= file_node
;
3732 /* Extract message documents into reallocated list of documents
3733 * @context is ISDS context
3734 * @documents is automatically reallocated message documents list structure
3735 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3736 * In case of error @documents will be freed. */
3737 static isds_error
extract_documents(struct isds_ctx
*context
,
3738 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3739 isds_error err
= IE_SUCCESS
;
3740 xmlXPathObjectPtr result
= NULL
;
3741 xmlNodePtr files_node
= xpath_ctx
->node
;
3742 struct isds_list
*document
, *prev_document
= NULL
;
3744 if (!context
) return IE_INVALID_CONTEXT
;
3745 if (!documents
) return IE_INVAL
;
3746 isds_list_free(documents
);
3747 if (!xpath_ctx
) return IE_INVAL
;
3749 /* Find documents */
3750 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3757 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3758 isds_printf_message(context
,
3759 _("Message does not contain any document"));
3765 /* Iterate over documents */
3766 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3768 /* Allocate and append list item */
3769 document
= calloc(1, sizeof(*document
));
3774 document
->destructor
= (void (*)(void **))isds_document_free
;
3775 if (i
== 0) *documents
= document
;
3776 else prev_document
->next
= document
;
3777 prev_document
= document
;
3779 /* Extract document */
3780 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3781 err
= extract_document(context
,
3782 (struct isds_document
**) &(document
->data
), xpath_ctx
);
3783 if (err
) goto leave
;
3788 if (err
) isds_list_free(documents
);
3789 xmlXPathFreeObject(result
);
3790 xpath_ctx
->node
= files_node
;
3796 /* Convert isds:dmRecord XML tree into structure
3797 * @context is ISDS context
3798 * @envelope is automatically reallocated message envelope structure
3799 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3800 * In case of error @envelope will be freed. */
3801 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
3802 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3803 isds_error err
= IE_SUCCESS
;
3804 xmlXPathObjectPtr result
= NULL
;
3806 if (!context
) return IE_INVALID_CONTEXT
;
3807 if (!envelope
) return IE_INVAL
;
3808 isds_envelope_free(envelope
);
3809 if (!xpath_ctx
) return IE_INVAL
;
3812 *envelope
= calloc(1, sizeof(**envelope
));
3819 /* Extract tRecord data */
3820 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
3822 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3823 * dmAcceptanceTime. */
3824 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
3825 if (err
) goto leave
;
3827 /* Extract envelope elements added by sender and ISDS
3828 * (XSD: gMessageEnvelope type) */
3829 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
3830 if (err
) goto leave
;
3832 /* Get message type */
3833 err
= append_message_type(context
, envelope
, xpath_ctx
);
3834 if (err
) goto leave
;
3838 if (err
) isds_envelope_free(envelope
);
3839 xmlXPathFreeObject(result
);
3844 /* Convert XSD:tStateChangesRecord type XML tree into structure
3845 * @context is ISDS context
3846 * @changed_status is automatically reallocated message state change structure
3847 * @xpath_ctx is XPath context with current node as element of
3848 * XSD:tStateChangesRecord type
3849 * In case of error @changed_status will be freed. */
3850 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
3851 struct isds_message_status_change
**changed_status
,
3852 xmlXPathContextPtr xpath_ctx
) {
3853 isds_error err
= IE_SUCCESS
;
3854 xmlXPathObjectPtr result
= NULL
;
3855 unsigned long int *unumber
= NULL
;
3856 char *string
= NULL
;
3858 if (!context
) return IE_INVALID_CONTEXT
;
3859 if (!changed_status
) return IE_INVAL
;
3860 isds_message_status_change_free(changed_status
);
3861 if (!xpath_ctx
) return IE_INVAL
;
3864 *changed_status
= calloc(1, sizeof(**changed_status
));
3865 if (!*changed_status
) {
3871 /* Extract tGetStateChangesInput data */
3872 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
3874 /* dmEventTime is mandatory */
3875 EXTRACT_STRING("isds:dmEventTime", string
);
3877 err
= timestring2timeval((xmlChar
*) string
,
3878 &((*changed_status
)->time
));
3880 char *string_locale
= _isds_utf82locale(string
);
3881 if (err
== IE_DATE
) err
= IE_ISDS
;
3882 isds_printf_message(context
,
3883 _("Could not convert dmEventTime as ISO time: %s"),
3885 free(string_locale
);
3891 /* dmMessageStatus element is mandatory */
3892 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
3894 isds_log_message(context
,
3895 _("Missing mandatory isds:dmMessageStatus integer"));
3899 err
= uint2isds_message_status(context
, unumber
,
3900 &((*changed_status
)->dmMessageStatus
));
3902 if (err
== IE_ENUM
) err
= IE_ISDS
;
3911 if (err
) isds_message_status_change_free(changed_status
);
3912 xmlXPathFreeObject(result
);
3915 #endif /* HAVE_LIBCURL */
3918 /* Find and convert isds:dmHash XML tree into structure
3919 * @context is ISDS context
3920 * @envelope is automatically reallocated message hash structure
3921 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3922 * In case of error @hash will be freed. */
3923 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
3924 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
3925 isds_error err
= IE_SUCCESS
;
3926 xmlNodePtr old_ctx_node
;
3927 xmlXPathObjectPtr result
= NULL
;
3928 char *string
= NULL
;
3930 if (!context
) return IE_INVALID_CONTEXT
;
3931 if (!hash
) return IE_INVAL
;
3932 isds_hash_free(hash
);
3933 if (!xpath_ctx
) return IE_INVAL
;
3935 old_ctx_node
= xpath_ctx
->node
;
3937 *hash
= calloc(1, sizeof(**hash
));
3944 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
3945 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
3954 /* Get hash algorithm */
3955 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
3956 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
3958 if (err
== IE_ENUM
) {
3959 char *string_locale
= _isds_utf82locale(string
);
3960 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
3962 free(string_locale
);
3968 /* Get hash value */
3969 EXTRACT_STRING(".", string
);
3971 isds_printf_message(context
,
3972 _("sisds:dmHash element is missing hash value"));
3976 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
3977 if ((*hash
)->length
== (size_t) -1) {
3978 isds_printf_message(context
,
3979 _("Error while Base64-decoding hash value"));
3985 if (err
) isds_hash_free(hash
);
3987 xmlXPathFreeObject(result
);
3988 xpath_ctx
->node
= old_ctx_node
;
3993 /* Find and append isds:dmQTimestamp XML tree into envelope.
3994 * Because one service is allowed to miss time-stamp content, and we think
3995 * other could too (flaw in specification), this function is deliberated and
3996 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3997 * @context is ISDS context
3998 * @envelope is automatically allocated envelope structure
3999 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4001 * In case of error @envelope will be freed. */
4002 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4003 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4004 isds_error err
= IE_SUCCESS
;
4005 xmlXPathObjectPtr result
= NULL
;
4006 char *string
= NULL
;
4008 if (!context
) return IE_INVALID_CONTEXT
;
4009 if (!envelope
) return IE_INVAL
;
4011 isds_envelope_free(envelope
);
4016 *envelope
= calloc(1, sizeof(**envelope
));
4022 zfree((*envelope
)->timestamp
);
4023 (*envelope
)->timestamp_length
= 0;
4026 /* Get dmQTimestamp */
4027 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4029 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4032 (*envelope
)->timestamp_length
=
4033 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4034 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4035 isds_printf_message(context
,
4036 _("Error while Base64-decoding time stamp value"));
4042 if (err
) isds_envelope_free(envelope
);
4044 xmlXPathFreeObject(result
);
4049 /* Convert XSD tReturnedMessage XML tree into message structure.
4050 * It does not store serialized XML tree into message->raw.
4051 * It does store (pointer to) parsed XML tree into message->xml if needed.
4052 * @context is ISDS context
4053 * @include_documents Use true if documents must be extracted
4054 * (tReturnedMessage XSD type), use false if documents shall be omitted
4055 * (tReturnedMessageEnvelope).
4056 * @message is automatically reallocated message structure
4057 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4059 * In case of error @message will be freed. */
4060 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4061 const _Bool include_documents
, struct isds_message
**message
,
4062 xmlXPathContextPtr xpath_ctx
) {
4063 isds_error err
= IE_SUCCESS
;
4064 xmlNodePtr message_node
;
4066 if (!context
) return IE_INVALID_CONTEXT
;
4067 if (!message
) return IE_INVAL
;
4068 isds_message_free(message
);
4069 if (!xpath_ctx
) return IE_INVAL
;
4072 *message
= calloc(1, sizeof(**message
));
4078 /* Save message XPATH context node */
4079 message_node
= xpath_ctx
->node
;
4083 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4084 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4085 if (err
) { err
= IE_ERROR
; goto leave
; }
4086 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4087 if (err
) goto leave
;
4089 if (include_documents
) {
4090 struct isds_list
*item
;
4092 /* Extract dmFiles */
4093 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4095 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4096 err
= IE_ISDS
; goto leave
;
4098 if (err
) { err
= IE_ERROR
; goto leave
; }
4099 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4100 if (err
) goto leave
;
4102 /* Store xmlDoc of this message if needed */
4103 /* Only if we got a XML document in all the documents. */
4104 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4105 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4106 (*message
)->xml
= xpath_ctx
->doc
;
4113 /* Restore context to message */
4114 xpath_ctx
->node
= message_node
;
4116 /* Extract dmHash */
4117 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4119 if (err
) goto leave
;
4121 /* Extract dmQTimestamp, */
4122 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4124 if (err
) goto leave
;
4126 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4127 * dmAcceptanceTime. */
4128 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4129 if (err
) goto leave
;
4131 /* Get message type */
4132 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4133 if (err
) goto leave
;
4136 if (err
) isds_message_free(message
);
4141 /* Extract message event into reallocated isds_event structure
4142 * @context is ISDS context
4143 * @event is automatically reallocated message event structure
4144 * @xpath_ctx is XPath context with current node as isds:dmEvent
4145 * In case of error @event will be freed. */
4146 static isds_error
extract_event(struct isds_ctx
*context
,
4147 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4148 isds_error err
= IE_SUCCESS
;
4149 xmlXPathObjectPtr result
= NULL
;
4150 xmlNodePtr event_node
= xpath_ctx
->node
;
4151 char *string
= NULL
;
4153 if (!context
) return IE_INVALID_CONTEXT
;
4154 if (!event
) return IE_INVAL
;
4155 isds_event_free(event
);
4156 if (!xpath_ctx
) return IE_INVAL
;
4158 *event
= calloc(1, sizeof(**event
));
4164 /* Extract event data.
4165 * All elements are optional according XSD. That's funny. */
4166 EXTRACT_STRING("sisds:dmEventTime", string
);
4168 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4170 char *string_locale
= _isds_utf82locale(string
);
4171 if (err
== IE_DATE
) err
= IE_ISDS
;
4172 isds_printf_message(context
,
4173 _("Could not convert dmEventTime as ISO time: %s"),
4175 free(string_locale
);
4181 /* dmEventDescr element has prefix and the rest */
4182 EXTRACT_STRING("sisds:dmEventDescr", string
);
4184 err
= eventstring2event((xmlChar
*) string
, *event
);
4185 if (err
) goto leave
;
4190 if (err
) isds_event_free(event
);
4192 xmlXPathFreeObject(result
);
4193 xpath_ctx
->node
= event_node
;
4198 /* Convert element of XSD tEventsArray type from XML tree into
4199 * isds_list of isds_event's structure. The list is automatically reallocated.
4200 * @context is ISDS context
4201 * @events is automatically reallocated list of event structures
4202 * @xpath_ctx is XPath context with current node as tEventsArray
4203 * In case of error @events will be freed. */
4204 static isds_error
extract_events(struct isds_ctx
*context
,
4205 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4206 isds_error err
= IE_SUCCESS
;
4207 xmlXPathObjectPtr result
= NULL
;
4208 xmlNodePtr events_node
= xpath_ctx
->node
;
4209 struct isds_list
*event
, *prev_event
= NULL
;
4211 if (!context
) return IE_INVALID_CONTEXT
;
4212 if (!events
) return IE_INVAL
;
4213 if (!xpath_ctx
) return IE_INVAL
;
4216 isds_list_free(events
);
4219 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4226 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4227 isds_printf_message(context
,
4228 _("Delivery info does not contain any event"));
4234 /* Iterate over events */
4235 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4237 /* Allocate and append list item */
4238 event
= calloc(1, sizeof(*event
));
4243 event
->destructor
= (void (*)(void **))isds_event_free
;
4244 if (i
== 0) *events
= event
;
4245 else prev_event
->next
= event
;
4249 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4250 err
= extract_event(context
,
4251 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4252 if (err
) goto leave
;
4257 if (err
) isds_list_free(events
);
4258 xmlXPathFreeObject(result
);
4259 xpath_ctx
->node
= events_node
;
4265 /* Insert Base64 encoded data as element with text child.
4266 * @context is session context
4267 * @parent is XML node to append @element with @data as child
4268 * @ns is XML namespace of @element, use NULL to inherit from @parent
4269 * @element is UTF-8 encoded name of new element
4270 * @data is bit stream to encode into @element
4271 * @length is size of @data in bytes
4272 * @return standard error code and fill long error message if needed */
4273 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4274 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4275 const void *data
, size_t length
) {
4276 isds_error err
= IE_SUCCESS
;
4279 if (!context
) return IE_INVALID_CONTEXT
;
4280 if (!data
&& length
> 0) return IE_INVAL
;
4281 if (!parent
|| !element
) return IE_INVAL
;
4283 xmlChar
*base64data
= NULL
;
4284 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4286 isds_printf_message(context
,
4287 ngettext("Not enough memory to encode %zd byte into Base64",
4288 "Not enough memory to encode %zd bytes into Base64",
4294 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4302 /* Convert isds_document structure into XML tree and append to dmFiles node.
4303 * @context is session context
4304 * @document is ISDS document
4305 * @dm_files is XML element the resulting tree will be appended to as a child.
4306 * @return error code, in case of error context' message is filled. */
4307 static isds_error
insert_document(struct isds_ctx
*context
,
4308 struct isds_document
*document
, xmlNodePtr dm_files
) {
4309 isds_error err
= IE_SUCCESS
;
4310 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4311 xmlAttrPtr attribute_node
;
4313 if (!context
) return IE_INVALID_CONTEXT
;
4314 if (!document
|| !dm_files
) return IE_INVAL
;
4316 /* Allocate new dmFile */
4317 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4319 isds_printf_message(context
, _("Could not allocate main dmFile"));
4323 /* Append the new dmFile.
4324 * XXX: Main document must go first */
4325 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4326 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4328 file
= xmlAddChild(dm_files
, new_file
);
4331 xmlFreeNode(new_file
); new_file
= NULL
;
4332 isds_printf_message(context
, _("Could not add dmFile child to "
4333 "%s element"), dm_files
->name
);
4338 /* @dmMimeType is required */
4339 if (!document
->dmMimeType
) {
4340 isds_log_message(context
,
4341 _("Document is missing mandatory MIME type definition"));
4345 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4347 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4349 isds_printf_message(context
,
4350 _("Document has unknown dmFileMetaType: %ld"),
4351 document
->dmFileMetaType
);
4355 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4357 if (document
->dmFileGuid
) {
4358 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4360 if (document
->dmUpFileGuid
) {
4361 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4364 /* @dmFileDescr is required */
4365 if (!document
->dmFileDescr
) {
4366 isds_log_message(context
,
4367 _("Document is missing mandatory description (title)"));
4371 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4373 if (document
->dmFormat
) {
4374 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4378 /* Insert content (body) of the document. */
4379 if (document
->is_xml
) {
4380 /* XML document requested */
4382 /* Allocate new dmXMLContent */
4383 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4385 isds_printf_message(context
,
4386 _("Could not allocate dmXMLContent element"));
4391 node
= xmlAddChild(file
, xmlcontent
);
4393 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4394 isds_printf_message(context
,
4395 _("Could not add dmXMLContent child to %s element"),
4401 /* Copy non-empty node list */
4402 if (document
->xml_node_list
) {
4403 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4404 document
->xml_node_list
);
4406 isds_printf_message(context
,
4407 _("Not enough memory to copy XML document"));
4412 if (!xmlAddChildList(node
, content
)) {
4413 xmlFreeNodeList(content
);
4414 isds_printf_message(context
,
4415 _("Error while adding XML document into dmXMLContent"));
4419 /* XXX: We cannot free the content here because it's part of node's
4420 * document since now. It will be freed with it automatically. */
4423 /* Binary document requested */
4424 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4425 document
->data
, document
->data_length
);
4426 if (err
) goto leave
;
4434 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4435 * The copy must be preallocated, the date are just appended into structure.
4436 * @context is ISDS context
4437 * @copy is message copy structure
4438 * @xpath_ctx is XPath context with current node as tMStatus */
4439 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4440 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4441 isds_error err
= IE_SUCCESS
;
4442 xmlXPathObjectPtr result
= NULL
;
4443 char *code
= NULL
, *message
= NULL
;
4445 if (!context
) return IE_INVALID_CONTEXT
;
4446 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4448 /* Free old values */
4449 zfree(copy
->dmStatus
);
4452 /* Get error specific to this copy */
4453 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4455 isds_log_message(context
,
4456 _("Missing isds:dmStatusCode under "
4457 "XSD:tMStatus type element"));
4462 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4463 /* This copy failed */
4464 copy
->error
= IE_ISDS
;
4465 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4467 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4468 if (!copy
->dmStatus
) {
4469 copy
->dmStatus
= code
;
4473 copy
->dmStatus
= code
;
4477 /* This copy succeeded. In this case only, message ID is valid */
4478 copy
->error
= IE_SUCCESS
;
4480 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4482 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4483 "but did not returned assigned message ID\n"));
4491 xmlXPathFreeObject(result
);
4496 /* Insert struct isds_approval data (box approval) into XML tree
4497 * @context is session context
4498 * @approval is libisds structure with approval description. NULL is
4500 * @parent is XML element to append @approval to */
4501 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4502 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4504 isds_error err
= IE_SUCCESS
;
4507 if (!context
) return IE_INVALID_CONTEXT
;
4508 if (!parent
) return IE_INVAL
;
4510 if (!approval
) return IE_SUCCESS
;
4512 /* Build XSD:gExtApproval */
4513 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4514 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4521 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4523 * @context is session context
4524 * @service_name is name of SERVICE_DB_ACCESS
4525 * @response is server SOAP body response as XML document
4526 * @raw_response is automatically reallocated bit stream with response body. Use
4527 * NULL if you don't care
4528 * @raw_response_length is size of @raw_response in bytes
4529 * @code is ISDS status code
4530 * @status_message is ISDS status message
4531 * @return error coded from lower layer, context message will be set up
4533 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4534 const xmlChar
*service_name
,
4535 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4536 xmlChar
**code
, xmlChar
**status_message
) {
4538 isds_error err
= IE_SUCCESS
;
4539 char *service_name_locale
= NULL
;
4540 xmlNodePtr request
= NULL
, node
;
4541 xmlNsPtr isds_ns
= NULL
;
4543 if (!context
) return IE_INVALID_CONTEXT
;
4544 if (!service_name
) return IE_INVAL
;
4545 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4546 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4548 /* Free output argument */
4549 xmlFreeDoc(*response
); *response
= NULL
;
4550 if (raw_response
) zfree(*raw_response
);
4552 free(*status_message
);
4555 /* Check if connection is established
4556 * TODO: This check should be done downstairs. */
4557 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4559 service_name_locale
= _isds_utf82locale((char*)service_name
);
4560 if (!service_name_locale
) {
4566 request
= xmlNewNode(NULL
, service_name
);
4568 isds_printf_message(context
,
4569 _("Could not build %s request"), service_name_locale
);
4573 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4575 isds_log_message(context
, _("Could not create ISDS name space"));
4579 xmlSetNs(request
, isds_ns
);
4582 /* Add XSD:tDummyInput child */
4583 INSERT_STRING(request
, "dbDummy", NULL
);
4586 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4587 service_name_locale
);
4590 err
= isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4591 raw_response
, raw_response_length
);
4592 xmlFreeNode(request
); request
= NULL
;
4595 isds_log(ILF_ISDS
, ILL_DEBUG
,
4596 _("Processing ISDS response on %s request failed\n"),
4597 service_name_locale
);
4601 /* Check for response status */
4602 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4603 code
, status_message
, NULL
);
4605 isds_log(ILF_ISDS
, ILL_DEBUG
,
4606 _("ISDS response on %s request is missing status\n"),
4607 service_name_locale
);
4611 /* Request processed, but nothing found */
4612 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4613 char *code_locale
= _isds_utf82locale((char*) *code
);
4614 char *status_message_locale
=
4615 _isds_utf82locale((char*) *status_message
);
4616 isds_log(ILF_ISDS
, ILL_DEBUG
,
4617 _("Server refused %s request (code=%s, message=%s)\n"),
4618 service_name_locale
, code_locale
, status_message_locale
);
4619 isds_log_message(context
, status_message_locale
);
4621 free(status_message_locale
);
4627 free(service_name_locale
);
4628 xmlFreeNode(request
);
4634 /* Get data about logged in user and his box. */
4635 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4636 struct isds_DbOwnerInfo
**db_owner_info
) {
4637 isds_error err
= IE_SUCCESS
;
4639 xmlDocPtr response
= NULL
;
4640 xmlChar
*code
= NULL
, *message
= NULL
;
4641 xmlXPathContextPtr xpath_ctx
= NULL
;
4642 xmlXPathObjectPtr result
= NULL
;
4643 char *string
= NULL
;
4646 if (!context
) return IE_INVALID_CONTEXT
;
4647 zfree(context
->long_message
);
4648 if (!db_owner_info
) return IE_INVAL
;
4649 isds_DbOwnerInfo_free(db_owner_info
);
4652 /* Check if connection is established */
4653 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4656 /* Do request and check for success */
4657 err
= build_send_check_dbdummy_request(context
,
4658 BAD_CAST
"GetOwnerInfoFromLogin",
4659 &response
, NULL
, NULL
, &code
, &message
);
4660 if (err
) goto leave
;
4664 /* Prepare structure */
4665 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4666 if (!*db_owner_info
) {
4670 xpath_ctx
= xmlXPathNewContext(response
);
4675 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4680 /* Set context node */
4681 result
= xmlXPathEvalExpression(BAD_CAST
4682 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4687 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4688 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4692 if (result
->nodesetval
->nodeNr
> 1) {
4693 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4697 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4698 xmlXPathFreeObject(result
); result
= NULL
;
4701 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4706 isds_DbOwnerInfo_free(db_owner_info
);
4710 xmlXPathFreeObject(result
);
4711 xmlXPathFreeContext(xpath_ctx
);
4715 xmlFreeDoc(response
);
4718 isds_log(ILF_ISDS
, ILL_DEBUG
,
4719 _("GetOwnerInfoFromLogin request processed by server "
4720 "successfully.\n"));
4721 #else /* not HAVE_LIBCURL */
4729 /* Get data about logged in user. */
4730 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4731 struct isds_DbUserInfo
**db_user_info
) {
4732 isds_error err
= IE_SUCCESS
;
4734 xmlDocPtr response
= NULL
;
4735 xmlChar
*code
= NULL
, *message
= NULL
;
4736 xmlXPathContextPtr xpath_ctx
= NULL
;
4737 xmlXPathObjectPtr result
= NULL
;
4740 if (!context
) return IE_INVALID_CONTEXT
;
4741 zfree(context
->long_message
);
4742 if (!db_user_info
) return IE_INVAL
;
4743 isds_DbUserInfo_free(db_user_info
);
4746 /* Check if connection is established */
4747 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4750 /* Do request and check for success */
4751 err
= build_send_check_dbdummy_request(context
,
4752 BAD_CAST
"GetUserInfoFromLogin",
4753 &response
, NULL
, NULL
, &code
, &message
);
4754 if (err
) goto leave
;
4758 /* Prepare structure */
4759 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4760 if (!*db_user_info
) {
4764 xpath_ctx
= xmlXPathNewContext(response
);
4769 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4774 /* Set context node */
4775 result
= xmlXPathEvalExpression(BAD_CAST
4776 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
4781 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4782 isds_log_message(context
, _("Missing dbUserInfo element"));
4786 if (result
->nodesetval
->nodeNr
> 1) {
4787 isds_log_message(context
, _("Multiple dbUserInfo element"));
4791 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4792 xmlXPathFreeObject(result
); result
= NULL
;
4795 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
4799 isds_DbUserInfo_free(db_user_info
);
4802 xmlXPathFreeObject(result
);
4803 xmlXPathFreeContext(xpath_ctx
);
4807 xmlFreeDoc(response
);
4810 isds_log(ILF_ISDS
, ILL_DEBUG
,
4811 _("GetUserInfoFromLogin request processed by server "
4812 "successfully.\n"));
4813 #else /* not HAVE_LIBCURL */
4821 /* Get expiration time of current password
4822 * @context is session context
4823 * @expiration is automatically reallocated time when password expires. If
4824 * password expiration is disables, NULL will be returned. In case of error
4825 * it will be nulled too. */
4826 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
4827 struct timeval
**expiration
) {
4828 isds_error err
= IE_SUCCESS
;
4830 xmlDocPtr response
= NULL
;
4831 xmlChar
*code
= NULL
, *message
= NULL
;
4832 xmlXPathContextPtr xpath_ctx
= NULL
;
4833 xmlXPathObjectPtr result
= NULL
;
4834 char *string
= NULL
;
4837 if (!context
) return IE_INVALID_CONTEXT
;
4838 zfree(context
->long_message
);
4839 if (!expiration
) return IE_INVAL
;
4843 /* Check if connection is established */
4844 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4847 /* Do request and check for success */
4848 err
= build_send_check_dbdummy_request(context
,
4849 BAD_CAST
"GetPasswordInfo",
4850 &response
, NULL
, NULL
, &code
, &message
);
4851 if (err
) goto leave
;
4855 xpath_ctx
= xmlXPathNewContext(response
);
4860 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4865 /* Set context node */
4866 result
= xmlXPathEvalExpression(BAD_CAST
4867 "/isds:GetPasswordInfoResponse", xpath_ctx
);
4872 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4873 isds_log_message(context
,
4874 _("Missing GetPasswordInfoResponse element"));
4878 if (result
->nodesetval
->nodeNr
> 1) {
4879 isds_log_message(context
,
4880 _("Multiple GetPasswordInfoResponse element"));
4884 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4885 xmlXPathFreeObject(result
); result
= NULL
;
4887 /* Extract expiration date */
4888 EXTRACT_STRING("isds:pswExpDate", string
);
4890 /* And convert it if any returned. Otherwise expiration is disabled. */
4891 err
= timestring2timeval((xmlChar
*) string
, expiration
);
4893 char *string_locale
= _isds_utf82locale(string
);
4894 if (err
== IE_DATE
) err
= IE_ISDS
;
4895 isds_printf_message(context
,
4896 _("Could not convert pswExpDate as ISO time: %s"),
4898 free(string_locale
);
4911 xmlXPathFreeObject(result
);
4912 xmlXPathFreeContext(xpath_ctx
);
4916 xmlFreeDoc(response
);
4919 isds_log(ILF_ISDS
, ILL_DEBUG
,
4920 _("GetPasswordInfo request processed by server "
4921 "successfully.\n"));
4922 #else /* not HAVE_LIBCURL */
4931 /* Request delivering new TOTP code from ISDS through side channel before
4932 * changing password.
4933 * @context is session context
4934 * @password is current password.
4935 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4936 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4937 * function for more details.
4938 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4940 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
4941 const char *password
, struct isds_otp
*otp
) {
4942 isds_error err
= IE_SUCCESS
;
4943 char *saved_url
= NULL
; /* No copy */
4944 xmlNsPtr isds_ns
= NULL
;
4945 xmlNodePtr request
= NULL
;
4946 xmlDocPtr response
= NULL
;
4947 xmlChar
*code
= NULL
, *message
= NULL
;
4948 const xmlChar
*codes
[] = {
4953 const char *meanings
[] = {
4954 N_("Unexpected error"),
4955 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4956 N_("One-time code could not been sent. Try later again.")
4958 const isds_otp_resolution resolutions
[] = {
4959 OTP_RESOLUTION_UNKNOWN
,
4960 OTP_RESOLUTION_TO_FAST
,
4961 OTP_RESOLUTION_TOTP_NOT_SENT
4964 if (NULL
== context
) return IE_INVALID_CONTEXT
;
4965 zfree(context
->long_message
);
4966 if (NULL
== password
) {
4967 isds_log_message(context
,
4968 _("Second argument (password) of isds_change_password() "
4973 /* Check if connection is established
4974 * TODO: This check should be done downstairs. */
4975 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4977 if (!context
->otp
) {
4978 isds_log_message(context
, _("This function requires OTP-authenticated "
4980 return IE_INVALID_CONTEXT
;
4983 isds_log_message(context
, _("If one-time password authentication "
4984 "method is in use, requesting new OTP code requires "
4985 "one-time credentials argument either"));
4988 if (otp
->method
!= OTP_TIME
) {
4989 isds_log_message(context
, _("Requesting new time-based OTP code from "
4990 "server requires one-time password authentication "
4994 if (otp
->otp_code
!= NULL
) {
4995 isds_log_message(context
, _("Requesting new time-based OTP code from "
4996 "server requires undefined OTP code member in "
4997 "one-time credentials argument"));
5003 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5005 isds_log_message(context
, _("Could not build SendSMSCode request"));
5008 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5010 isds_log_message(context
, _("Could not create ISDS name space"));
5011 xmlFreeNode(request
);
5014 xmlSetNs(request
, isds_ns
);
5016 /* Change URL temporarily for sending this request only */
5018 char *new_url
= NULL
;
5019 if ((err
= _isds_build_url_from_context(context
,
5020 "%1$.*2$sasws/changePassword", &new_url
))) {
5023 saved_url
= context
->url
;
5024 context
->url
= new_url
;
5027 /* Store credentials for sending this request only */
5028 context
->otp_credentials
= otp
;
5029 _isds_discard_credentials(context
, 0);
5030 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5032 _isds_discard_credentials(context
, 0);
5036 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5039 err
= isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5041 /* Remove temporal credentials */
5042 _isds_discard_credentials(context
, 0);
5043 /* Detach pointer to OTP credentials from context */
5044 context
->otp_credentials
= NULL
;
5045 /* Keep context->otp true to keep signaling this is OTP session */
5047 /* Destroy request */
5048 xmlFreeNode(request
); request
= NULL
;
5051 isds_log(ILF_ISDS
, ILL_DEBUG
,
5052 _("Processing ISDS response on SendSMSCode request failed\n"));
5056 /* Check for response status */
5057 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5058 &code
, &message
, NULL
);
5060 isds_log(ILF_ISDS
, ILL_DEBUG
,
5061 _("ISDS response on SendSMSCode request is missing "
5066 /* Check for error */
5067 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5068 char *code_locale
= _isds_utf82locale((char*)code
);
5069 char *message_locale
= _isds_utf82locale((char*)message
);
5071 isds_log(ILF_ISDS
, ILL_DEBUG
,
5072 _("Server refused to send new code on SendSMSCode "
5073 "request (code=%s, message=%s)\n"),
5074 code_locale
, message_locale
);
5076 /* Check for known error codes */
5077 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5078 if (!xmlStrcmp(code
, codes
[i
])) break;
5080 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5081 isds_log_message(context
, _(meanings
[i
]));
5082 /* Mimic otp->resolution according to the code, specification does
5083 * prescribe OTP header to be available. */
5084 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5085 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5086 otp
->resolution
= resolutions
[i
];
5088 isds_log_message(context
, message_locale
);
5091 free(message_locale
);
5097 /* Otherwise new code sent successfully */
5098 /* Mimic otp->resolution according to the code, specification does
5099 * prescribe OTP header to be available. */
5100 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5101 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5104 if (NULL
!= saved_url
) {
5105 /* Revert URL to original one */
5106 zfree(context
->url
);
5107 context
->url
= saved_url
;
5112 xmlFreeDoc(response
);
5113 xmlFreeNode(request
);
5116 isds_log(ILF_ISDS
, ILL_DEBUG
,
5117 _("New OTP code has been sent successfully on SendSMSCode "
5124 /* Change user password in ISDS.
5125 * User must supply old password, new password will takes effect after some
5126 * time, current session can continue. Password must fulfill some constraints.
5127 * @context is session context
5128 * @old_password is current password.
5129 * @new_password is requested new password
5130 * @otp auxiliary data required if one-time password authentication is in use,
5131 * defines OTP code (if known) and returns fine grade resolution of OTP
5132 * procedure. Pass NULL, if one-time password authentication is not needed.
5133 * Please note the @otp argument must match OTP method used at log-in time. See
5134 * isds_login() function for more details.
5135 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5136 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5137 * awaiting OTP code that has been delivered by side channel to the user. */
5138 isds_error
isds_change_password(struct isds_ctx
*context
,
5139 const char *old_password
, const char *new_password
,
5140 struct isds_otp
*otp
) {
5141 isds_error err
= IE_SUCCESS
;
5143 char *saved_url
= NULL
; /* No copy */
5144 xmlNsPtr isds_ns
= NULL
;
5145 xmlNodePtr request
= NULL
, node
;
5146 xmlDocPtr response
= NULL
;
5147 xmlChar
*code
= NULL
, *message
= NULL
;
5148 const xmlChar
*codes
[] = {
5161 const char *meanings
[] = {
5162 N_("Password length must be between 8 and 32 characters"),
5163 N_("New password must differ from the current one"),
5164 N_("Password contains forbidden character"),
5165 N_("Password must contain at least one upper-case letter, "
5166 "one lower-case, and one digit"),
5167 N_("Password cannot contain sequence of three identical characters"),
5168 N_("Password cannot contain user identifier"),
5169 N_("Password is too simmple"),
5170 N_("Old password is not valid"),
5171 N_("Passwords cannot be reused"),
5172 N_("Unexpected error"),
5173 N_("LDAP update error")
5177 if (!context
) return IE_INVALID_CONTEXT
;
5178 zfree(context
->long_message
);
5179 if (NULL
== old_password
) {
5180 isds_log_message(context
,
5181 _("Second argument (old password) of isds_change_password() "
5185 if (NULL
== new_password
) {
5186 isds_log_message(context
,
5187 _("Third argument (new password) of isds_change_password() "
5193 /* Check if connection is established
5194 * TODO: This check should be done downstairs. */
5195 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5197 if (context
->otp
&& NULL
== otp
) {
5198 isds_log_message(context
, _("If one-time password authentication "
5199 "method is in use, changing password requires one-time "
5200 "credentials either"));
5204 /* Build ChangeISDSPassword request */
5205 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5206 BAD_CAST
"ChangePasswordOTP");
5208 isds_log_message(context
, (NULL
== otp
) ?
5209 _("Could not build ChangeISDSPassword request") :
5210 _("Could not build ChangePasswordOTP request"));
5213 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5215 isds_log_message(context
, _("Could not create ISDS name space"));
5216 xmlFreeNode(request
);
5219 xmlSetNs(request
, isds_ns
);
5221 INSERT_STRING(request
, "dbOldPassword", old_password
);
5222 INSERT_STRING(request
, "dbNewPassword", new_password
);
5225 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5226 switch (otp
->method
) {
5228 isds_log(ILF_SEC
, ILL_INFO
,
5229 _("Selected authentication method: "
5230 "HMAC-based one-time password\n"));
5231 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5234 isds_log(ILF_SEC
, ILL_INFO
,
5235 _("Selected authentication method: "
5236 "Time-based one-time password\n"));
5237 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5238 if (otp
->otp_code
== NULL
) {
5239 isds_log(ILF_SEC
, ILL_INFO
,
5240 _("OTP code has not been provided by "
5241 "application, requesting server for "
5243 err
= _isds_request_totp_code(context
, old_password
, otp
);
5244 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5248 isds_log(ILF_SEC
, ILL_INFO
,
5249 _("OTP code has been provided by "
5250 "application, not requesting server "
5255 isds_log_message(context
,
5256 _("Unknown one-time password authentication "
5257 "method requested by application"));
5262 /* Change URL temporarily for sending this request only */
5264 char *new_url
= NULL
;
5265 if ((err
= _isds_build_url_from_context(context
,
5266 "%1$.*2$sasws/changePassword", &new_url
))) {
5269 saved_url
= context
->url
;
5270 context
->url
= new_url
;
5273 /* Store credentials for sending this request only */
5274 context
->otp_credentials
= otp
;
5275 _isds_discard_credentials(context
, 0);
5276 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5277 old_password
, NULL
))) {
5278 _isds_discard_credentials(context
, 0);
5284 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5285 _("Sending ChangeISDSPassword request to ISDS\n") :
5286 _("Sending ChangePasswordOTP request to ISDS\n"));
5289 err
= isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5290 request
, &response
, NULL
, NULL
);
5293 /* Remove temporal credentials */
5294 _isds_discard_credentials(context
, 0);
5295 /* Detach pointer to OTP credentials from context */
5296 context
->otp_credentials
= NULL
;
5297 /* Keep context->otp true to keep signaling this is OTP session */
5300 /* Destroy request */
5301 xmlFreeNode(request
); request
= NULL
;
5304 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5305 _("Processing ISDS response on ChangeISDSPassword "
5306 "request failed\n") :
5307 _("Processing ISDS response on ChangePasswordOTP "
5308 "request failed\n"));
5312 /* Check for response status */
5313 err
= isds_response_status(context
,
5314 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5315 &code
, &message
, NULL
);
5317 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5318 _("ISDS response on ChangeISDSPassword request is missing "
5320 _("ISDS response on ChangePasswordOTP request is missing "
5325 /* Check for known error codes */
5326 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5327 if (!xmlStrcmp(code
, codes
[i
])) {
5328 char *code_locale
= _isds_utf82locale((char*)code
);
5329 char *message_locale
= _isds_utf82locale((char*)message
);
5330 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5331 _("Server refused to change password on ChangeISDSPassword "
5332 "request (code=%s, message=%s)\n") :
5333 _("Server refused to change password on ChangePasswordOTP "
5334 "request (code=%s, message=%s)\n"),
5335 code_locale
, message_locale
);
5337 free(message_locale
);
5338 isds_log_message(context
, _(meanings
[i
]));
5345 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5346 char *code_locale
= _isds_utf82locale((char*)code
);
5347 char *message_locale
= _isds_utf82locale((char*)message
);
5348 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5349 _("Server refused to change password on ChangeISDSPassword "
5350 "request (code=%s, message=%s)\n") :
5351 _("Server refused to change password on ChangePasswordOTP "
5352 "request (code=%s, message=%s)\n"),
5353 code_locale
, message_locale
);
5354 isds_log_message(context
, message_locale
);
5356 free(message_locale
);
5361 /* Otherwise password changed successfully */
5364 if (NULL
!= saved_url
) {
5365 /* Revert URL to original one */
5366 zfree(context
->url
);
5367 context
->url
= saved_url
;
5372 xmlFreeDoc(response
);
5373 xmlFreeNode(request
);
5376 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5377 _("Password changed successfully on ChangeISDSPassword "
5379 _("Password changed successfully on ChangePasswordOTP "
5381 #else /* not HAVE_LIBCURL */
5390 /* Generic middle part with request sending and response check.
5391 * It sends prepared request and checks for error code.
5392 * @context is ISDS session context.
5393 * @service is ISDS service handler
5394 * @service_name is name in scope of given @service
5395 * @request is XML tree with request. Will be freed to save memory.
5396 * @response is XML document outputting ISDS response.
5397 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5398 * NULL, if you don't care. */
5399 static isds_error
send_destroy_request_check_response(
5400 struct isds_ctx
*context
,
5401 const isds_service service
, const xmlChar
*service_name
,
5402 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
) {
5403 isds_error err
= IE_SUCCESS
;
5404 char *service_name_locale
= NULL
;
5405 xmlChar
*code
= NULL
, *message
= NULL
;
5408 if (!context
) return IE_INVALID_CONTEXT
;
5409 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5413 /* Check if connection is established
5414 * TODO: This check should be done downstairs. */
5415 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5417 service_name_locale
= _isds_utf82locale((char*) service_name
);
5418 if (!service_name_locale
) {
5423 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5424 service_name_locale
);
5427 err
= isds(context
, service
, *request
, response
, NULL
, NULL
);
5428 xmlFreeNode(*request
); *request
= NULL
;
5431 isds_log(ILF_ISDS
, ILL_DEBUG
,
5432 _("Processing ISDS response on %s request failed\n"),
5433 service_name_locale
);
5437 /* Check for response status */
5438 err
= isds_response_status(context
, service
, *response
,
5439 &code
, &message
, refnumber
);
5441 isds_log(ILF_ISDS
, ILL_DEBUG
,
5442 _("ISDS response on %s request is missing status\n"),
5443 service_name_locale
);
5447 /* Request processed, but server failed */
5448 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5449 char *code_locale
= _isds_utf82locale((char*) code
);
5450 char *message_locale
= _isds_utf82locale((char*) message
);
5451 isds_log(ILF_ISDS
, ILL_DEBUG
,
5452 _("Server refused %s request (code=%s, message=%s)\n"),
5453 service_name_locale
, code_locale
, message_locale
);
5454 isds_log_message(context
, message_locale
);
5456 free(message_locale
);
5465 if (err
&& *response
) {
5466 xmlFreeDoc(*response
);
5470 xmlFreeNode(*request
);
5473 free(service_name_locale
);
5479 /* Generic bottom half with request sending.
5480 * It sends prepared request, checks for error code, destroys response and
5481 * request and log success or failure.
5482 * @context is ISDS session context.
5483 * @service is ISDS service handler
5484 * @service_name is name in scope of given @service
5485 * @request is XML tree with request. Will be freed to save memory.
5486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5487 * NULL, if you don't care. */
5488 static isds_error
send_request_check_drop_response(
5489 struct isds_ctx
*context
,
5490 const isds_service service
, const xmlChar
*service_name
,
5491 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5492 isds_error err
= IE_SUCCESS
;
5493 xmlDocPtr response
= NULL
;
5496 if (!context
) return IE_INVALID_CONTEXT
;
5497 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5500 /* Send request and check response*/
5501 err
= send_destroy_request_check_response(context
,
5502 service
, service_name
, request
, &response
, refnumber
);
5504 xmlFreeDoc(response
);
5507 xmlFreeNode(*request
);
5512 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5513 isds_log(ILF_ISDS
, ILL_DEBUG
,
5514 _("%s request processed by server successfully.\n"),
5515 service_name_locale
);
5516 free(service_name_locale
);
5523 /* Insert isds_credentials_delivery structure into XML request if not NULL
5524 * @context is session context
5525 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5526 * credentials delivery. The email field is passed.
5527 * @parent is XML element where to insert */
5528 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5529 const struct isds_credentials_delivery
*credentials_delivery
,
5530 xmlNodePtr parent
) {
5531 isds_error err
= IE_SUCCESS
;
5534 if (!context
) return IE_INVALID_CONTEXT
;
5535 if (!parent
) return IE_INVAL
;
5537 if (credentials_delivery
) {
5538 /* Following elements are valid only for services:
5539 * NewAccessData, AddDataBoxUser, CreateDataBox */
5540 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5541 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5549 /* Extract credentials delivery from ISDS response.
5550 * @context is session context
5551 * @credentials_delivery is pointer to valid structure to fill in returned
5552 * user's password (and new log-in name). If NULL, do not extract the data.
5553 * @response is pointer to XML document with ISDS response
5554 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5555 * @return IE_SUCCESS even if new user name has not been found because it's not
5556 * clear whether it's returned always. */
5557 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5558 struct isds_credentials_delivery
*credentials_delivery
,
5559 xmlDocPtr response
, const char *request_name
) {
5560 isds_error err
= IE_SUCCESS
;
5561 xmlXPathContextPtr xpath_ctx
= NULL
;
5562 xmlXPathObjectPtr result
= NULL
;
5563 char *xpath_query
= NULL
;
5565 if (!context
) return IE_INVALID_CONTEXT
;
5566 if (credentials_delivery
) {
5567 zfree(credentials_delivery
->token
);
5568 zfree(credentials_delivery
->new_user_name
);
5570 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5573 /* Extract optional token */
5574 if (credentials_delivery
) {
5575 xpath_ctx
= xmlXPathNewContext(response
);
5580 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5585 /* Verify root element */
5586 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5591 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5596 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5597 char *request_name_locale
= _isds_utf82locale(request_name
);
5598 isds_log(ILF_ISDS
, ILL_WARNING
,
5599 _("Wrong element in ISDS response for %s request "
5600 "while extracting credentials delivery details\n"),
5601 request_name_locale
);
5602 free(request_name_locale
);
5606 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5609 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5611 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5613 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5614 if (!credentials_delivery
->token
) {
5615 char *request_name_locale
= _isds_utf82locale(request_name
);
5616 isds_log(ILF_ISDS
, ILL_ERR
,
5617 _("ISDS did not return token on %s request "
5618 "even if requested\n"), request_name_locale
);
5619 free(request_name_locale
);
5626 xmlXPathFreeObject(result
);
5627 xmlXPathFreeContext(xpath_ctx
);
5633 /* Build XSD:tCreateDBInput request type for box creating.
5634 * @context is session context
5635 * @request outputs built XML tree
5636 * @service_name is request name of SERVICE_DB_MANIPULATION service
5637 * @box is box description to create including single primary user (in case of
5639 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5640 * box, or contact address of PFO box owner)
5641 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5642 * @upper_box_id is optional ID of supper box if currently created box is
5644 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5646 * @credentials_delivery is valid pointer if ISDS should return token that box
5647 * owner can use to obtain his new credentials in on-line way. Then valid email
5648 * member value should be supplied.
5649 * @approval is optional external approval of box manipulation */
5650 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
5651 xmlNodePtr
*request
, const xmlChar
*service_name
,
5652 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5653 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
5654 const xmlChar
*ceo_label
,
5655 const struct isds_credentials_delivery
*credentials_delivery
,
5656 const struct isds_approval
*approval
) {
5657 isds_error err
= IE_SUCCESS
;
5658 xmlNsPtr isds_ns
= NULL
;
5659 xmlNodePtr node
, dbPrimaryUsers
;
5660 xmlChar
*string
= NULL
;
5661 const struct isds_list
*item
;
5664 if (!context
) return IE_INVALID_CONTEXT
;
5665 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
5669 /* Build CreateDataBox-similar request */
5670 *request
= xmlNewNode(NULL
, service_name
);
5672 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
5673 isds_printf_message(context
, _("Could build %s request"),
5674 service_name_locale
);
5675 free(service_name_locale
);
5678 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
5679 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
5681 isds_log_message(context
, _("Could not create ISDS1 name space"));
5682 xmlFreeNode(*request
);
5686 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
5688 isds_log_message(context
, _("Could not create ISDS name space"));
5689 xmlFreeNode(*request
);
5693 xmlSetNs(*request
, isds_ns
);
5695 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
5696 err
= insert_DbOwnerInfo(context
, box
, node
);
5697 if (err
) goto leave
;
5700 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5701 * verbose documentation allows none dbUserInfo */
5702 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
5703 for (item
= users
; item
; item
= item
->next
) {
5705 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
5706 err
= insert_DbUserInfo(context
,
5707 (struct isds_DbUserInfo
*) item
->data
, node
);
5708 if (err
) goto leave
;
5712 INSERT_STRING(*request
, "dbFormerNames", former_names
);
5713 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
5714 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
5716 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
5717 if (err
) goto leave
;
5719 err
= insert_GExtApproval(context
, approval
, *request
);
5720 if (err
) goto leave
;
5724 xmlFreeNode(*request
);
5730 #endif /* HAVE_LIBCURL */
5734 * @context is session context
5735 * @box is box description to create including single primary user (in case of
5736 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5737 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5738 * box, or contact address of PFO box owner)
5739 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5740 * @upper_box_id is optional ID of supper box if currently created box is
5742 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5743 * @credentials_delivery is NULL if new password should be delivered off-line
5744 * to box owner. It is valid pointer if owner should obtain new password on-line
5745 * on dedicated web server. Then input @credentials_delivery.email value is
5746 * his e-mail address he must provide to dedicated web server together
5747 * with output reallocated @credentials_delivery.token member. Output
5748 * member @credentials_delivery.new_user_name is unused up on this call.
5749 * @approval is optional external approval of box manipulation
5750 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5751 * NULL, if you don't care.*/
5752 isds_error
isds_add_box(struct isds_ctx
*context
,
5753 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5754 const char *former_names
, const char *upper_box_id
,
5755 const char *ceo_label
,
5756 struct isds_credentials_delivery
*credentials_delivery
,
5757 const struct isds_approval
*approval
, char **refnumber
) {
5758 isds_error err
= IE_SUCCESS
;
5760 xmlNodePtr request
= NULL
;
5761 xmlDocPtr response
= NULL
;
5762 xmlXPathContextPtr xpath_ctx
= NULL
;
5763 xmlXPathObjectPtr result
= NULL
;
5767 if (!context
) return IE_INVALID_CONTEXT
;
5768 zfree(context
->long_message
);
5769 if (credentials_delivery
) {
5770 zfree(credentials_delivery
->token
);
5771 zfree(credentials_delivery
->new_user_name
);
5773 if (!box
) return IE_INVAL
;
5776 /* Scratch box ID */
5779 /* Build CreateDataBox request */
5780 err
= build_CreateDBInput_request(context
,
5781 &request
, BAD_CAST
"CreateDataBox",
5782 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
5783 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
5784 if (err
) goto leave
;
5786 /* Send it to server and process response */
5787 err
= send_destroy_request_check_response(context
,
5788 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
5789 &response
, (xmlChar
**) refnumber
);
5791 /* Extract box ID */
5792 xpath_ctx
= xmlXPathNewContext(response
);
5797 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5801 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
5803 /* Extract optional token */
5804 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
5808 xmlXPathFreeObject(result
);
5809 xmlXPathFreeContext(xpath_ctx
);
5810 xmlFreeDoc(response
);
5811 xmlFreeNode(request
);
5814 isds_log(ILF_ISDS
, ILL_DEBUG
,
5815 _("CreateDataBox request processed by server successfully.\n"));
5817 #else /* not HAVE_LIBCURL */
5825 /* Notify ISDS about new PFO entity.
5826 * This function has no real effect.
5827 * @context is session context
5828 * @box is PFO description including single primary user.
5829 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5830 * @former_names is optional undocumented string. Pass NULL if you don't care.
5831 * @upper_box_id is optional ID of supper box if currently created box is
5833 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5834 * @approval is optional external approval of box manipulation
5835 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5836 * NULL, if you don't care.*/
5837 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
5838 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5839 const char *former_names
, const char *upper_box_id
,
5840 const char *ceo_label
, const struct isds_approval
*approval
,
5842 isds_error err
= IE_SUCCESS
;
5844 xmlNodePtr request
= NULL
;
5847 if (!context
) return IE_INVALID_CONTEXT
;
5848 zfree(context
->long_message
);
5849 if (!box
) return IE_INVAL
;
5852 /* Build CreateDataBoxPFOInfo request */
5853 err
= build_CreateDBInput_request(context
,
5854 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
5855 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
5856 (xmlChar
*) ceo_label
, NULL
, approval
);
5857 if (err
) goto leave
;
5859 /* Send it to server and process response */
5860 err
= send_request_check_drop_response(context
,
5861 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
5862 (xmlChar
**) refnumber
);
5863 /* XXX: XML Schema names output dbID element but textual documentation
5864 * states no box identifier is returned. */
5866 xmlFreeNode(request
);
5867 #else /* not HAVE_LIBCURL */
5874 /* Common implementation for removing given box.
5875 * @context is session context
5876 * @service_name is UTF-8 encoded name fo ISDS service
5877 * @box is box description to delete
5878 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5879 * carry sane value. If NULL, do not inject this information into request.
5880 * @approval is optional external approval of box manipulation
5881 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5882 * NULL, if you don't care.*/
5883 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
5884 const xmlChar
*service_name
,
5885 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
5886 const struct isds_approval
*approval
, char **refnumber
) {
5887 isds_error err
= IE_SUCCESS
;
5889 xmlNsPtr isds_ns
= NULL
;
5890 xmlNodePtr request
= NULL
;
5892 xmlChar
*string
= NULL
;
5896 if (!context
) return IE_INVALID_CONTEXT
;
5897 zfree(context
->long_message
);
5898 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
5902 /* Build DeleteDataBox(Promptly) request */
5903 request
= xmlNewNode(NULL
, service_name
);
5905 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
5906 isds_printf_message(context
,
5907 _("Could build %s request"), service_name_locale
);
5908 free(service_name_locale
);
5911 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5913 isds_log_message(context
, _("Could not create ISDS name space"));
5914 xmlFreeNode(request
);
5917 xmlSetNs(request
, isds_ns
);
5919 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5920 err
= insert_DbOwnerInfo(context
, box
, node
);
5921 if (err
) goto leave
;
5924 err
= tm2datestring(since
, &string
);
5926 isds_log_message(context
,
5927 _("Could not convert `since' argument to ISO date string"));
5930 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
5934 err
= insert_GExtApproval(context
, approval
, request
);
5935 if (err
) goto leave
;
5938 /* Send it to server and process response */
5939 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
5940 service_name
, &request
, (xmlChar
**) refnumber
);
5943 xmlFreeNode(request
);
5945 #else /* not HAVE_LIBCURL */
5952 /* Remove given box permanently.
5953 * @context is session context
5954 * @box is box description to delete
5955 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5957 * @approval is optional external approval of box manipulation
5958 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5959 * NULL, if you don't care.*/
5960 isds_error
isds_delete_box(struct isds_ctx
*context
,
5961 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
5962 const struct isds_approval
*approval
, char **refnumber
) {
5963 if (!context
) return IE_INVALID_CONTEXT
;
5964 zfree(context
->long_message
);
5965 if (!box
|| !since
) return IE_INVAL
;
5967 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
5968 box
, since
, approval
, refnumber
);
5972 /* Undocumented function.
5973 * @context is session context
5974 * @box is box description to delete
5975 * @approval is optional external approval of box manipulation
5976 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5977 * NULL, if you don't care.*/
5978 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
5979 const struct isds_DbOwnerInfo
*box
,
5980 const struct isds_approval
*approval
, char **refnumber
) {
5981 if (!context
) return IE_INVALID_CONTEXT
;
5982 zfree(context
->long_message
);
5983 if (!box
) return IE_INVAL
;
5985 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
5986 box
, NULL
, approval
, refnumber
);
5990 /* Update data about given box.
5991 * @context is session context
5992 * @old_box current box description
5993 * @new_box are updated data about @old_box
5994 * @approval is optional external approval of box manipulation
5995 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5996 * NULL, if you don't care.*/
5997 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
5998 const struct isds_DbOwnerInfo
*old_box
,
5999 const struct isds_DbOwnerInfo
*new_box
,
6000 const struct isds_approval
*approval
, char **refnumber
) {
6001 isds_error err
= IE_SUCCESS
;
6003 xmlNsPtr isds_ns
= NULL
;
6004 xmlNodePtr request
= NULL
;
6009 if (!context
) return IE_INVALID_CONTEXT
;
6010 zfree(context
->long_message
);
6011 if (!old_box
|| !new_box
) return IE_INVAL
;
6015 /* Build UpdateDataBoxDescr request */
6016 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6018 isds_log_message(context
,
6019 _("Could build UpdateDataBoxDescr request"));
6022 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6024 isds_log_message(context
, _("Could not create ISDS name space"));
6025 xmlFreeNode(request
);
6028 xmlSetNs(request
, isds_ns
);
6030 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6031 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6032 if (err
) goto leave
;
6034 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6035 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6036 if (err
) goto leave
;
6038 err
= insert_GExtApproval(context
, approval
, request
);
6039 if (err
) goto leave
;
6042 /* Send it to server and process response */
6043 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6044 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6047 xmlFreeNode(request
);
6048 #else /* not HAVE_LIBCURL */
6057 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6059 * @context is session context
6060 * @service is SOAP service
6061 * @service_name is name of request in @service
6062 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6063 * @box_id is box ID of interest
6064 * @approval is optional external approval of box manipulation
6065 * @response is server SOAP body response as XML document
6066 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6067 * NULL, if you don't care.
6068 * @return error coded from lower layer, context message will be set up
6070 static isds_error
build_send_dbid_request_check_response(
6071 struct isds_ctx
*context
, const isds_service service
,
6072 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6073 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6074 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6076 isds_error err
= IE_SUCCESS
;
6077 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6078 xmlNodePtr request
= NULL
, node
;
6079 xmlNsPtr isds_ns
= NULL
;
6081 if (!context
) return IE_INVALID_CONTEXT
;
6082 if (!service_name
|| !box_id
) return IE_INVAL
;
6083 if (!response
) return IE_INVAL
;
6085 /* Free output argument */
6086 xmlFreeDoc(*response
); *response
= NULL
;
6088 /* Prepare strings */
6089 service_name_locale
= _isds_utf82locale((char*)service_name
);
6090 if (!service_name_locale
) {
6094 box_id_locale
= _isds_utf82locale((char*)box_id
);
6095 if (!box_id_locale
) {
6101 request
= xmlNewNode(NULL
, service_name
);
6103 isds_printf_message(context
,
6104 _("Could not build %s request for %s box"), service_name_locale
,
6109 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6111 isds_log_message(context
, _("Could not create ISDS name space"));
6115 xmlSetNs(request
, isds_ns
);
6117 /* Add XSD:tIdDbInput children */
6118 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6119 INSERT_STRING(request
, box_id_element
, box_id
);
6120 err
= insert_GExtApproval(context
, approval
, request
);
6121 if (err
) goto leave
;
6123 /* Send request and check response*/
6124 err
= send_destroy_request_check_response(context
,
6125 service
, service_name
, &request
, response
, refnumber
);
6128 free(service_name_locale
);
6129 free(box_id_locale
);
6130 xmlFreeNode(request
);
6133 #endif /* HAVE_LIBCURL */
6136 /* Get data about all users assigned to given box.
6137 * @context is session context
6139 * @users is automatically reallocated list of struct isds_DbUserInfo */
6140 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6141 struct isds_list
**users
) {
6142 isds_error err
= IE_SUCCESS
;
6144 xmlDocPtr response
= NULL
;
6145 xmlXPathContextPtr xpath_ctx
= NULL
;
6146 xmlXPathObjectPtr result
= NULL
;
6148 struct isds_list
*item
, *prev_item
= NULL
;
6151 if (!context
) return IE_INVALID_CONTEXT
;
6152 zfree(context
->long_message
);
6153 if (!users
|| !box_id
) return IE_INVAL
;
6154 isds_list_free(users
);
6158 /* Do request and check for success */
6159 err
= build_send_dbid_request_check_response(context
,
6160 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6161 BAD_CAST box_id
, NULL
, &response
, NULL
);
6162 if (err
) goto leave
;
6166 /* Prepare structure */
6167 xpath_ctx
= xmlXPathNewContext(response
);
6172 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6177 /* Set context node */
6178 result
= xmlXPathEvalExpression(BAD_CAST
6179 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6185 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6186 /* Iterate over all users */
6187 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6189 /* Prepare structure */
6190 item
= calloc(1, sizeof(*item
));
6195 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6196 if (i
== 0) *users
= item
;
6197 else prev_item
->next
= item
;
6201 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6202 err
= extract_DbUserInfo(context
,
6203 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6204 if (err
) goto leave
;
6210 isds_list_free(users
);
6213 xmlXPathFreeObject(result
);
6214 xmlXPathFreeContext(xpath_ctx
);
6215 xmlFreeDoc(response
);
6218 isds_log(ILF_ISDS
, ILL_DEBUG
,
6219 _("GetDataBoxUsers request processed by server "
6220 "successfully.\n"));
6221 #else /* not HAVE_LIBCURL */
6229 /* Update data about user assigned to given box.
6230 * @context is session context
6231 * @box is box identification
6232 * @old_user identifies user to update
6233 * @new_user are updated data about @old_user
6234 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6235 * NULL, if you don't care.*/
6236 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6237 const struct isds_DbOwnerInfo
*box
,
6238 const struct isds_DbUserInfo
*old_user
,
6239 const struct isds_DbUserInfo
*new_user
,
6241 isds_error err
= IE_SUCCESS
;
6243 xmlNsPtr isds_ns
= NULL
;
6244 xmlNodePtr request
= NULL
;
6249 if (!context
) return IE_INVALID_CONTEXT
;
6250 zfree(context
->long_message
);
6251 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6255 /* Build UpdateDataBoxUser request */
6256 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6258 isds_log_message(context
,
6259 _("Could build UpdateDataBoxUser request"));
6262 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6264 isds_log_message(context
, _("Could not create ISDS name space"));
6265 xmlFreeNode(request
);
6268 xmlSetNs(request
, isds_ns
);
6270 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6271 err
= insert_DbOwnerInfo(context
, box
, node
);
6272 if (err
) goto leave
;
6274 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6275 err
= insert_DbUserInfo(context
, old_user
, node
);
6276 if (err
) goto leave
;
6278 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6279 err
= insert_DbUserInfo(context
, new_user
, node
);
6280 if (err
) goto leave
;
6282 /* Send it to server and process response */
6283 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6284 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6287 xmlFreeNode(request
);
6288 #else /* not HAVE_LIBCURL */
6296 /* Undocumented function.
6297 * @context is session context
6298 * @box_id is UTF-8 encoded box identifier
6299 * @token is UTF-8 encoded temporary password
6300 * @user_id outputs UTF-8 encoded reallocated user identifier
6301 * @password outpus UTF-8 encoded reallocated user password
6302 * Output arguments will be nulled in case of error */
6303 isds_error
isds_activate(struct isds_ctx
*context
,
6304 const char *box_id
, const char *token
,
6305 char **user_id
, char **password
) {
6306 isds_error err
= IE_SUCCESS
;
6308 xmlNsPtr isds_ns
= NULL
;
6309 xmlNodePtr request
= NULL
, node
;
6310 xmlDocPtr response
= NULL
;
6311 xmlXPathContextPtr xpath_ctx
= NULL
;
6312 xmlXPathObjectPtr result
= NULL
;
6316 if (!context
) return IE_INVALID_CONTEXT
;
6317 zfree(context
->long_message
);
6319 if (user_id
) zfree(*user_id
);
6320 if (password
) zfree(*password
);
6322 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6326 /* Build Activate request */
6327 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6329 isds_log_message(context
, _("Could build Activate request"));
6332 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6334 isds_log_message(context
, _("Could not create ISDS name space"));
6335 xmlFreeNode(request
);
6338 xmlSetNs(request
, isds_ns
);
6340 INSERT_STRING(request
, "dbAccessDataId", token
);
6341 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6342 INSERT_STRING(request
, "dbID", box_id
);
6345 /* Send request and check response*/
6346 err
= send_destroy_request_check_response(context
,
6347 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6349 if (err
) goto leave
;
6353 xpath_ctx
= xmlXPathNewContext(response
);
6358 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6362 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6368 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6369 isds_log_message(context
, _("Missing ActivateResponse element"));
6373 if (result
->nodesetval
->nodeNr
> 1) {
6374 isds_log_message(context
, _("Multiple ActivateResponse element"));
6378 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6379 xmlXPathFreeObject(result
); result
= NULL
;
6381 EXTRACT_STRING("isds:userId", *user_id
);
6383 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6384 "but did not return `userId' element.\n"));
6386 EXTRACT_STRING("isds:password", *password
);
6388 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6389 "but did not return `password' element.\n"));
6392 xmlXPathFreeObject(result
);
6393 xmlXPathFreeContext(xpath_ctx
);
6394 xmlFreeDoc(response
);
6395 xmlFreeNode(request
);
6398 isds_log(ILF_ISDS
, ILL_DEBUG
,
6399 _("Activate request processed by server successfully.\n"));
6400 #else /* not HAVE_LIBCURL */
6408 /* Reset credentials of user assigned to given box.
6409 * @context is session context
6410 * @box is box identification
6411 * @user identifies user to reset password
6412 * @fee_paid is true if fee has been paid, false otherwise
6413 * @approval is optional external approval of box manipulation
6414 * @credentials_delivery is NULL if new password should be delivered off-line
6415 * to the user. It is valid pointer if user should obtain new password on-line
6416 * on dedicated web server. Then input @credentials_delivery.email value is
6417 * user's e-mail address user must provide to dedicated web server together
6418 * with @credentials_delivery.token. The output reallocated token user needs
6419 * to use to authorize on the web server to view his new password. Output
6420 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6421 * ISDS changed up on this call. (No reason why server could change the name
6423 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6424 * NULL, if you don't care.*/
6425 isds_error
isds_reset_password(struct isds_ctx
*context
,
6426 const struct isds_DbOwnerInfo
*box
,
6427 const struct isds_DbUserInfo
*user
,
6428 const _Bool fee_paid
, const struct isds_approval
*approval
,
6429 struct isds_credentials_delivery
*credentials_delivery
,
6431 isds_error err
= IE_SUCCESS
;
6433 xmlNsPtr isds_ns
= NULL
;
6434 xmlNodePtr request
= NULL
, node
;
6435 xmlDocPtr response
= NULL
;
6439 if (!context
) return IE_INVALID_CONTEXT
;
6440 zfree(context
->long_message
);
6442 if (credentials_delivery
) {
6443 zfree(credentials_delivery
->token
);
6444 zfree(credentials_delivery
->new_user_name
);
6446 if (!box
|| !user
) return IE_INVAL
;
6450 /* Build NewAccessData request */
6451 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6453 isds_log_message(context
,
6454 _("Could build NewAccessData request"));
6457 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6459 isds_log_message(context
, _("Could not create ISDS name space"));
6460 xmlFreeNode(request
);
6463 xmlSetNs(request
, isds_ns
);
6465 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6466 err
= insert_DbOwnerInfo(context
, box
, node
);
6467 if (err
) goto leave
;
6469 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6470 err
= insert_DbUserInfo(context
, user
, node
);
6471 if (err
) goto leave
;
6473 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6475 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6476 if (err
) goto leave
;
6478 err
= insert_GExtApproval(context
, approval
, request
);
6479 if (err
) goto leave
;
6481 /* Send request and check response*/
6482 err
= send_destroy_request_check_response(context
,
6483 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6484 &response
, (xmlChar
**) refnumber
);
6485 if (err
) goto leave
;
6488 /* Extract optional token */
6489 err
= extract_credentials_delivery(context
, credentials_delivery
,
6490 response
, "NewAccessData");
6493 xmlFreeDoc(response
);
6494 xmlFreeNode(request
);
6497 isds_log(ILF_ISDS
, ILL_DEBUG
,
6498 _("NewAccessData request processed by server "
6499 "successfully.\n"));
6500 #else /* not HAVE_LIBCURL */
6508 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6509 * code, destroy response and log success.
6510 * @context is ISDS session context.
6511 * @service_name is name of SERVICE_DB_MANIPULATION service
6512 * @box is box identification
6513 * @user identifies user to remove
6514 * @credentials_delivery is NULL if new user's password should be delivered
6515 * off-line to the user. It is valid pointer if user should obtain new
6516 * password on-line on dedicated web server. Then input
6517 * @credentials_delivery.email value is user's e-mail address user must
6518 * provide to dedicated web server together with @credentials_delivery.token.
6519 * The output reallocated token user needs to use to authorize on the web
6520 * server to view his new password. Output reallocated
6521 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6522 * assingned or changed up on this call.
6523 * @approval is optional external approval of box manipulation
6524 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6525 * NULL, if you don't care. */
6526 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6527 struct isds_ctx
*context
, const xmlChar
*service_name
,
6528 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6529 struct isds_credentials_delivery
*credentials_delivery
,
6530 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6531 isds_error err
= IE_SUCCESS
;
6533 xmlNsPtr isds_ns
= NULL
;
6534 xmlNodePtr request
= NULL
, node
;
6535 xmlDocPtr response
= NULL
;
6539 if (!context
) return IE_INVALID_CONTEXT
;
6540 zfree(context
->long_message
);
6541 if (credentials_delivery
) {
6542 zfree(credentials_delivery
->token
);
6543 zfree(credentials_delivery
->new_user_name
);
6545 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6550 /* Build NewAccessData or similar request */
6551 request
= xmlNewNode(NULL
, service_name
);
6553 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6554 isds_printf_message(context
, _("Could not build %s request"),
6555 service_name_locale
);
6556 free(service_name_locale
);
6559 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6561 isds_log_message(context
, _("Could not create ISDS name space"));
6562 xmlFreeNode(request
);
6565 xmlSetNs(request
, isds_ns
);
6567 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6568 err
= insert_DbOwnerInfo(context
, box
, node
);
6569 if (err
) goto leave
;
6571 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6572 err
= insert_DbUserInfo(context
, user
, node
);
6573 if (err
) goto leave
;
6575 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6576 if (err
) goto leave
;
6578 err
= insert_GExtApproval(context
, approval
, request
);
6579 if (err
) goto leave
;
6582 /* Send request and check response*/
6583 err
= send_destroy_request_check_response(context
,
6584 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
, refnumber
);
6586 xmlFreeNode(request
);
6589 /* Pick up credentials_delivery if requested */
6590 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6591 (char *)service_name
);
6594 xmlFreeDoc(response
);
6595 if (request
) xmlFreeNode(request
);
6598 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6599 isds_log(ILF_ISDS
, ILL_DEBUG
,
6600 _("%s request processed by server successfully.\n"),
6601 service_name_locale
);
6602 free(service_name_locale
);
6604 #else /* not HAVE_LIBCURL */
6612 /* Assign new user to given box.
6613 * @context is session context
6614 * @box is box identification
6615 * @user defines new user to add
6616 * @credentials_delivery is NULL if new user's password should be delivered
6617 * off-line to the user. It is valid pointer if user should obtain new
6618 * password on-line on dedicated web server. Then input
6619 * @credentials_delivery.email value is user's e-mail address user must
6620 * provide to dedicated web server together with @credentials_delivery.token.
6621 * The output reallocated token user needs to use to authorize on the web
6622 * server to view his new password. Output reallocated
6623 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6624 * assingned up on this call.
6625 * @approval is optional external approval of box manipulation
6626 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6627 * NULL, if you don't care.*/
6628 isds_error
isds_add_user(struct isds_ctx
*context
,
6629 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6630 struct isds_credentials_delivery
*credentials_delivery
,
6631 const struct isds_approval
*approval
, char **refnumber
) {
6632 return build_send_manipulationboxuser_request_check_drop_response(context
,
6633 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
6634 approval
, (xmlChar
**) refnumber
);
6638 /* Remove user assigned to given box.
6639 * @context is session context
6640 * @box is box identification
6641 * @user identifies user to remove
6642 * @approval is optional external approval of box manipulation
6643 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6644 * NULL, if you don't care.*/
6645 isds_error
isds_delete_user(struct isds_ctx
*context
,
6646 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6647 const struct isds_approval
*approval
, char **refnumber
) {
6648 return build_send_manipulationboxuser_request_check_drop_response(context
,
6649 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
6650 (xmlChar
**) refnumber
);
6654 /* Get list of boxes in ZIP archive.
6655 * @context is session context
6656 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6657 * System recognizes following values currently: ALL (all boxes), UPG
6658 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6659 * receiving commercial messages). This argument is a string because
6660 * specification states new values can appear in the future. Not all list
6661 * types are available to all users.
6662 * @buffer is automatically reallocated memory to store the list of boxes. The
6663 * list is zipped CSV file.
6664 * @buffer_length is size of @buffer data in bytes.
6665 * In case of error @buffer will be freed and @buffer_length will be
6667 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
6668 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
6669 isds_error err
= IE_SUCCESS
;
6671 xmlNsPtr isds_ns
= NULL
;
6672 xmlNodePtr request
= NULL
, node
;
6673 xmlDocPtr response
= NULL
;
6674 xmlXPathContextPtr xpath_ctx
= NULL
;
6675 xmlXPathObjectPtr result
= NULL
;
6676 char *string
= NULL
;
6680 if (!context
) return IE_INVALID_CONTEXT
;
6681 zfree(context
->long_message
);
6682 if (buffer
) zfree(*buffer
);
6683 if (!buffer
|| !buffer_length
) return IE_INVAL
;
6687 /* Check if connection is established
6688 * TODO: This check should be done downstairs. */
6689 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6692 /* Build AuthenticateMessage request */
6693 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
6695 isds_log_message(context
,
6696 _("Could not build GetDataBoxList request"));
6699 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6701 isds_log_message(context
, _("Could not create ISDS name space"));
6702 xmlFreeNode(request
);
6705 xmlSetNs(request
, isds_ns
);
6706 INSERT_STRING(request
, "dblType", list_identifier
);
6708 /* Send request to server and process response */
6709 err
= send_destroy_request_check_response(context
,
6710 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
6712 if (err
) goto leave
;
6715 /* Extract Base-64 encoded ZIP file */
6716 xpath_ctx
= xmlXPathNewContext(response
);
6721 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6725 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
6727 /* Decode non-empty archive */
6728 if (string
&& string
[0] != '\0') {
6729 *buffer_length
= _isds_b64decode(string
, buffer
);
6730 if (*buffer_length
== (size_t) -1) {
6731 isds_printf_message(context
,
6732 _("Error while Base64-decoding box list archive"));
6741 xmlXPathFreeObject(result
);
6742 xmlXPathFreeContext(xpath_ctx
);
6743 xmlFreeDoc(response
);
6744 xmlFreeNode(request
);
6747 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
6748 "processed by server successfully.\n"));
6750 #else /* not HAVE_LIBCURL */
6758 /* Find boxes suiting given criteria.
6759 * @criteria is filter. You should fill in at least some members.
6760 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6761 * possibly empty. Input NULL or valid old structure.
6763 * IE_SUCCESS if search succeeded, @boxes contains useful data
6764 * IE_NOEXIST if no such box exists, @boxes will be NULL
6765 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6766 * contains still valid data
6767 * other code if something bad happens. @boxes will be NULL. */
6768 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
6769 const struct isds_DbOwnerInfo
*criteria
,
6770 struct isds_list
**boxes
) {
6771 isds_error err
= IE_SUCCESS
;
6773 _Bool truncated
= 0;
6774 xmlNsPtr isds_ns
= NULL
;
6775 xmlNodePtr request
= NULL
;
6776 xmlDocPtr response
= NULL
;
6777 xmlChar
*code
= NULL
, *message
= NULL
;
6778 xmlNodePtr db_owner_info
;
6779 xmlXPathContextPtr xpath_ctx
= NULL
;
6780 xmlXPathObjectPtr result
= NULL
;
6781 xmlChar
*string
= NULL
;
6785 if (!context
) return IE_INVALID_CONTEXT
;
6786 zfree(context
->long_message
);
6787 if (!boxes
) return IE_INVAL
;
6788 isds_list_free(boxes
);
6795 /* Check if connection is established
6796 * TODO: This check should be done downstairs. */
6797 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6800 /* Build FindDataBox request */
6801 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
6803 isds_log_message(context
,
6804 _("Could build FindDataBox request"));
6807 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6809 isds_log_message(context
, _("Could not create ISDS name space"));
6810 xmlFreeNode(request
);
6813 xmlSetNs(request
, isds_ns
);
6814 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
6815 if (!db_owner_info
) {
6816 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
6817 "FindDataBox element"));
6818 xmlFreeNode(request
);
6822 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
6823 if (err
) goto leave
;
6826 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
6829 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
6831 /* Destroy request */
6832 xmlFreeNode(request
); request
= NULL
;
6835 isds_log(ILF_ISDS
, ILL_DEBUG
,
6836 _("Processing ISDS response on FindDataBox "
6837 "request failed\n"));
6841 /* Check for response status */
6842 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
6843 &code
, &message
, NULL
);
6845 isds_log(ILF_ISDS
, ILL_DEBUG
,
6846 _("ISDS response on FindDataBox request is missing status\n"));
6850 /* Request processed, but nothing found */
6851 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
6852 !xmlStrcmp(code
, BAD_CAST
"5001")) {
6853 char *code_locale
= _isds_utf82locale((char*)code
);
6854 char *message_locale
= _isds_utf82locale((char*)message
);
6855 isds_log(ILF_ISDS
, ILL_DEBUG
,
6856 _("Server did not found any box on FindDataBox request "
6857 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6858 isds_log_message(context
, message_locale
);
6860 free(message_locale
);
6865 /* Warning, not a error */
6866 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
6867 char *code_locale
= _isds_utf82locale((char*)code
);
6868 char *message_locale
= _isds_utf82locale((char*)message
);
6869 isds_log(ILF_ISDS
, ILL_DEBUG
,
6870 _("Server truncated response on FindDataBox request "
6871 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6872 isds_log_message(context
, message_locale
);
6874 free(message_locale
);
6879 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6880 char *code_locale
= _isds_utf82locale((char*)code
);
6881 char *message_locale
= _isds_utf82locale((char*)message
);
6882 isds_log(ILF_ISDS
, ILL_DEBUG
,
6883 _("Server refused FindDataBox request "
6884 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6885 isds_log_message(context
, message_locale
);
6887 free(message_locale
);
6892 xpath_ctx
= xmlXPathNewContext(response
);
6897 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6902 /* Extract boxes if they present */
6903 result
= xmlXPathEvalExpression(BAD_CAST
6904 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6910 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6911 struct isds_list
*item
, *prev_item
= NULL
;
6912 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6913 item
= calloc(1, sizeof(*item
));
6919 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
6920 if (i
== 0) *boxes
= item
;
6921 else prev_item
->next
= item
;
6924 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6925 err
= extract_DbOwnerInfo(context
,
6926 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
6927 if (err
) goto leave
;
6933 isds_list_free(boxes
);
6935 if (truncated
) err
= IE_2BIG
;
6939 xmlFreeNode(request
);
6940 xmlXPathFreeObject(result
);
6941 xmlXPathFreeContext(xpath_ctx
);
6945 xmlFreeDoc(response
);
6948 isds_log(ILF_ISDS
, ILL_DEBUG
,
6949 _("FindDataBox request processed by server successfully.\n"));
6950 #else /* not HAVE_LIBCURL */
6958 /* Get status of a box.
6959 * @context is ISDS session context.
6960 * @box_id is UTF-8 encoded box identifier as zero terminated string
6961 * @box_status is return value of box status.
6963 * IE_SUCCESS if box has been found and its status retrieved
6964 * IE_NOEXIST if box is not known to ISDS server
6965 * or other appropriate error.
6966 * You can use isds_DbState to enumerate box status. However out of enum
6967 * range value can be returned too. This is feature because ISDS
6968 * specification leaves the set of values open.
6969 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6970 * the box has been deleted, but ISDS still lists its former existence. */
6971 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
6972 long int *box_status
) {
6973 isds_error err
= IE_SUCCESS
;
6975 xmlNsPtr isds_ns
= NULL
;
6976 xmlNodePtr request
= NULL
, db_id
;
6977 xmlDocPtr response
= NULL
;
6978 xmlChar
*code
= NULL
, *message
= NULL
;
6979 xmlXPathContextPtr xpath_ctx
= NULL
;
6980 xmlXPathObjectPtr result
= NULL
;
6981 xmlChar
*string
= NULL
;
6984 if (!context
) return IE_INVALID_CONTEXT
;
6985 zfree(context
->long_message
);
6986 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
6989 /* Check if connection is established
6990 * TODO: This check should be done downstairs. */
6991 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6994 /* Build CheckDataBox request */
6995 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
6997 isds_log_message(context
,
6998 _("Could build CheckDataBox request"));
7001 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7003 isds_log_message(context
, _("Could not create ISDS name space"));
7004 xmlFreeNode(request
);
7007 xmlSetNs(request
, isds_ns
);
7008 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7010 isds_log_message(context
, _("Could not add dbID child to "
7011 "CheckDataBox element"));
7012 xmlFreeNode(request
);
7017 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CheckDataBox request to ISDS\n"));
7020 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7022 /* Destroy request */
7023 xmlFreeNode(request
);
7026 isds_log(ILF_ISDS
, ILL_DEBUG
,
7027 _("Processing ISDS response on CheckDataBox "
7028 "request failed\n"));
7032 /* Check for response status */
7033 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7034 &code
, &message
, NULL
);
7036 isds_log(ILF_ISDS
, ILL_DEBUG
,
7037 _("ISDS response on CheckDataBox request is missing status\n"));
7041 /* Request processed, but nothing found */
7042 if (!xmlStrcmp(code
, BAD_CAST
"5001")) {
7043 char *box_id_locale
= _isds_utf82locale((char*)box_id
);
7044 char *code_locale
= _isds_utf82locale((char*)code
);
7045 char *message_locale
= _isds_utf82locale((char*)message
);
7046 isds_log(ILF_ISDS
, ILL_DEBUG
,
7047 _("Server did not found box %s on CheckDataBox request "
7048 "(code=%s, message=%s)\n"),
7049 box_id_locale
, code_locale
, message_locale
);
7050 isds_log_message(context
, message_locale
);
7051 free(box_id_locale
);
7053 free(message_locale
);
7059 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7060 char *code_locale
= _isds_utf82locale((char*)code
);
7061 char *message_locale
= _isds_utf82locale((char*)message
);
7062 isds_log(ILF_ISDS
, ILL_DEBUG
,
7063 _("Server refused CheckDataBox request "
7064 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7065 isds_log_message(context
, message_locale
);
7067 free(message_locale
);
7073 xpath_ctx
= xmlXPathNewContext(response
);
7078 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7082 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7088 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7089 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7093 if (result
->nodesetval
->nodeNr
> 1) {
7094 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7098 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7099 xmlXPathFreeObject(result
); result
= NULL
;
7101 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7106 xmlXPathFreeObject(result
);
7107 xmlXPathFreeContext(xpath_ctx
);
7111 xmlFreeDoc(response
);
7114 isds_log(ILF_ISDS
, ILL_DEBUG
,
7115 _("CheckDataBox request processed by server successfully.\n"));
7116 #else /* not HAVE_LIBCURL */
7124 /* Get list of permissions to send commercial messages.
7125 * @context is ISDS session context.
7126 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7127 * @permissions is a reallocated list of permissions (struct
7128 * isds_commercial_permission*) to send commercial messages from @box_id. The
7129 * order of permissions is significant as the server applies the permissions
7130 * and associated pre-paid credits in the order. Empty list means no
7133 * IE_SUCCESS if the list has been obtained correctly,
7134 * or other appropriate error. */
7135 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7136 const char *box_id
, struct isds_list
**permissions
) {
7137 isds_error err
= IE_SUCCESS
;
7139 xmlDocPtr response
= NULL
;
7140 xmlXPathContextPtr xpath_ctx
= NULL
;
7141 xmlXPathObjectPtr result
= NULL
;
7144 if (!context
) return IE_INVALID_CONTEXT
;
7145 zfree(context
->long_message
);
7146 if (NULL
== permissions
) return IE_INVAL
;
7147 isds_list_free(permissions
);
7148 if (NULL
== box_id
) return IE_INVAL
;
7151 /* Check if connection is established */
7152 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7154 /* Do request and check for success */
7155 err
= build_send_dbid_request_check_response(context
,
7156 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7157 BAD_CAST box_id
, NULL
, &response
, NULL
);
7159 isds_log(ILF_ISDS
, ILL_DEBUG
,
7160 _("PDZInfo request processed by server successfully.\n"));
7164 /* Prepare structure */
7165 xpath_ctx
= xmlXPathNewContext(response
);
7170 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7175 /* Set context node */
7176 result
= xmlXPathEvalExpression(BAD_CAST
7177 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7183 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7184 /* Iterate over all permission records */
7185 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7186 struct isds_list
*item
, *prev_item
= NULL
;
7188 /* Prepare structure */
7189 item
= calloc(1, sizeof(*item
));
7194 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7195 if (i
== 0) *permissions
= item
;
7196 else prev_item
->next
= item
;
7200 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7201 err
= extract_DbPDZRecord(context
,
7202 (struct isds_commercial_permission
**) (&item
->data
),
7204 if (err
) goto leave
;
7210 isds_list_free(permissions
);
7213 xmlXPathFreeObject(result
);
7214 xmlXPathFreeContext(xpath_ctx
);
7215 xmlFreeDoc(response
);
7217 #else /* not HAVE_LIBCURL */
7225 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7226 * code, destroy response and log success.
7227 * @context is ISDS session context.
7228 * @service_name is name of SERVICE_DB_MANIPULATION service
7229 * @box_id is UTF-8 encoded box identifier as zero terminated string
7230 * @approval is optional external approval of box manipulation
7231 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7232 * NULL, if you don't care. */
7233 static isds_error
build_send_manipulationdbid_request_check_drop_response(
7234 struct isds_ctx
*context
, const xmlChar
*service_name
,
7235 const xmlChar
*box_id
, const struct isds_approval
*approval
,
7236 xmlChar
**refnumber
) {
7237 isds_error err
= IE_SUCCESS
;
7239 xmlDocPtr response
= NULL
;
7242 if (!context
) return IE_INVALID_CONTEXT
;
7243 zfree(context
->long_message
);
7244 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
7247 /* Check if connection is established */
7248 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7250 /* Do request and check for success */
7251 err
= build_send_dbid_request_check_response(context
,
7252 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
7253 &response
, refnumber
);
7254 xmlFreeDoc(response
);
7257 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7258 isds_log(ILF_ISDS
, ILL_DEBUG
,
7259 _("%s request processed by server successfully.\n"),
7260 service_name_locale
);
7261 free(service_name_locale
);
7263 #else /* not HAVE_LIBCURL */
7271 /* Switch box into state where box can receive commercial messages (off by
7273 * @context is ISDS session context.
7274 * @box_id is UTF-8 encoded box identifier as zero terminated string
7275 * @allow is true for enable, false for disable commercial messages income
7276 * @approval is optional external approval of box manipulation
7277 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7278 * NULL, if you don't care. */
7279 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
7280 const char *box_id
, const _Bool allow
,
7281 const struct isds_approval
*approval
, char **refnumber
) {
7282 return build_send_manipulationdbid_request_check_drop_response(context
,
7283 (allow
) ? BAD_CAST
"SetOpenAddressing" :
7284 BAD_CAST
"ClearOpenAddressing",
7285 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7289 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7290 * message acceptance). This is just a box permission. Sender must apply
7291 * such role by sending each message.
7292 * @context is ISDS session context.
7293 * @box_id is UTF-8 encoded box identifier as zero terminated string
7294 * @allow is true for enable, false for disable OVM role permission
7295 * @approval is optional external approval of box manipulation
7296 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7297 * NULL, if you don't care. */
7298 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
7299 const char *box_id
, const _Bool allow
,
7300 const struct isds_approval
*approval
, char **refnumber
) {
7301 return build_send_manipulationdbid_request_check_drop_response(context
,
7302 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
7303 BAD_CAST
"ClearEffectiveOVM",
7304 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7308 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7309 * code, destroy response and log success.
7310 * @context is ISDS session context.
7311 * @service_name is name of SERVICE_DB_MANIPULATION service
7312 * @owner is structure describing box
7313 * @approval is optional external approval of box manipulation
7314 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7315 * NULL, if you don't care. */
7316 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
7317 struct isds_ctx
*context
, const xmlChar
*service_name
,
7318 const struct isds_DbOwnerInfo
*owner
,
7319 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7320 isds_error err
= IE_SUCCESS
;
7322 char *service_name_locale
= NULL
;
7323 xmlNodePtr request
= NULL
, db_owner_info
;
7324 xmlNsPtr isds_ns
= NULL
;
7328 if (!context
) return IE_INVALID_CONTEXT
;
7329 zfree(context
->long_message
);
7330 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
7333 service_name_locale
= _isds_utf82locale((char*)service_name
);
7334 if (!service_name_locale
) {
7340 request
= xmlNewNode(NULL
, service_name
);
7342 isds_printf_message(context
,
7343 _("Could not build %s request"), service_name_locale
);
7347 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7349 isds_log_message(context
, _("Could not create ISDS name space"));
7353 xmlSetNs(request
, isds_ns
);
7356 /* Add XSD:tOwnerInfoInput child*/
7357 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
7358 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
7359 if (err
) goto leave
;
7361 /* Add XSD:gExtApproval*/
7362 err
= insert_GExtApproval(context
, approval
, request
);
7363 if (err
) goto leave
;
7365 /* Send it to server and process response */
7366 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7367 service_name
, &request
, refnumber
);
7370 xmlFreeNode(request
);
7371 free(service_name_locale
);
7372 #else /* not HAVE_LIBCURL */
7380 /* Switch box accessibility state on request of box owner.
7381 * Despite the name, owner must do the request off-line. This function is
7382 * designed for such off-line meeting points (e.g. Czech POINT).
7383 * @context is ISDS session context.
7384 * @box identifies box to switch accessibility state.
7385 * @allow is true for making accessible, false to disallow access.
7386 * @approval is optional external approval of box manipulation
7387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7388 * NULL, if you don't care. */
7389 isds_error
isds_switch_box_accessibility_on_owner_request(
7390 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7391 const _Bool allow
, const struct isds_approval
*approval
,
7393 return build_send_manipulationdbowner_request_check_drop_response(context
,
7394 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
7395 BAD_CAST
"DisableOwnDataBox",
7396 box
, approval
, (xmlChar
**) refnumber
);
7400 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7402 * @context is ISDS session context.
7403 * @box identifies box to switch accessibility state.
7404 * @since is date since accessibility has been denied. This can be past too.
7405 * Only tm_year, tm_mon and tm_mday carry sane value.
7406 * @approval is optional external approval of box manipulation
7407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7408 * NULL, if you don't care. */
7409 isds_error
isds_disable_box_accessibility_externaly(
7410 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7411 const struct tm
*since
, const struct isds_approval
*approval
,
7413 isds_error err
= IE_SUCCESS
;
7415 char *service_name_locale
= NULL
;
7416 xmlNodePtr request
= NULL
, node
;
7417 xmlNsPtr isds_ns
= NULL
;
7418 xmlChar
*string
= NULL
;
7422 if (!context
) return IE_INVALID_CONTEXT
;
7423 zfree(context
->long_message
);
7424 if (!box
|| !since
) return IE_INVAL
;
7428 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
7430 isds_printf_message(context
,
7431 _("Could not build %s request"), "DisableDataBoxExternally");
7435 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7437 isds_log_message(context
, _("Could not create ISDS name space"));
7441 xmlSetNs(request
, isds_ns
);
7444 /* Add @box identification */
7445 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7446 err
= insert_DbOwnerInfo(context
, box
, node
);
7447 if (err
) goto leave
;
7449 /* Add @since date */
7450 err
= tm2datestring(since
, &string
);
7452 isds_log_message(context
,
7453 _("Could not convert `since' argument to ISO date string"));
7456 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
7460 err
= insert_GExtApproval(context
, approval
, request
);
7461 if (err
) goto leave
;
7463 /* Send it to server and process response */
7464 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7465 BAD_CAST
"DisableDataBoxExternally", &request
,
7466 (xmlChar
**) refnumber
);
7470 xmlFreeNode(request
);
7471 free(service_name_locale
);
7472 #else /* not HAVE_LIBCURL */
7481 /* Insert struct isds_message data (envelope (recipient data optional) and
7482 * documents into XML tree
7483 * @context is session context
7484 * @outgoing_message is libisds structure with message data
7485 * @create_message is XML CreateMessage or CreateMultipleMessage element
7486 * @process_recipient true for recipient data serialization, false for no
7488 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
7489 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
7490 const _Bool process_recipient
) {
7492 isds_error err
= IE_SUCCESS
;
7493 xmlNodePtr envelope
, dm_files
, node
;
7494 xmlChar
*string
= NULL
;
7496 if (!context
) return IE_INVALID_CONTEXT
;
7497 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
7500 /* Build envelope */
7501 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
7503 isds_printf_message(context
, _("Could not add dmEnvelope child to "
7504 "%s element"), create_message
->name
);
7508 if (!outgoing_message
->envelope
) {
7509 isds_log_message(context
, _("Outgoing message is missing envelope"));
7514 /* Insert optional message type */
7515 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
7517 if (err
) goto leave
;
7519 INSERT_STRING(envelope
, "dmSenderOrgUnit",
7520 outgoing_message
->envelope
->dmSenderOrgUnit
);
7521 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
7522 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
7524 if (process_recipient
) {
7525 if (!outgoing_message
->envelope
->dbIDRecipient
) {
7526 isds_log_message(context
,
7527 _("Outgoing message is missing recipient box identifier"));
7531 INSERT_STRING(envelope
, "dbIDRecipient",
7532 outgoing_message
->envelope
->dbIDRecipient
);
7534 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
7535 outgoing_message
->envelope
->dmRecipientOrgUnit
);
7536 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
7537 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
7538 INSERT_STRING(envelope
, "dmToHands",
7539 outgoing_message
->envelope
->dmToHands
);
7542 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
7544 INSERT_STRING(envelope
, "dmAnnotation",
7545 outgoing_message
->envelope
->dmAnnotation
);
7547 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
7548 0, 50, "dmRecipientRefNumber");
7549 INSERT_STRING(envelope
, "dmRecipientRefNumber",
7550 outgoing_message
->envelope
->dmRecipientRefNumber
);
7552 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
7553 0, 50, "dmSenderRefNumber");
7554 INSERT_STRING(envelope
, "dmSenderRefNumber",
7555 outgoing_message
->envelope
->dmSenderRefNumber
);
7557 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
7558 0, 50, "dmRecipientIdent");
7559 INSERT_STRING(envelope
, "dmRecipientIdent",
7560 outgoing_message
->envelope
->dmRecipientIdent
);
7562 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
7563 0, 50, "dmSenderIdent");
7564 INSERT_STRING(envelope
, "dmSenderIdent",
7565 outgoing_message
->envelope
->dmSenderIdent
);
7567 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
7568 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
7569 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
7570 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
7571 INSERT_STRING(envelope
, "dmLegalTitleSect",
7572 outgoing_message
->envelope
->dmLegalTitleSect
);
7573 INSERT_STRING(envelope
, "dmLegalTitlePar",
7574 outgoing_message
->envelope
->dmLegalTitlePar
);
7575 INSERT_STRING(envelope
, "dmLegalTitlePoint",
7576 outgoing_message
->envelope
->dmLegalTitlePoint
);
7578 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
7579 outgoing_message
->envelope
->dmPersonalDelivery
);
7580 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
7581 outgoing_message
->envelope
->dmAllowSubstDelivery
);
7583 /* ???: Should we require value for dbEffectiveOVM sender?
7584 * ISDS has default as true */
7585 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
7586 INSERT_BOOLEAN(envelope
, "dmOVM",
7587 outgoing_message
->envelope
->dmPublishOwnID
);
7590 /* Append dmFiles */
7591 if (!outgoing_message
->documents
) {
7592 isds_log_message(context
,
7593 _("Outgoing message is missing list of documents"));
7597 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
7599 isds_printf_message(context
, _("Could not add dmFiles child to "
7600 "%s element"), create_message
->name
);
7605 /* Check for document hierarchy */
7606 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
7607 if (err
) goto leave
;
7609 /* Process each document */
7610 for (struct isds_list
*item
=
7611 (struct isds_list
*) outgoing_message
->documents
;
7612 item
; item
= item
->next
) {
7614 isds_log_message(context
,
7615 _("List of documents contains empty item"));
7619 /* FIXME: Check for dmFileMetaType and for document references.
7620 * Only first document can be of MAIN type */
7621 err
= insert_document(context
, (struct isds_document
*) item
->data
,
7624 if (err
) goto leave
;
7631 #endif /* HAVE_LIBCURL */
7634 /* Send a message via ISDS to a recipient
7635 * @context is session context
7636 * @outgoing_message is message to send; Some members are mandatory (like
7637 * dbIDRecipient), some are optional and some are irrelevant (especially data
7638 * about sender). Included pointer to isds_list documents must contain at
7639 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7640 * members will be filled with valid data from ISDS. Exact list of write
7641 * members is subject to change. Currently dmID is changed.
7642 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7643 isds_error
isds_send_message(struct isds_ctx
*context
,
7644 struct isds_message
*outgoing_message
) {
7646 isds_error err
= IE_SUCCESS
;
7648 xmlNsPtr isds_ns
= NULL
;
7649 xmlNodePtr request
= NULL
;
7650 xmlDocPtr response
= NULL
;
7651 xmlChar
*code
= NULL
, *message
= NULL
;
7652 xmlXPathContextPtr xpath_ctx
= NULL
;
7653 xmlXPathObjectPtr result
= NULL
;
7654 /*_Bool message_is_complete = 0;*/
7657 if (!context
) return IE_INVALID_CONTEXT
;
7658 zfree(context
->long_message
);
7659 if (!outgoing_message
) return IE_INVAL
;
7662 /* Check if connection is established
7663 * TODO: This check should be done downstairs. */
7664 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7667 /* Build CreateMessage request */
7668 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
7670 isds_log_message(context
,
7671 _("Could not build CreateMessage request"));
7674 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7676 isds_log_message(context
, _("Could not create ISDS name space"));
7677 xmlFreeNode(request
);
7680 xmlSetNs(request
, isds_ns
);
7682 /* Append envelope and files */
7683 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
7684 if (err
) goto leave
;
7687 /* Signal we can serialize message since now */
7688 /*message_is_complete = 1;*/
7691 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
7694 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
7696 /* Don't' destroy request, we want to provide it to application later */
7699 isds_log(ILF_ISDS
, ILL_DEBUG
,
7700 _("Processing ISDS response on CreateMessage "
7701 "request failed\n"));
7705 /* Check for response status */
7706 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
7707 &code
, &message
, NULL
);
7709 isds_log(ILF_ISDS
, ILL_DEBUG
,
7710 _("ISDS response on CreateMessage request "
7711 "is missing status\n"));
7715 /* Request processed, but refused by server or server failed */
7716 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7717 char *box_id_locale
=
7718 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7719 char *code_locale
= _isds_utf82locale((char*)code
);
7720 char *message_locale
= _isds_utf82locale((char*)message
);
7721 isds_log(ILF_ISDS
, ILL_DEBUG
,
7722 _("Server did not accept message for %s on CreateMessage "
7723 "request (code=%s, message=%s)\n"),
7724 box_id_locale
, code_locale
, message_locale
);
7725 isds_log_message(context
, message_locale
);
7726 free(box_id_locale
);
7728 free(message_locale
);
7735 xpath_ctx
= xmlXPathNewContext(response
);
7740 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7744 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
7750 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7751 isds_log_message(context
, _("Missing CreateMessageResponse element"));
7755 if (result
->nodesetval
->nodeNr
> 1) {
7756 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
7760 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7761 xmlXPathFreeObject(result
); result
= NULL
;
7763 if (outgoing_message
->envelope
->dmID
) {
7764 free(outgoing_message
->envelope
->dmID
);
7765 outgoing_message
->envelope
->dmID
= NULL
;
7767 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
7768 if (!outgoing_message
->envelope
->dmID
) {
7769 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
7770 "but did not return assigned message ID\n"));
7774 /* TODO: Serialize message into structure member raw */
7775 /* XXX: Each web service transport message in different format.
7776 * Therefore it's not possible to save them directly.
7777 * To save them, one must figure out common format.
7778 * We can leave it on application, or we can implement the ESS format. */
7779 /*if (message_is_complete) {
7780 if (outgoing_message->envelope->dmID) {
7782 /* Add assigned message ID as first child*/
7783 /*xmlNodePtr dmid_text = xmlNewText(
7784 (xmlChar *) outgoing_message->envelope->dmID);
7785 if (!dmid_text) goto serialization_failed;
7787 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7789 if (!dmid_element) {
7790 xmlFreeNode(dmid_text);
7791 goto serialization_failed;
7794 xmlNodePtr dmid_element_with_text =
7795 xmlAddChild(dmid_element, dmid_text);
7796 if (!dmid_element_with_text) {
7797 xmlFreeNode(dmid_element);
7798 xmlFreeNode(dmid_text);
7799 goto serialization_failed;
7802 node = xmlAddPrevSibling(envelope->childern,
7803 dmid_element_with_text);
7805 xmlFreeNodeList(dmid_element_with_text);
7806 goto serialization_failed;
7810 /* Serialize message with ID into raw */
7811 /*buffer = serialize_element(envelope)*/
7814 serialization_failed:
7819 xmlXPathFreeObject(result
);
7820 xmlXPathFreeContext(xpath_ctx
);
7824 xmlFreeDoc(response
);
7825 xmlFreeNode(request
);
7828 isds_log(ILF_ISDS
, ILL_DEBUG
,
7829 _("CreateMessage request processed by server "
7830 "successfully.\n"));
7831 #else /* not HAVE_LIBCURL */
7839 /* Send a message via ISDS to a multiple recipients
7840 * @context is session context
7841 * @outgoing_message is message to send; Some members are mandatory,
7842 * some are optional and some are irrelevant (especially data
7843 * about sender). Data about recipient will be substituted by ISDS from
7844 * @copies. Included pointer to isds_list documents must
7845 * contain at least one document of FILEMETATYPE_MAIN.
7846 * @copies is list of isds_message_copy structures addressing all desired
7847 * recipients. This is read-write structure, some members will be filled with
7848 * valid data from ISDS (message IDs, error codes, error descriptions).
7850 * ISDS_SUCCESS if all messages have been sent
7851 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7852 * succeeded messages can be identified by copies->data->error),
7853 * or other error code if something other goes wrong. */
7854 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
7855 const struct isds_message
*outgoing_message
,
7856 struct isds_list
*copies
) {
7858 isds_error err
= IE_SUCCESS
;
7860 isds_error append_err
;
7861 xmlNsPtr isds_ns
= NULL
;
7862 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
7863 struct isds_list
*item
;
7864 struct isds_message_copy
*copy
;
7865 xmlDocPtr response
= NULL
;
7866 xmlChar
*code
= NULL
, *message
= NULL
;
7867 xmlXPathContextPtr xpath_ctx
= NULL
;
7868 xmlXPathObjectPtr result
= NULL
;
7869 xmlChar
*string
= NULL
;
7873 if (!context
) return IE_INVALID_CONTEXT
;
7874 zfree(context
->long_message
);
7875 if (!outgoing_message
|| !copies
) return IE_INVAL
;
7878 /* Check if connection is established
7879 * TODO: This check should be done downstairs. */
7880 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7883 /* Build CreateMultipleMessage request */
7884 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
7886 isds_log_message(context
,
7887 _("Could not build CreateMultipleMessage request"));
7890 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7892 isds_log_message(context
, _("Could not create ISDS name space"));
7893 xmlFreeNode(request
);
7896 xmlSetNs(request
, isds_ns
);
7899 /* Build recipients */
7900 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
7902 isds_log_message(context
, _("Could not add dmRecipients child to "
7903 "CreateMultipleMessage element"));
7904 xmlFreeNode(request
);
7908 /* Insert each recipient */
7909 for (item
= copies
; item
; item
= item
->next
) {
7910 copy
= (struct isds_message_copy
*) item
->data
;
7912 isds_log_message(context
,
7913 _("`copies' list item contains empty data"));
7918 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
7920 isds_log_message(context
, _("Could not add dmRecipient child to "
7921 "dmRecipients element"));
7926 if (!copy
->dbIDRecipient
) {
7927 isds_log_message(context
,
7928 _("Message copy is missing recipient box identifier"));
7932 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
7933 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
7934 copy
->dmRecipientOrgUnit
);
7935 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
7936 copy
->dmRecipientOrgUnitNum
, string
);
7937 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
7940 /* Append envelope and files */
7941 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
7942 if (err
) goto leave
;
7945 isds_log(ILF_ISDS
, ILL_DEBUG
,
7946 _("Sending CreateMultipleMessage request to ISDS\n"));
7949 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
7951 isds_log(ILF_ISDS
, ILL_DEBUG
,
7952 _("Processing ISDS response on CreateMultipleMessage "
7953 "request failed\n"));
7957 /* Check for response status */
7958 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
7959 &code
, &message
, NULL
);
7961 isds_log(ILF_ISDS
, ILL_DEBUG
,
7962 _("ISDS response on CreateMultipleMessage request "
7963 "is missing status\n"));
7967 /* Request processed, but some copies failed */
7968 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
7969 char *box_id_locale
=
7970 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7971 char *code_locale
= _isds_utf82locale((char*)code
);
7972 char *message_locale
= _isds_utf82locale((char*)message
);
7973 isds_log(ILF_ISDS
, ILL_DEBUG
,
7974 _("Server did accept message for multiple recipients "
7975 "on CreateMultipleMessage request but delivery to "
7976 "some of them failed (code=%s, message=%s)\n"),
7977 box_id_locale
, code_locale
, message_locale
);
7978 isds_log_message(context
, message_locale
);
7979 free(box_id_locale
);
7981 free(message_locale
);
7982 err
= IE_PARTIAL_SUCCESS
;
7985 /* Request refused by server as whole */
7986 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7987 char *box_id_locale
=
7988 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7989 char *code_locale
= _isds_utf82locale((char*)code
);
7990 char *message_locale
= _isds_utf82locale((char*)message
);
7991 isds_log(ILF_ISDS
, ILL_DEBUG
,
7992 _("Server did not accept message for multiple recipients "
7993 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7994 box_id_locale
, code_locale
, message_locale
);
7995 isds_log_message(context
, message_locale
);
7996 free(box_id_locale
);
7998 free(message_locale
);
8005 xpath_ctx
= xmlXPathNewContext(response
);
8010 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8014 result
= xmlXPathEvalExpression(
8015 BAD_CAST
"/isds:CreateMultipleMessageResponse"
8016 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8022 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8023 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
8028 /* Extract message ID and delivery status for each copy */
8029 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
8030 item
= item
->next
, i
++) {
8031 copy
= (struct isds_message_copy
*) item
->data
;
8032 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8034 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
8040 if (item
|| i
< result
->nodesetval
->nodeNr
) {
8041 isds_printf_message(context
, _("ISDS returned unexpected number of "
8042 "message copy delivery states: %d"),
8043 result
->nodesetval
->nodeNr
);
8052 xmlXPathFreeObject(result
);
8053 xmlXPathFreeContext(xpath_ctx
);
8057 xmlFreeDoc(response
);
8058 xmlFreeNode(request
);
8061 isds_log(ILF_ISDS
, ILL_DEBUG
,
8062 _("CreateMultipleMessageResponse request processed by server "
8063 "successfully.\n"));
8064 #else /* not HAVE_LIBCURL */
8072 /* Get list of messages. This is common core for getting sent or received
8074 * Any criterion argument can be NULL, if you don't care about it.
8075 * @context is session context. Must not be NULL.
8076 * @outgoing_direction is true if you want list of outgoing messages,
8077 * it's false if you want incoming messages.
8078 * @from_time is minimal time and date of message sending inclusive.
8079 * @to_time is maximal time and date of message sending inclusive
8080 * @organization_unit_number is number of sender/recipient respectively.
8081 * @status_filter is bit field of isds_message_status values. Use special
8082 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8083 * all values, you can use bit-wise arithmetic if you want.)
8084 * @offset is index of first message we are interested in. First message is 1.
8085 * Set to 0 (or 1) if you don't care.
8086 * @number is maximal length of list you want to get as input value, outputs
8087 * number of messages matching these criteria. Can be NULL if you don't care
8088 * (applies to output value either).
8089 * @messages is automatically reallocated list of isds_message's. Be ware that
8090 * it returns only brief overview (envelope and some other fields) about each
8091 * message, not the complete message. FIXME: Specify exact fields.
8092 * The list is sorted by delivery time in ascending order.
8093 * Use NULL if you don't care about don't need the data (useful if you want to
8094 * know only the @number). If you provide &NULL, list will be allocated on
8095 * heap, if you provide pointer to non-NULL, list will be freed automatically
8096 * at first. Also in case of error the list will be NULLed.
8097 * @return IE_SUCCESS or appropriate error code. */
8098 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
8099 _Bool outgoing_direction
,
8100 const struct timeval
*from_time
, const struct timeval
*to_time
,
8101 const long int *organization_unit_number
,
8102 const unsigned int status_filter
,
8103 const unsigned long int offset
, unsigned long int *number
,
8104 struct isds_list
**messages
) {
8106 isds_error err
= IE_SUCCESS
;
8108 xmlNsPtr isds_ns
= NULL
;
8109 xmlNodePtr request
= NULL
, node
;
8110 xmlDocPtr response
= NULL
;
8111 xmlChar
*code
= NULL
, *message
= NULL
;
8112 xmlXPathContextPtr xpath_ctx
= NULL
;
8113 xmlXPathObjectPtr result
= NULL
;
8114 xmlChar
*string
= NULL
;
8115 long unsigned int count
= 0;
8118 if (!context
) return IE_INVALID_CONTEXT
;
8119 zfree(context
->long_message
);
8121 /* Free former message list if any */
8122 if (messages
) isds_list_free(messages
);
8125 /* Check if connection is established
8126 * TODO: This check should be done downstairs. */
8127 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8129 /* Build GetListOf*Messages request */
8130 request
= xmlNewNode(NULL
,
8131 (outgoing_direction
) ?
8132 BAD_CAST
"GetListOfSentMessages" :
8133 BAD_CAST
"GetListOfReceivedMessages"
8136 isds_log_message(context
,
8137 (outgoing_direction
) ?
8138 _("Could not build GetListOfSentMessages request") :
8139 _("Could not build GetListOfReceivedMessages request")
8143 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8145 isds_log_message(context
, _("Could not create ISDS name space"));
8146 xmlFreeNode(request
);
8149 xmlSetNs(request
, isds_ns
);
8153 err
= timeval2timestring(from_time
, &string
);
8154 if (err
) goto leave
;
8156 INSERT_STRING(request
, "dmFromTime", string
);
8157 free(string
); string
= NULL
;
8160 err
= timeval2timestring(to_time
, &string
);
8161 if (err
) goto leave
;
8163 INSERT_STRING(request
, "dmToTime", string
);
8164 free(string
); string
= NULL
;
8166 if (outgoing_direction
) {
8167 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
8168 organization_unit_number
, string
);
8170 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
8171 organization_unit_number
, string
);
8174 if (status_filter
> MESSAGESTATE_ANY
) {
8175 isds_printf_message(context
,
8176 _("Invalid message state filter value: %ld"), status_filter
);
8180 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
8183 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
8185 INSERT_STRING(request
, "dmOffset", "1");
8188 /* number 0 means no limit */
8189 if (number
&& *number
== 0) {
8190 INSERT_STRING(request
, "dmLimit", NULL
);
8192 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
8196 isds_log(ILF_ISDS
, ILL_DEBUG
,
8197 (outgoing_direction
) ?
8198 _("Sending GetListOfSentMessages request to ISDS\n") :
8199 _("Sending GetListOfReceivedMessages request to ISDS\n")
8203 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
8204 xmlFreeNode(request
); request
= NULL
;
8207 isds_log(ILF_ISDS
, ILL_DEBUG
,
8208 (outgoing_direction
) ?
8209 _("Processing ISDS response on GetListOfSentMessages "
8210 "request failed\n") :
8211 _("Processing ISDS response on GetListOfReceivedMessages "
8217 /* Check for response status */
8218 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
8219 &code
, &message
, NULL
);
8221 isds_log(ILF_ISDS
, ILL_DEBUG
,
8222 (outgoing_direction
) ?
8223 _("ISDS response on GetListOfSentMessages request "
8224 "is missing status\n") :
8225 _("ISDS response on GetListOfReceivedMessages request "
8226 "is missing status\n")
8231 /* Request processed, but nothing found */
8232 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8233 char *code_locale
= _isds_utf82locale((char*)code
);
8234 char *message_locale
= _isds_utf82locale((char*)message
);
8235 isds_log(ILF_ISDS
, ILL_DEBUG
,
8236 (outgoing_direction
) ?
8237 _("Server refused GetListOfSentMessages request "
8238 "(code=%s, message=%s)\n") :
8239 _("Server refused GetListOfReceivedMessages request "
8240 "(code=%s, message=%s)\n"),
8241 code_locale
, message_locale
);
8242 isds_log_message(context
, message_locale
);
8244 free(message_locale
);
8251 xpath_ctx
= xmlXPathNewContext(response
);
8256 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8260 result
= xmlXPathEvalExpression(
8261 (outgoing_direction
) ?
8262 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
8263 "isds:dmRecords/isds:dmRecord" :
8264 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
8265 "isds:dmRecords/isds:dmRecord",
8272 /* Fill output arguments in */
8273 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8274 struct isds_envelope
*envelope
;
8275 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8277 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8278 /* Create new message */
8279 item
= calloc(1, sizeof(*item
));
8284 item
->destructor
= (void(*)(void**)) &isds_message_free
;
8285 item
->data
= calloc(1, sizeof(struct isds_message
));
8287 isds_list_free(&item
);
8292 /* Extract envelope data */
8293 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8295 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
8297 isds_list_free(&item
);
8301 /* Attach extracted envelope */
8302 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
8304 /* Append new message into the list */
8306 *messages
= last_item
= item
;
8308 last_item
->next
= item
;
8313 if (number
) *number
= count
;
8317 isds_list_free(messages
);
8321 xmlXPathFreeObject(result
);
8322 xmlXPathFreeContext(xpath_ctx
);
8326 xmlFreeDoc(response
);
8327 xmlFreeNode(request
);
8330 isds_log(ILF_ISDS
, ILL_DEBUG
,
8331 (outgoing_direction
) ?
8332 _("GetListOfSentMessages request processed by server "
8333 "successfully.\n") :
8334 _("GetListOfReceivedMessages request processed by server "
8337 #else /* not HAVE_LIBCURL */
8344 /* Get list of outgoing (already sent) messages.
8345 * Any criterion argument can be NULL, if you don't care about it.
8346 * @context is session context. Must not be NULL.
8347 * @from_time is minimal time and date of message sending inclusive.
8348 * @to_time is maximal time and date of message sending inclusive
8349 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8350 * @status_filter is bit field of isds_message_status values. Use special
8351 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8352 * all values, you can use bit-wise arithmetic if you want.)
8353 * @offset is index of first message we are interested in. First message is 1.
8354 * Set to 0 (or 1) if you don't care.
8355 * @number is maximal length of list you want to get as input value, outputs
8356 * number of messages matching these criteria. Can be NULL if you don't care
8357 * (applies to output value either).
8358 * @messages is automatically reallocated list of isds_message's. Be ware that
8359 * it returns only brief overview (envelope and some other fields) about each
8360 * message, not the complete message. FIXME: Specify exact fields.
8361 * The list is sorted by delivery time in ascending order.
8362 * Use NULL if you don't care about the meta data (useful if you want to know
8363 * only the @number). If you provide &NULL, list will be allocated on heap,
8364 * if you provide pointer to non-NULL, list will be freed automatically at
8365 * first. Also in case of error the list will be NULLed.
8366 * @return IE_SUCCESS or appropriate error code. */
8367 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
8368 const struct timeval
*from_time
, const struct timeval
*to_time
,
8369 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
8370 const unsigned long int offset
, unsigned long int *number
,
8371 struct isds_list
**messages
) {
8373 return isds_get_list_of_messages(
8375 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
8381 /* Get list of incoming (addressed to you) messages.
8382 * Any criterion argument can be NULL, if you don't care about it.
8383 * @context is session context. Must not be NULL.
8384 * @from_time is minimal time and date of message sending inclusive.
8385 * @to_time is maximal time and date of message sending inclusive
8386 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8387 * @status_filter is bit field of isds_message_status values. Use special
8388 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8389 * all values, you can use bit-wise arithmetic if you want.)
8390 * @offset is index of first message we are interested in. First message is 1.
8391 * Set to 0 (or 1) if you don't care.
8392 * @number is maximal length of list you want to get as input value, outputs
8393 * number of messages matching these criteria. Can be NULL if you don't care
8394 * (applies to output value either).
8395 * @messages is automatically reallocated list of isds_message's. Be ware that
8396 * it returns only brief overview (envelope and some other fields) about each
8397 * message, not the complete message. FIXME: Specify exact fields.
8398 * Use NULL if you don't care about the meta data (useful if you want to know
8399 * only the @number). If you provide &NULL, list will be allocated on heap,
8400 * if you provide pointer to non-NULL, list will be freed automatically at
8401 * first. Also in case of error the list will be NULLed.
8402 * @return IE_SUCCESS or appropriate error code. */
8403 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
8404 const struct timeval
*from_time
, const struct timeval
*to_time
,
8405 const long int *dmRecipientOrgUnitNum
,
8406 const unsigned int status_filter
,
8407 const unsigned long int offset
, unsigned long int *number
,
8408 struct isds_list
**messages
) {
8410 return isds_get_list_of_messages(
8412 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
8418 /* Get list of sent message state changes.
8419 * Any criterion argument can be NULL, if you don't care about it.
8420 * @context is session context. Must not be NULL.
8421 * @from_time is minimal time and date of status changes inclusive
8422 * @to_time is maximal time and date of status changes inclusive
8423 * @changed_states is automatically reallocated list of
8424 * isds_message_status_change's. If you provide &NULL, list will be allocated
8425 * on heap, if you provide pointer to non-NULL, list will be freed
8426 * automatically at first. Also in case of error the list will be NULLed.
8427 * XXX: The list item ordering is not specified.
8428 * XXX: Server provides only `recent' changes.
8429 * @return IE_SUCCESS or appropriate error code. */
8430 isds_error
isds_get_list_of_sent_message_state_changes(
8431 struct isds_ctx
*context
,
8432 const struct timeval
*from_time
, const struct timeval
*to_time
,
8433 struct isds_list
**changed_states
) {
8435 isds_error err
= IE_SUCCESS
;
8437 xmlNsPtr isds_ns
= NULL
;
8438 xmlNodePtr request
= NULL
, node
;
8439 xmlDocPtr response
= NULL
;
8440 xmlXPathContextPtr xpath_ctx
= NULL
;
8441 xmlXPathObjectPtr result
= NULL
;
8442 xmlChar
*string
= NULL
;
8443 long unsigned int count
= 0;
8446 if (!context
) return IE_INVALID_CONTEXT
;
8447 zfree(context
->long_message
);
8449 /* Free former message list if any */
8450 isds_list_free(changed_states
);
8453 /* Check if connection is established
8454 * TODO: This check should be done downstairs. */
8455 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8457 /* Build GetMessageStateChanges request */
8458 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
8460 isds_log_message(context
,
8461 _("Could not build GetMessageStateChanges request"));
8464 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8466 isds_log_message(context
, _("Could not create ISDS name space"));
8467 xmlFreeNode(request
);
8470 xmlSetNs(request
, isds_ns
);
8474 err
= timeval2timestring(from_time
, &string
);
8475 if (err
) goto leave
;
8477 INSERT_STRING(request
, "dmFromTime", string
);
8481 err
= timeval2timestring(to_time
, &string
);
8482 if (err
) goto leave
;
8484 INSERT_STRING(request
, "dmToTime", string
);
8489 err
= send_destroy_request_check_response(context
,
8490 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
8492 if (err
) goto leave
;
8496 xpath_ctx
= xmlXPathNewContext(response
);
8501 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8505 result
= xmlXPathEvalExpression(
8506 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
8507 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
8513 /* Fill output arguments in */
8514 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8515 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8517 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8518 /* Create new status change */
8519 item
= calloc(1, sizeof(*item
));
8525 (void(*)(void**)) &isds_message_status_change_free
;
8527 /* Extract message status change */
8528 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8529 err
= extract_StateChangesRecord(context
,
8530 (struct isds_message_status_change
**) &item
->data
,
8533 isds_list_free(&item
);
8537 /* Append new message status change into the list */
8538 if (!*changed_states
) {
8539 *changed_states
= last_item
= item
;
8541 last_item
->next
= item
;
8549 isds_list_free(changed_states
);
8553 xmlXPathFreeObject(result
);
8554 xmlXPathFreeContext(xpath_ctx
);
8555 xmlFreeDoc(response
);
8556 xmlFreeNode(request
);
8559 isds_log(ILF_ISDS
, ILL_DEBUG
,
8560 _("GetMessageStateChanges request processed by server "
8561 "successfully.\n"));
8562 #else /* not HAVE_LIBCURL */
8570 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8572 * @context is session context
8573 * @service is ISDS WS service handler
8574 * @service_name is name of SERVICE_DM_OPERATIONS
8575 * @message_id is message ID to send as service argument to ISDS
8576 * @response is server SOAP body response as XML document
8577 * @raw_response is automatically reallocated bit stream with response body. Use
8578 * NULL if you don't care
8579 * @raw_response_length is size of @raw_response in bytes
8580 * @code is ISDS status code
8581 * @status_message is ISDS status message
8582 * @return error coded from lower layer, context message will be set up
8584 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
8585 const isds_service service
, const xmlChar
*service_name
,
8586 const char *message_id
,
8587 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
8588 xmlChar
**code
, xmlChar
**status_message
) {
8590 isds_error err
= IE_SUCCESS
;
8591 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
8592 xmlNodePtr request
= NULL
, node
;
8593 xmlNsPtr isds_ns
= NULL
;
8595 if (!context
) return IE_INVALID_CONTEXT
;
8596 if (!service_name
|| !message_id
) return IE_INVAL
;
8597 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
8598 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
8600 /* Free output argument */
8601 xmlFreeDoc(*response
); *response
= NULL
;
8602 if (raw_response
) zfree(*raw_response
);
8604 free(*status_message
);
8607 /* Check if connection is established
8608 * TODO: This check should be done downstairs. */
8609 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8611 service_name_locale
= _isds_utf82locale((char*)service_name
);
8612 message_id_locale
= _isds_utf82locale(message_id
);
8613 if (!service_name_locale
|| !message_id_locale
) {
8619 request
= xmlNewNode(NULL
, service_name
);
8621 isds_printf_message(context
,
8622 _("Could not build %s request for %s message ID"),
8623 service_name_locale
, message_id_locale
);
8627 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8629 isds_log_message(context
, _("Could not create ISDS name space"));
8633 xmlSetNs(request
, isds_ns
);
8636 /* Add requested ID */
8637 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
8638 if (err
) goto leave
;
8639 INSERT_STRING(request
, "dmID", message_id
);
8642 isds_log(ILF_ISDS
, ILL_DEBUG
,
8643 _("Sending %s request for %s message ID to ISDS\n"),
8644 service_name_locale
, message_id_locale
);
8647 err
= isds(context
, service
, request
, response
,
8648 raw_response
, raw_response_length
);
8649 xmlFreeNode(request
); request
= NULL
;
8652 isds_log(ILF_ISDS
, ILL_DEBUG
,
8653 _("Processing ISDS response on %s request failed\n"),
8654 service_name_locale
);
8658 /* Check for response status */
8659 err
= isds_response_status(context
, service
, *response
,
8660 code
, status_message
, NULL
);
8662 isds_log(ILF_ISDS
, ILL_DEBUG
,
8663 _("ISDS response on %s request is missing status\n"),
8664 service_name_locale
);
8668 /* Request processed, but nothing found */
8669 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
8670 char *code_locale
= _isds_utf82locale((char*) *code
);
8671 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
8672 isds_log(ILF_ISDS
, ILL_DEBUG
,
8673 _("Server refused %s request for %s message ID "
8674 "(code=%s, message=%s)\n"),
8675 service_name_locale
, message_id_locale
,
8676 code_locale
, status_message_locale
);
8677 isds_log_message(context
, status_message_locale
);
8679 free(status_message_locale
);
8685 free(message_id_locale
);
8686 free(service_name_locale
);
8687 xmlFreeNode(request
);
8692 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8693 * signed data and free ISDS response.
8694 * @context is session context
8695 * @message_id is UTF-8 encoded message ID for logging purpose
8696 * @response is parsed XML document. It will be freed and NULLed in the middle
8697 * of function run to save memory. This is not guaranteed in case of error.
8698 * @request_name is name of ISDS request used to construct response root
8699 * element name and for logging purpose.
8700 * @raw is reallocated output buffer with DER encoded CMS data
8701 * @raw_length is size of @raw buffer in bytes
8702 * @returns standard error codes, in case of error, @raw will be freed and
8703 * NULLed, @response sometimes. */
8704 static isds_error
find_extract_signed_data_free_response(
8705 struct isds_ctx
*context
, const xmlChar
*message_id
,
8706 xmlDocPtr
*response
, const xmlChar
*request_name
,
8707 void **raw
, size_t *raw_length
) {
8709 isds_error err
= IE_SUCCESS
;
8710 char *xpath_expression
= NULL
;
8711 xmlXPathContextPtr xpath_ctx
= NULL
;
8712 xmlXPathObjectPtr result
= NULL
;
8713 char *encoded_structure
= NULL
;
8715 if (!context
) return IE_INVALID_CONTEXT
;
8716 if (!raw
) return IE_INVAL
;
8718 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
8721 /* Build XPath expression */
8722 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
8723 "Response/isds:dmSignature");
8724 if (!xpath_expression
) return IE_NOMEM
;
8727 xpath_ctx
= xmlXPathNewContext(*response
);
8732 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8736 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
8741 /* Empty response */
8742 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8743 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8744 isds_printf_message(context
,
8745 _("Server did not return any signed data for message ID `%s' "
8747 message_id_locale
, request_name
);
8748 free(message_id_locale
);
8752 /* More responses */
8753 if (result
->nodesetval
->nodeNr
> 1) {
8754 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8755 isds_printf_message(context
,
8756 _("Server did return more signed data for message ID `%s' "
8758 message_id_locale
, request_name
);
8759 free(message_id_locale
);
8764 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8766 /* Extract PKCS#7 structure */
8767 EXTRACT_STRING(".", encoded_structure
);
8768 if (!encoded_structure
) {
8769 isds_log_message(context
, _("dmSignature element is empty"));
8772 /* Here we have delivery info as standalone CMS in encoded_structure.
8773 * We don't need any other data, free them: */
8774 xmlXPathFreeObject(result
); result
= NULL
;
8775 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
8776 xmlFreeDoc(*response
); *response
= NULL
;
8779 /* Decode PKCS#7 to DER format */
8780 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
8781 if (*raw_length
== (size_t) -1) {
8782 isds_log_message(context
,
8783 _("Error while Base64-decoding PKCS#7 structure"));
8794 free(encoded_structure
);
8795 xmlXPathFreeObject(result
);
8796 xmlXPathFreeContext(xpath_ctx
);
8797 free(xpath_expression
);
8801 #endif /* HAVE_LIBCURL */
8804 /* Download incoming message envelope identified by ID.
8805 * @context is session context
8806 * @message_id is message identifier (you can get them from
8807 * isds_get_list_of_received_messages())
8808 * @message is automatically reallocated message retrieved from ISDS.
8809 * It will miss documents per se. Use isds_get_received_message(), if you are
8810 * interested in documents (content) too.
8811 * Returned hash and timestamp require documents to be verifiable. */
8812 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
8813 const char *message_id
, struct isds_message
**message
) {
8815 isds_error err
= IE_SUCCESS
;
8817 xmlDocPtr response
= NULL
;
8818 xmlChar
*code
= NULL
, *status_message
= NULL
;
8819 xmlXPathContextPtr xpath_ctx
= NULL
;
8820 xmlXPathObjectPtr result
= NULL
;
8823 if (!context
) return IE_INVALID_CONTEXT
;
8824 zfree(context
->long_message
);
8826 /* Free former message if any */
8827 if (!message
) return IE_INVAL
;
8828 isds_message_free(message
);
8831 /* Do request and check for success */
8832 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
8833 BAD_CAST
"MessageEnvelopeDownload", message_id
,
8834 &response
, NULL
, NULL
, &code
, &status_message
);
8835 if (err
) goto leave
;
8838 xpath_ctx
= xmlXPathNewContext(response
);
8843 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8847 result
= xmlXPathEvalExpression(
8848 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
8849 "isds:dmReturnedMessageEnvelope",
8855 /* Empty response */
8856 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8857 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8858 isds_printf_message(context
,
8859 _("Server did not return any envelope for ID `%s' "
8860 "on MessageEnvelopeDownload request"), message_id_locale
);
8861 free(message_id_locale
);
8866 if (result
->nodesetval
->nodeNr
> 1) {
8867 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8868 isds_printf_message(context
,
8869 _("Server did return more envelopes for ID `%s' "
8870 "on MessageEnvelopeDownload request"), message_id_locale
);
8871 free(message_id_locale
);
8876 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8878 /* Extract the envelope (= message without documents, hence 0) */
8879 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
8880 if (err
) goto leave
;
8883 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
8884 &(*message
)->raw_length
);
8888 isds_message_free(message
);
8891 xmlXPathFreeObject(result
);
8892 xmlXPathFreeContext(xpath_ctx
);
8895 free(status_message
);
8896 if (!*message
|| !(*message
)->xml
) {
8897 xmlFreeDoc(response
);
8901 isds_log(ILF_ISDS
, ILL_DEBUG
,
8902 _("MessageEnvelopeDownload request processed by server "
8905 #else /* not HAVE_LIBCURL */
8912 /* Load delivery info of any format from buffer.
8913 * @context is session context
8914 * @raw_type advertises format of @buffer content. Only delivery info types
8916 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8917 * retrieve such data from message->raw after calling
8918 * isds_get_signed_delivery_info().
8919 * @length is length of buffer in bytes.
8920 * @message is automatically reallocated message parsed from @buffer.
8921 * @strategy selects how buffer will be attached into raw isds_message member.
8923 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
8924 const isds_raw_type raw_type
,
8925 const void *buffer
, const size_t length
,
8926 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
8928 isds_error err
= IE_SUCCESS
;
8929 message_ns_type message_ns
;
8930 xmlDocPtr message_doc
= NULL
;
8931 xmlXPathContextPtr xpath_ctx
= NULL
;
8932 xmlXPathObjectPtr result
= NULL
;
8933 void *xml_stream
= NULL
;
8934 size_t xml_stream_length
= 0;
8936 if (!context
) return IE_INVALID_CONTEXT
;
8937 zfree(context
->long_message
);
8938 if (!message
) return IE_INVAL
;
8939 isds_message_free(message
);
8940 if (!buffer
) return IE_INVAL
;
8943 /* Select buffer format and extract XML from CMS*/
8945 case RAWTYPE_DELIVERYINFO
:
8946 message_ns
= MESSAGE_NS_UNSIGNED
;
8947 xml_stream
= (void *) buffer
;
8948 xml_stream_length
= length
;
8951 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
8952 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
8953 xml_stream
= (void *) buffer
;
8954 xml_stream_length
= length
;
8957 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
8958 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
8959 err
= _isds_extract_cms_data(context
, buffer
, length
,
8960 &xml_stream
, &xml_stream_length
);
8961 if (err
) goto leave
;
8965 isds_log_message(context
, _("Bad raw delivery representation type"));
8970 isds_log(ILF_ISDS
, ILL_DEBUG
,
8971 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8972 xml_stream_length
, xml_stream
);
8974 /* Convert delivery info XML stream into XPath context */
8975 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
8980 xpath_ctx
= xmlXPathNewContext(message_doc
);
8985 /* XXX: Name spaces mangled for signed delivery info:
8986 * http://isds.czechpoint.cz/v20/delivery:
8988 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8990 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8991 * <p:dmID>170272</p:dmID>
8994 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8996 * </q:dmEvents>...</q:dmEvents>
8998 * </q:GetDeliveryInfoResponse>
9000 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9004 result
= xmlXPathEvalExpression(
9005 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9011 /* Empty delivery info */
9012 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9013 isds_printf_message(context
,
9014 _("XML document is not sisds:dmDelivery document"));
9018 /* More delivery info's */
9019 if (result
->nodesetval
->nodeNr
> 1) {
9020 isds_printf_message(context
,
9021 _("XML document has more sisds:dmDelivery elements"));
9025 /* One delivery info */
9026 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9028 /* Extract the envelope (= message without documents, hence 0).
9029 * XXX: extract_TReturnedMessage() can obtain attachments size,
9030 * but delivery info carries none. It's coded as option elements,
9031 * so it should work. */
9032 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9033 if (err
) goto leave
;
9035 /* Extract events */
9036 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
9037 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
9038 if (err
) { err
= IE_ERROR
; goto leave
; }
9039 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
9040 if (err
) goto leave
;
9042 /* Append raw CMS structure into message */
9043 (*message
)->raw_type
= raw_type
;
9045 case BUFFER_DONT_STORE
:
9048 (*message
)->raw
= malloc(length
);
9049 if (!(*message
)->raw
) {
9053 memcpy((*message
)->raw
, buffer
, length
);
9054 (*message
)->raw_length
= length
;
9057 (*message
)->raw
= (void *) buffer
;
9058 (*message
)->raw_length
= length
;
9067 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9068 isds_message_free(message
);
9071 xmlXPathFreeObject(result
);
9072 xmlXPathFreeContext(xpath_ctx
);
9073 if (!*message
|| !(*message
)->xml
) {
9074 xmlFreeDoc(message_doc
);
9076 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9079 isds_log(ILF_ISDS
, ILL_DEBUG
,
9080 _("Delivery info loaded successfully.\n"));
9085 /* Download signed delivery info-sheet of given message identified by ID.
9086 * @context is session context
9087 * @message_id is message identifier (you can get them from
9088 * isds_get_list_of_{sent,received}_messages())
9089 * @message is automatically reallocated message retrieved from ISDS.
9090 * It will miss documents per se. Use isds_get_signed_received_message(),
9091 * if you are interested in documents (content). OTOH, only this function
9092 * can get list events message has gone through. */
9093 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
9094 const char *message_id
, struct isds_message
**message
) {
9096 isds_error err
= IE_SUCCESS
;
9098 xmlDocPtr response
= NULL
;
9099 xmlChar
*code
= NULL
, *status_message
= NULL
;
9101 size_t raw_length
= 0;
9104 if (!context
) return IE_INVALID_CONTEXT
;
9105 zfree(context
->long_message
);
9107 /* Free former message if any */
9108 if (!message
) return IE_INVAL
;
9109 isds_message_free(message
);
9112 /* Do request and check for success */
9113 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9114 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
9115 &response
, NULL
, NULL
, &code
, &status_message
);
9116 if (err
) goto leave
;
9118 /* Find signed delivery info, extract it into raw and maybe free
9120 err
= find_extract_signed_data_free_response(context
,
9121 (xmlChar
*)message_id
, &response
,
9122 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
9123 if (err
) goto leave
;
9125 /* Parse delivery info */
9126 err
= isds_load_delivery_info(context
,
9127 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
9128 message
, BUFFER_MOVE
);
9129 if (err
) goto leave
;
9135 isds_message_free(message
);
9140 free(status_message
);
9141 xmlFreeDoc(response
);
9144 isds_log(ILF_ISDS
, ILL_DEBUG
,
9145 _("GetSignedDeliveryInfo request processed by server "
9148 #else /* not HAVE_LIBCURL */
9155 /* Download delivery info-sheet of given message identified by ID.
9156 * @context is session context
9157 * @message_id is message identifier (you can get them from
9158 * isds_get_list_of_{sent,received}_messages())
9159 * @message is automatically reallocated message retrieved from ISDS.
9160 * It will miss documents per se. Use isds_get_received_message(), if you are
9161 * interested in documents (content). OTOH, only this function can get list
9162 * of events message has gone through. */
9163 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
9164 const char *message_id
, struct isds_message
**message
) {
9166 isds_error err
= IE_SUCCESS
;
9168 xmlDocPtr response
= NULL
;
9169 xmlChar
*code
= NULL
, *status_message
= NULL
;
9170 xmlNodePtr delivery_node
= NULL
;
9172 size_t raw_length
= 0;
9175 if (!context
) return IE_INVALID_CONTEXT
;
9176 zfree(context
->long_message
);
9178 /* Free former message if any */
9179 if (!message
) return IE_INVAL
;
9180 isds_message_free(message
);
9183 /* Do request and check for success */
9184 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9185 BAD_CAST
"GetDeliveryInfo", message_id
,
9186 &response
, NULL
, NULL
, &code
, &status_message
);
9187 if (err
) goto leave
;
9190 /* Serialize delivery info */
9191 delivery_node
= xmlDocGetRootElement(response
);
9192 if (!delivery_node
) {
9193 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9194 isds_printf_message(context
,
9195 _("Server did not return any delivery info for ID `%s' "
9196 "on GetDeliveryInfo request"), message_id_locale
);
9197 free(message_id_locale
);
9201 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
9202 if (err
) goto leave
;
9204 /* Parse delivery info */
9205 /* TODO: Here we parse the response second time. We could single delivery
9206 * parser from isds_load_delivery_info() to make things faster. */
9207 err
= isds_load_delivery_info(context
,
9208 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
9209 message
, BUFFER_MOVE
);
9210 if (err
) goto leave
;
9217 isds_message_free(message
);
9222 free(status_message
);
9223 xmlFreeDoc(response
);
9226 isds_log(ILF_ISDS
, ILL_DEBUG
,
9227 _("GetDeliveryInfo request processed by server "
9230 #else /* not HAVE_LIBCURL */
9237 /* Download incoming message identified by ID.
9238 * @context is session context
9239 * @message_id is message identifier (you can get them from
9240 * isds_get_list_of_received_messages())
9241 * @message is automatically reallocated message retrieved from ISDS */
9242 isds_error
isds_get_received_message(struct isds_ctx
*context
,
9243 const char *message_id
, struct isds_message
**message
) {
9245 isds_error err
= IE_SUCCESS
;
9247 xmlDocPtr response
= NULL
;
9248 void *xml_stream
= NULL
;
9249 size_t xml_stream_length
;
9250 xmlChar
*code
= NULL
, *status_message
= NULL
;
9251 xmlXPathContextPtr xpath_ctx
= NULL
;
9252 xmlXPathObjectPtr result
= NULL
;
9253 char *phys_path
= NULL
;
9254 size_t phys_start
, phys_end
;
9257 if (!context
) return IE_INVALID_CONTEXT
;
9258 zfree(context
->long_message
);
9260 /* Free former message if any */
9261 if (message
) isds_message_free(message
);
9264 /* Do request and check for success */
9265 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9266 BAD_CAST
"MessageDownload", message_id
,
9267 &response
, &xml_stream
, &xml_stream_length
,
9268 &code
, &status_message
);
9269 if (err
) goto leave
;
9272 xpath_ctx
= xmlXPathNewContext(response
);
9277 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9281 result
= xmlXPathEvalExpression(
9282 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9288 /* Empty response */
9289 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9290 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9291 isds_printf_message(context
,
9292 _("Server did not return any message for ID `%s' "
9293 "on MessageDownload request"), message_id_locale
);
9294 free(message_id_locale
);
9299 if (result
->nodesetval
->nodeNr
> 1) {
9300 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9301 isds_printf_message(context
,
9302 _("Server did return more messages for ID `%s' "
9303 "on MessageDownload request"), message_id_locale
);
9304 free(message_id_locale
);
9309 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9311 /* Extract the message */
9312 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9313 if (err
) goto leave
;
9315 /* Locate raw XML blob */
9317 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
9318 PHYSXML_ELEMENT_SEPARATOR
9319 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
9320 PHYSXML_ELEMENT_SEPARATOR
9321 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
9327 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
9328 phys_path
, &phys_start
, &phys_end
);
9331 isds_log_message(context
,
9332 _("Substring with isds:MessageDownloadResponse element "
9333 "could not be located in raw SOAP message"));
9337 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9338 &(*message)->raw_length);*/
9339 /* TODO: Store name space declarations from ancestors */
9340 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9341 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9342 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
9343 (*message
)->raw
= malloc((*message
)->raw_length
);
9344 if (!(*message
)->raw
) {
9348 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
9353 isds_message_free(message
);
9358 xmlXPathFreeObject(result
);
9359 xmlXPathFreeContext(xpath_ctx
);
9362 free(status_message
);
9364 if (!*message
|| !(*message
)->xml
) {
9365 xmlFreeDoc(response
);
9369 isds_log(ILF_ISDS
, ILL_DEBUG
,
9370 _("MessageDownload request processed by server "
9373 #else /* not HAVE_LIBCURL */
9380 /* Load message of any type from buffer.
9381 * @context is session context
9382 * @raw_type defines content type of @buffer. Only message types are allowed.
9383 * @buffer is message raw representation. Format (CMS, plain signed,
9384 * message direction) is defined in @raw_type. You can retrieve such data
9385 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9386 * @length is length of buffer in bytes.
9387 * @message is automatically reallocated message parsed from @buffer.
9388 * @strategy selects how buffer will be attached into raw isds_message member.
9390 isds_error
isds_load_message(struct isds_ctx
*context
,
9391 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
9392 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9394 isds_error err
= IE_SUCCESS
;
9395 void *xml_stream
= NULL
;
9396 size_t xml_stream_length
= 0;
9397 message_ns_type message_ns
;
9398 xmlDocPtr message_doc
= NULL
;
9399 xmlXPathContextPtr xpath_ctx
= NULL
;
9400 xmlXPathObjectPtr result
= NULL
;
9402 if (!context
) return IE_INVALID_CONTEXT
;
9403 zfree(context
->long_message
);
9404 if (!message
) return IE_INVAL
;
9405 isds_message_free(message
);
9406 if (!buffer
) return IE_INVAL
;
9409 /* Select buffer format and extract XML from CMS*/
9411 case RAWTYPE_INCOMING_MESSAGE
:
9412 message_ns
= MESSAGE_NS_UNSIGNED
;
9413 xml_stream
= (void *) buffer
;
9414 xml_stream_length
= length
;
9417 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
9418 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9419 xml_stream
= (void *) buffer
;
9420 xml_stream_length
= length
;
9423 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
9424 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9425 err
= _isds_extract_cms_data(context
, buffer
, length
,
9426 &xml_stream
, &xml_stream_length
);
9427 if (err
) goto leave
;
9430 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
9431 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9432 xml_stream
= (void *) buffer
;
9433 xml_stream_length
= length
;
9436 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9437 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9438 err
= _isds_extract_cms_data(context
, buffer
, length
,
9439 &xml_stream
, &xml_stream_length
);
9440 if (err
) goto leave
;
9444 isds_log_message(context
, _("Bad raw message representation type"));
9449 isds_log(ILF_ISDS
, ILL_DEBUG
,
9450 _("Loading message:\n%.*s\nEnd of message\n"),
9451 xml_stream_length
, xml_stream
);
9453 /* Convert messages XML stream into XPath context */
9454 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9459 xpath_ctx
= xmlXPathNewContext(message_doc
);
9464 /* XXX: Standard name space for unsigned incoming direction:
9465 * http://isds.czechpoint.cz/v20/
9467 * XXX: Name spaces mangled for signed outgoing direction:
9468 * http://isds.czechpoint.cz/v20/SentMessage:
9470 * <q:MessageDownloadResponse
9471 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9472 * <q:dmReturnedMessage>
9473 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9474 * <p:dmID>151916</p:dmID>
9477 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9479 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9480 * </q:dmReturnedMessage>
9481 * </q:MessageDownloadResponse>
9483 * XXX: Name spaces mangled for signed incoming direction:
9484 * http://isds.czechpoint.cz/v20/message:
9486 * <q:MessageDownloadResponse
9487 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9488 * <q:dmReturnedMessage>
9489 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9490 * <p:dmID>151916</p:dmID>
9493 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9495 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9496 * </q:dmReturnedMessage>
9497 * </q:MessageDownloadResponse>
9499 * Stupidity of ISDS developers is unlimited */
9500 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9504 result
= xmlXPathEvalExpression(
9505 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9512 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9513 isds_printf_message(context
,
9514 _("XML document does not contain "
9515 "sisds:dmReturnedMessage element"));
9520 if (result
->nodesetval
->nodeNr
> 1) {
9521 isds_printf_message(context
,
9522 _("XML document has more sisds:dmReturnedMessage elements"));
9527 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9529 /* Extract the message */
9530 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9531 if (err
) goto leave
;
9533 /* Append raw buffer into message */
9534 (*message
)->raw_type
= raw_type
;
9536 case BUFFER_DONT_STORE
:
9539 (*message
)->raw
= malloc(length
);
9540 if (!(*message
)->raw
) {
9544 memcpy((*message
)->raw
, buffer
, length
);
9545 (*message
)->raw_length
= length
;
9548 (*message
)->raw
= (void *) buffer
;
9549 (*message
)->raw_length
= length
;
9559 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9560 isds_message_free(message
);
9563 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9564 xmlXPathFreeObject(result
);
9565 xmlXPathFreeContext(xpath_ctx
);
9566 if (!*message
|| !(*message
)->xml
) {
9567 xmlFreeDoc(message_doc
);
9571 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
9576 /* Determine type of raw message or delivery info according some heuristics.
9577 * It does not validate the raw blob.
9578 * @context is session context
9579 * @raw_type returns content type of @buffer. Valid only if exit code of this
9580 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9581 * reallocated memory.
9582 * @buffer is message raw representation.
9583 * @length is length of buffer in bytes. */
9584 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
9585 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
9587 void *xml_stream
= NULL
;
9588 size_t xml_stream_length
= 0;
9589 xmlDocPtr document
= NULL
;
9590 xmlNodePtr root
= NULL
;
9592 if (!context
) return IE_INVALID_CONTEXT
;
9593 zfree(context
->long_message
);
9594 if (length
== 0 || !buffer
) return IE_INVAL
;
9595 if (!raw_type
) return IE_INVAL
;
9598 err
= _isds_extract_cms_data(context
, buffer
, length
,
9599 &xml_stream
, &xml_stream_length
);
9601 xml_stream
= (void *) buffer
;
9602 xml_stream_length
= (size_t) length
;
9607 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
9609 isds_printf_message(context
,
9610 _("Could not parse data as XML document"));
9615 /* Get root element */
9616 root
= xmlDocGetRootElement(document
);
9618 isds_printf_message(context
,
9619 _("XML document is missing root element"));
9624 if (!root
->ns
|| !root
->ns
->href
) {
9625 isds_printf_message(context
,
9626 _("Root element does not belong to any name space"));
9631 /* Test name space */
9632 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
9633 if (xml_stream
== buffer
)
9634 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
9636 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
9637 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
9638 if (xml_stream
== buffer
)
9639 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
9641 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
9642 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
9643 if (xml_stream
== buffer
)
9644 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
9646 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
9647 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
9648 if (xml_stream
!= buffer
) {
9649 isds_printf_message(context
,
9650 _("Document in ISDS name space is encapsulated into CMS" ));
9652 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
9653 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9654 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
9655 *raw_type
= RAWTYPE_DELIVERYINFO
;
9657 isds_printf_message(context
,
9658 _("Unknown root element in ISDS name space"));
9662 isds_printf_message(context
,
9663 _("Unknown name space"));
9668 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9669 xmlFreeDoc(document
);
9674 /* Download signed incoming/outgoing message identified by ID.
9675 * @context is session context
9676 * @output is true for outgoing message, false for incoming message
9677 * @message_id is message identifier (you can get them from
9678 * isds_get_list_of_{sent,received}_messages())
9679 * @message is automatically reallocated message retrieved from ISDS. The raw
9680 * member will be filled with PKCS#7 structure in DER format. */
9681 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
9682 const _Bool outgoing
, const char *message_id
,
9683 struct isds_message
**message
) {
9685 isds_error err
= IE_SUCCESS
;
9687 xmlDocPtr response
= NULL
;
9688 xmlChar
*code
= NULL
, *status_message
= NULL
;
9689 xmlXPathContextPtr xpath_ctx
= NULL
;
9690 xmlXPathObjectPtr result
= NULL
;
9691 char *encoded_structure
= NULL
;
9693 size_t raw_length
= 0;
9696 if (!context
) return IE_INVALID_CONTEXT
;
9697 zfree(context
->long_message
);
9698 if (!message
) return IE_INVAL
;
9699 isds_message_free(message
);
9702 /* Do request and check for success */
9703 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9704 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
9705 BAD_CAST
"SignedMessageDownload",
9706 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
9707 if (err
) goto leave
;
9709 /* Find signed message, extract it into raw and maybe free
9711 err
= find_extract_signed_data_free_response(context
,
9712 (xmlChar
*)message_id
, &response
,
9713 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
9714 BAD_CAST
"SignedMessageDownload",
9716 if (err
) goto leave
;
9719 err
= isds_load_message(context
,
9720 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9721 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
9722 raw
, raw_length
, message
, BUFFER_MOVE
);
9723 if (err
) goto leave
;
9729 isds_message_free(message
);
9732 free(encoded_structure
);
9733 xmlXPathFreeObject(result
);
9734 xmlXPathFreeContext(xpath_ctx
);
9738 free(status_message
);
9739 xmlFreeDoc(response
);
9742 isds_log(ILF_ISDS
, ILL_DEBUG
,
9744 _("SignedSentMessageDownload request processed by server "
9745 "successfully.\n") :
9746 _("SignedMessageDownload request processed by server "
9749 #else /* not HAVE_LIBCURL */
9756 /* Download signed incoming message identified by ID.
9757 * @context is session context
9758 * @message_id is message identifier (you can get them from
9759 * isds_get_list_of_received_messages())
9760 * @message is automatically reallocated message retrieved from ISDS. The raw
9761 * member will be filled with PKCS#7 structure in DER format. */
9762 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
9763 const char *message_id
, struct isds_message
**message
) {
9764 return isds_get_signed_message(context
, 0, message_id
, message
);
9768 /* Download signed outgoing message identified by ID.
9769 * @context is session context
9770 * @message_id is message identifier (you can get them from
9771 * isds_get_list_of_sent_messages())
9772 * @message is automatically reallocated message retrieved from ISDS. The raw
9773 * member will be filled with PKCS#7 structure in DER format. */
9774 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
9775 const char *message_id
, struct isds_message
**message
) {
9776 return isds_get_signed_message(context
, 1, message_id
, message
);
9780 /* Get type and name of user who sent a message identified by ID.
9781 * @context is session context
9782 * @message_id is message identifier
9783 * @sender_type is pointer to automatically allocated type of sender detected
9784 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9785 * library or to the server, NULL will be returned. Pass NULL if you don't
9787 * @raw_sender_type is automatically reallocated UTF-8 string describing
9788 * sender type or NULL if not known to server. Pass NULL if you don't care.
9789 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9790 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9791 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
9792 const char *message_id
, isds_sender_type
**sender_type
,
9793 char **raw_sender_type
, char **sender_name
) {
9794 isds_error err
= IE_SUCCESS
;
9796 xmlDocPtr response
= NULL
;
9797 xmlChar
*code
= NULL
, *status_message
= NULL
;
9798 xmlXPathContextPtr xpath_ctx
= NULL
;
9799 xmlXPathObjectPtr result
= NULL
;
9800 char *type_string
= NULL
;
9803 if (!context
) return IE_INVALID_CONTEXT
;
9804 zfree(context
->long_message
);
9805 if (sender_type
) zfree(*sender_type
);
9806 if (raw_sender_type
) zfree(*raw_sender_type
);
9807 if (sender_name
) zfree(*sender_name
);
9808 if (!message_id
) return IE_INVAL
;
9811 /* Do request and check for success */
9812 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9813 BAD_CAST
"GetMessageAuthor",
9814 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
9815 if (err
) goto leave
;
9818 xpath_ctx
= xmlXPathNewContext(response
);
9823 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9827 result
= xmlXPathEvalExpression(
9828 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
9833 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9834 isds_log_message(context
,
9835 _("Missing GetMessageAuthorResponse element"));
9839 if (result
->nodesetval
->nodeNr
> 1) {
9840 isds_log_message(context
,
9841 _("Multiple GetMessageAuthorResponse element"));
9845 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9846 xmlXPathFreeObject(result
); result
= NULL
;
9848 /* Fill output arguments in */
9849 EXTRACT_STRING("isds:userType", type_string
);
9851 *sender_type
= calloc(1, sizeof(**sender_type
));
9852 if (!*sender_type
) {
9858 err
= string2isds_sender_type((xmlChar
*)type_string
,
9861 zfree(*sender_type
);
9862 if (err
== IE_ENUM
) {
9864 char *type_string_locale
= _isds_utf82locale(type_string
);
9865 isds_log(ILF_ISDS
, ILL_WARNING
,
9866 _("Unknown isds:userType value: %s"),
9867 type_string_locale
);
9868 free(type_string_locale
);
9874 EXTRACT_STRING("isds:authorName", *sender_name
);
9878 if (sender_type
) zfree(*sender_type
);
9880 if (sender_name
) zfree(*sender_name
);
9882 if (raw_sender_type
) *raw_sender_type
= type_string
;
9884 xmlXPathFreeObject(result
);
9885 xmlXPathFreeContext(xpath_ctx
);
9888 free(status_message
);
9889 xmlFreeDoc(response
);
9892 isds_log(ILF_ISDS
, ILL_DEBUG
,
9893 _("GetMessageAuthor request processed by server "
9894 "successfully.\n"));
9895 #else /* not HAVE_LIBCURL */
9902 /* Retrieve hash of message identified by ID stored in ISDS.
9903 * @context is session context
9904 * @message_id is message identifier
9905 * @hash is automatically reallocated message hash downloaded from ISDS.
9906 * Message must exist in system and must not be deleted. */
9907 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
9908 const char *message_id
, struct isds_hash
**hash
) {
9910 isds_error err
= IE_SUCCESS
;
9912 xmlDocPtr response
= NULL
;
9913 xmlChar
*code
= NULL
, *status_message
= NULL
;
9914 xmlXPathContextPtr xpath_ctx
= NULL
;
9915 xmlXPathObjectPtr result
= NULL
;
9918 if (!context
) return IE_INVALID_CONTEXT
;
9919 zfree(context
->long_message
);
9921 isds_hash_free(hash
);
9924 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9925 BAD_CAST
"VerifyMessage", message_id
,
9926 &response
, NULL
, NULL
, &code
, &status_message
);
9927 if (err
) goto leave
;
9931 xpath_ctx
= xmlXPathNewContext(response
);
9936 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9940 result
= xmlXPathEvalExpression(
9941 BAD_CAST
"/isds:VerifyMessageResponse",
9947 /* Empty response */
9948 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9949 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9950 isds_printf_message(context
,
9951 _("Server did not return any response for ID `%s' "
9952 "on VerifyMessage request"), message_id_locale
);
9953 free(message_id_locale
);
9957 /* More responses */
9958 if (result
->nodesetval
->nodeNr
> 1) {
9959 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9960 isds_printf_message(context
,
9961 _("Server did return more responses for ID `%s' "
9962 "on VerifyMessage request"), message_id_locale
);
9963 free(message_id_locale
);
9968 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9970 /* Extract the hash */
9971 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
9975 isds_hash_free(hash
);
9978 xmlXPathFreeObject(result
);
9979 xmlXPathFreeContext(xpath_ctx
);
9982 free(status_message
);
9983 xmlFreeDoc(response
);
9986 isds_log(ILF_ISDS
, ILL_DEBUG
,
9987 _("VerifyMessage request processed by server "
9990 #else /* not HAVE_LIBCURL */
9997 /* Mark message as read. This is a transactional commit function to acknowledge
9998 * to ISDS the message has been downloaded and processed by client properly.
9999 * @context is session context
10000 * @message_id is message identifier. */
10001 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
10002 const char *message_id
) {
10004 isds_error err
= IE_SUCCESS
;
10006 xmlDocPtr response
= NULL
;
10007 xmlChar
*code
= NULL
, *status_message
= NULL
;
10010 if (!context
) return IE_INVALID_CONTEXT
;
10011 zfree(context
->long_message
);
10014 /* Do request and check for success */
10015 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10016 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
10017 &response
, NULL
, NULL
, &code
, &status_message
);
10020 free(status_message
);
10021 xmlFreeDoc(response
);
10024 isds_log(ILF_ISDS
, ILL_DEBUG
,
10025 _("MarkMessageAsDownloaded request processed by server "
10028 #else /* not HAVE_LIBCURL */
10035 /* Mark message as received by recipient. This is applicable only to
10036 * commercial message. Use envelope->dmType message member to distinguish
10037 * commercial message from government message. Government message is
10038 * received automatically (by law), commercial message on recipient request.
10039 * @context is session context
10040 * @message_id is message identifier. */
10041 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
10042 const char *message_id
) {
10044 isds_error err
= IE_SUCCESS
;
10046 xmlDocPtr response
= NULL
;
10047 xmlChar
*code
= NULL
, *status_message
= NULL
;
10050 if (!context
) return IE_INVALID_CONTEXT
;
10051 zfree(context
->long_message
);
10054 /* Do request and check for success */
10055 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10056 BAD_CAST
"ConfirmDelivery", message_id
,
10057 &response
, NULL
, NULL
, &code
, &status_message
);
10060 free(status_message
);
10061 xmlFreeDoc(response
);
10064 isds_log(ILF_ISDS
, ILL_DEBUG
,
10065 _("ConfirmDelivery request processed by server "
10068 #else /* not HAVE_LIBCURL */
10075 /* Send document for authorized conversion into Czech POINT system.
10076 * This is public anonymous service, no log-in necessary. Special context is
10077 * used to reuse keep-a-live HTTPS connection.
10078 * @context is Czech POINT session context. DO NOT use context connected to
10079 * ISDS server. Use new context or context used by this function previously.
10080 * @document is document to convert. Only data, data_length, dmFileDescr and
10081 * is_xml members are significant. Be ware that not all document formats can be
10082 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10083 * @id is reallocated identifier assigned by Czech POINT system to
10084 * your document on submit. Use is to tell it to Czech POINT officer.
10085 * @date is reallocated document submit date (submitted documents
10086 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10088 isds_error
czp_convert_document(struct isds_ctx
*context
,
10089 const struct isds_document
*document
,
10090 char **id
, struct tm
**date
) {
10091 isds_error err
= IE_SUCCESS
;
10093 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
10094 xmlNodePtr request
= NULL
, node
;
10095 xmlDocPtr response
= NULL
;
10097 xmlXPathContextPtr xpath_ctx
= NULL
;
10098 xmlXPathObjectPtr result
= NULL
;
10099 long int status
= -1;
10100 long int *status_ptr
= &status
;
10101 char *string
= NULL
;
10105 if (!context
) return IE_INVALID_CONTEXT
;
10106 zfree(context
->long_message
);
10107 if (!document
|| !id
|| !date
) return IE_INVAL
;
10109 if (document
->is_xml
) {
10110 isds_log_message(context
,
10111 _("XML documents cannot be submitted to conversion"));
10115 /* Free output arguments */
10120 /* Store configuration */
10121 context
->type
= CTX_TYPE_CZP
;
10122 free(context
->url
);
10123 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
10124 if (!(context
->url
))
10127 /* Prepare CURL handle if not yet connected */
10128 if (!context
->curl
) {
10129 context
->curl
= curl_easy_init();
10130 if (!(context
->curl
))
10134 /* Build conversion request */
10135 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
10137 isds_log_message(context
,
10138 _("Could not build Czech POINT conversion request"));
10141 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
10143 isds_log_message(context
,
10144 _("Could not create Czech POINT deposit name space"));
10145 xmlFreeNode(request
);
10148 xmlSetNs(request
, deposit_ns
);
10150 /* Insert children. They are in empty namespace! */
10151 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
10153 isds_log_message(context
, _("Could not create empty name space"));
10157 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
10158 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
10159 document
->dmFileDescr
);
10161 /* Document encoded in Base64 */
10162 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
10163 document
->data
, document
->data_length
);
10164 if (err
) goto leave
;
10166 isds_log(ILF_ISDS
, ILL_DEBUG
,
10167 _("Submitting document for conversion into Czech POINT deposit"));
10169 /* Send conversion request */
10170 err
= _czp_czpdeposit(context
, request
, &response
);
10171 xmlFreeNode(request
); request
= NULL
;
10174 czp_do_close_connection(context
);
10179 /* Extract response */
10180 xpath_ctx
= xmlXPathNewContext(response
);
10185 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10189 result
= xmlXPathEvalExpression(
10190 BAD_CAST
"/deposit:saveDocumentResponse/return",
10196 /* Empty response */
10197 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10198 isds_printf_message(context
,
10199 _("Missing `return' element in Czech POINT deposit response"));
10203 /* More responses */
10204 if (result
->nodesetval
->nodeNr
> 1) {
10205 isds_printf_message(context
,
10206 _("Multiple `return' element in Czech POINT deposit response"));
10211 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10214 EXTRACT_LONGINT("status", status_ptr
, 1);
10216 EXTRACT_STRING("statusMsg", string
);
10217 char *string_locale
= _isds_utf82locale(string
);
10218 isds_printf_message(context
,
10219 _("Czech POINT deposit refused document for conversion "
10220 "(code=%ld, message=%s)"),
10221 status
, string_locale
);
10222 free(string_locale
);
10227 /* Get document ID */
10228 EXTRACT_STRING("documentID", *id
);
10230 /* Get submit date */
10231 EXTRACT_STRING("dateInserted", string
);
10233 *date
= calloc(1, sizeof(**date
));
10238 err
= datestring2tm((xmlChar
*)string
, *date
);
10240 if (err
== IE_NOTSUP
) {
10242 char *string_locale
= _isds_utf82locale(string
);
10243 isds_printf_message(context
,
10244 _("Invalid dateInserted value: %s"), string_locale
);
10245 free(string_locale
);
10253 xmlXPathFreeObject(result
);
10254 xmlXPathFreeContext(xpath_ctx
);
10256 xmlFreeDoc(response
);
10257 xmlFreeNode(request
);
10260 char *id_locale
= _isds_utf82locale((char *) *id
);
10261 isds_log(ILF_ISDS
, ILL_DEBUG
,
10262 _("Document %s has been submitted for conversion "
10263 "to server successfully\n"), id_locale
);
10266 #else /* not HAVE_LIBCURL */
10273 /* Close possibly opened connection to Czech POINT document deposit.
10274 * @context is Czech POINT session context. */
10275 isds_error
czp_close_connection(struct isds_ctx
*context
) {
10276 if (!context
) return IE_INVALID_CONTEXT
;
10277 zfree(context
->long_message
);
10279 return czp_do_close_connection(context
);
10286 /* Send request for new box creation in testing ISDS instance.
10287 * It's not possible to request for a production box currently, as it
10288 * communicates via e-mail.
10289 * XXX: This function does not work either. Server complains about invalid
10291 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10293 * @context is special session context for box creation request. DO NOT use
10294 * standard context as it could reveal your password. Use fresh new context or
10295 * context previously used by this function.
10296 * @box is box description to create including single primary user (in case of
10297 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10298 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10299 * box, or contact address of PFO box owner). The email member is mandatory as
10300 * it will be used to deliver credentials.
10301 * @former_names is former name of box owner. Pass NULL if you don't care.
10302 * @approval is optional external approval of box manipulation
10303 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10304 * NULL, if you don't care.*/
10305 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
10306 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
10307 const char *former_names
, const struct isds_approval
*approval
,
10308 char **refnumber
) {
10309 isds_error err
= IE_SUCCESS
;
10311 xmlNodePtr request
= NULL
;
10312 xmlDocPtr response
= NULL
;
10313 xmlXPathContextPtr xpath_ctx
= NULL
;
10314 xmlXPathObjectPtr result
= NULL
;
10318 if (!context
) return IE_INVALID_CONTEXT
;
10319 zfree(context
->long_message
);
10320 if (!box
) return IE_INVAL
;
10323 if (!box
->email
|| box
->email
[0] == '\0') {
10324 isds_log_message(context
, _("E-mail field is mandatory"));
10328 /* Scratch box ID */
10331 /* Store configuration */
10332 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
10333 free(context
->url
);
10334 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
10335 if (!(context
->url
))
10338 /* Prepare CURL handle if not yet connected */
10339 if (!context
->curl
) {
10340 context
->curl
= curl_easy_init();
10341 if (!(context
->curl
))
10345 /* Build CreateDataBox request */
10346 err
= build_CreateDBInput_request(context
,
10347 &request
, BAD_CAST
"CreateDataBox",
10348 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
10349 if (err
) goto leave
;
10351 /* Send it to server and process response */
10352 err
= send_destroy_request_check_response(context
,
10353 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
10354 &response
, (xmlChar
**) refnumber
);
10355 if (err
) goto leave
;
10357 /* Extract box ID */
10358 xpath_ctx
= xmlXPathNewContext(response
);
10363 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10367 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
10370 xmlXPathFreeObject(result
);
10371 xmlXPathFreeContext(xpath_ctx
);
10372 xmlFreeDoc(response
);
10373 xmlFreeNode(request
);
10376 isds_log(ILF_ISDS
, ILL_DEBUG
,
10377 _("CreateDataBox request processed by server successfully.\n"));
10379 #else /* not HAVE_LIBCURL */
10387 /* Submit CMS signed message to ISDS to verify its originality. This is
10388 * stronger form of isds_verify_message_hash() because ISDS does more checks
10389 * than simple one (potentialy old weak) hash comparison.
10390 * @context is session context
10391 * @message is memory with raw CMS signed message bit stream
10392 * @length is @message size in bytes
10394 * IE_SUCCESS if message originates in ISDS
10395 * IE_NOTEQUAL if message is unknown to ISDS
10396 * other code for other errors */
10397 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
10398 const void *message
, size_t length
) {
10399 isds_error err
= IE_SUCCESS
;
10401 xmlNsPtr isds_ns
= NULL
;
10402 xmlNodePtr request
= NULL
;
10403 xmlDocPtr response
= NULL
;
10404 xmlXPathContextPtr xpath_ctx
= NULL
;
10405 xmlXPathObjectPtr result
= NULL
;
10406 _Bool
*authentic
= NULL
;
10409 if (!context
) return IE_INVALID_CONTEXT
;
10410 zfree(context
->long_message
);
10411 if (!message
|| length
== 0) return IE_INVAL
;
10414 /* Check if connection is established
10415 * TODO: This check should be done downstairs. */
10416 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10419 /* Build AuthenticateMessage request */
10420 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
10422 isds_log_message(context
,
10423 _("Could not build AuthenticateMessage request"));
10426 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10428 isds_log_message(context
, _("Could not create ISDS name space"));
10429 xmlFreeNode(request
);
10432 xmlSetNs(request
, isds_ns
);
10434 /* Insert Base64 encoded message */
10435 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
10437 if (err
) goto leave
;
10439 /* Send request to server and process response */
10440 err
= send_destroy_request_check_response(context
,
10441 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
10443 if (err
) goto leave
;
10446 /* ISDS has decided */
10447 xpath_ctx
= xmlXPathNewContext(response
);
10452 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10457 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
10460 isds_log_message(context
,
10461 _("Server did not return any response on "
10462 "AuthenticateMessage request"));
10467 isds_log(ILF_ISDS
, ILL_DEBUG
,
10468 _("ISDS authenticated the message successfully\n"));
10470 isds_log_message(context
, _("ISDS does not know the message"));
10477 xmlXPathFreeObject(result
);
10478 xmlXPathFreeContext(xpath_ctx
);
10480 xmlFreeDoc(response
);
10481 xmlFreeNode(request
);
10482 #else /* not HAVE_LIBCURL */
10489 #undef INSERT_ELEMENT
10490 #undef CHECK_FOR_STRING_LENGTH
10491 #undef INSERT_STRING_ATTRIBUTE
10492 #undef INSERT_ULONGINTNOPTR
10493 #undef INSERT_ULONGINT
10494 #undef INSERT_LONGINT
10495 #undef INSERT_BOOLEAN
10496 #undef INSERT_SCALAR_BOOLEAN
10497 #undef INSERT_STRING
10498 #undef INSERT_STRING_WITH_NS
10499 #undef EXTRACT_STRING_ATTRIBUTE
10500 #undef EXTRACT_ULONGINT
10501 #undef EXTRACT_LONGINT
10502 #undef EXTRACT_BOOLEAN
10503 #undef EXTRACT_STRING
10506 /* Compute hash of message from raw representation and store it into envelope.
10507 * Original hash structure will be destroyed in envelope.
10508 * @context is session context
10509 * @message is message carrying raw XML message blob
10510 * @algorithm is desired hash algorithm to use */
10511 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
10512 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
10513 isds_error err
= IE_SUCCESS
;
10515 void *xml_stream
= NULL
;
10516 size_t xml_stream_length
;
10517 size_t phys_start
, phys_end
;
10518 char *phys_path
= NULL
;
10519 struct isds_hash
*new_hash
= NULL
;
10522 if (!context
) return IE_INVALID_CONTEXT
;
10523 zfree(context
->long_message
);
10524 if (!message
) return IE_INVAL
;
10526 if (!message
->raw
) {
10527 isds_log_message(context
,
10528 _("Message does not carry raw representation"));
10532 switch (message
->raw_type
) {
10533 case RAWTYPE_INCOMING_MESSAGE
:
10535 xml_stream
= message
->raw
;
10536 xml_stream_length
= message
->raw_length
;
10539 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10540 nsuri
= SISDS_INCOMING_NS
;
10541 xml_stream
= message
->raw
;
10542 xml_stream_length
= message
->raw_length
;
10545 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10546 nsuri
= SISDS_INCOMING_NS
;
10547 err
= _isds_extract_cms_data(context
,
10548 message
->raw
, message
->raw_length
,
10549 &xml_stream
, &xml_stream_length
);
10550 if (err
) goto leave
;
10553 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10554 nsuri
= SISDS_OUTGOING_NS
;
10555 xml_stream
= message
->raw
;
10556 xml_stream_length
= message
->raw_length
;
10559 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10560 nsuri
= SISDS_OUTGOING_NS
;
10561 err
= _isds_extract_cms_data(context
,
10562 message
->raw
, message
->raw_length
,
10563 &xml_stream
, &xml_stream_length
);
10564 if (err
) goto leave
;
10568 isds_log_message(context
, _("Bad raw representation type"));
10574 /* XXX: Hash is computed from original string representing isds:dmDm
10575 * subtree. That means no encoding, white space, xmlns attributes changes.
10576 * In other words, input for hash can be invalid XML stream. */
10577 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
10578 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10579 PHYSXML_ELEMENT_SEPARATOR
,
10580 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
10581 PHYSXML_ELEMENT_SEPARATOR
10582 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
10586 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10587 phys_path
, &phys_start
, &phys_end
);
10590 isds_log_message(context
,
10591 _("Substring with isds:dmDM element could not be located "
10592 "in raw message"));
10598 new_hash
= calloc(1, sizeof(*new_hash
));
10603 new_hash
->algorithm
= algorithm
;
10604 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
10607 isds_log_message(context
, _("Could not compute message hash"));
10611 /* Save computed hash */
10612 if (!message
->envelope
) {
10613 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
10614 if (!message
->envelope
) {
10619 isds_hash_free(&message
->envelope
->hash
);
10620 message
->envelope
->hash
= new_hash
;
10624 isds_hash_free(&new_hash
);
10628 if (xml_stream
!= message
->raw
) free(xml_stream
);
10633 /* Compare two hashes.
10634 * @h1 is first hash
10635 * @h2 is another hash
10637 * IE_SUCCESS if hashes equal
10638 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10639 * IE_ENUM if not comparable, but both structures defined
10640 * IE_INVAL if some of the structures are undefined (NULL)
10641 * IE_ERROR if internal error occurs */
10642 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
10643 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
10644 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
10645 if (h1
->length
!= h2
->length
) return IE_ERROR
;
10646 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
10647 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
10649 for (int i
= 0; i
< h1
->length
; i
++) {
10650 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
10651 return IE_NOTEQUAL
;
10657 /* Check message has gone through ISDS by comparing message hash stored in
10658 * ISDS and locally computed hash. You must provide message with valid raw
10659 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10660 * This is convenient wrapper for isds_download_message_hash(),
10661 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10662 * @context is session context
10663 * @message is message with valid raw and envelope member; envelope->hash
10664 * member will be changed during function run. Use envelope on heap only.
10666 * IE_SUCCESS if message originates in ISDS
10667 * IE_NOTEQUAL if message is unknown to ISDS
10668 * other code for other errors */
10669 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
10670 struct isds_message
*message
) {
10671 isds_error err
= IE_SUCCESS
;
10672 struct isds_hash
*downloaded_hash
= NULL
;
10674 if (!context
) return IE_INVALID_CONTEXT
;
10675 zfree(context
->long_message
);
10676 if (!message
) return IE_INVAL
;
10678 if (!message
->envelope
) {
10679 isds_log_message(context
,
10680 _("Given message structure is missing envelope"));
10683 if (!message
->raw
) {
10684 isds_log_message(context
,
10685 _("Given message structure is missing raw representation"));
10689 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
10691 if (err
) goto leave
;
10693 err
= isds_compute_message_hash(context
, message
,
10694 downloaded_hash
->algorithm
);
10695 if (err
) goto leave
;
10697 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
10700 isds_hash_free(&downloaded_hash
);
10705 /* Search for document by document ID in list of documents. IDs are compared
10707 * @documents is list of isds_documents
10708 * @id is document identifier
10709 * @return first matching document or NULL. */
10710 const struct isds_document
*isds_find_document_by_id(
10711 const struct isds_list
*documents
, const char *id
) {
10712 const struct isds_list
*item
;
10713 const struct isds_document
*document
;
10715 for (item
= documents
; item
; item
= item
->next
) {
10716 document
= (struct isds_document
*) item
->data
;
10717 if (!document
) continue;
10719 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
10727 /* Normalize @mime_type to be proper MIME type.
10728 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10729 * guess regular MIME type (e.g. "application/pdf").
10730 * @mime_type is UTF-8 encoded MIME type to fix
10731 * @return original @mime_type if no better interpretation exists, or array to
10732 * constant static UTF-8 encoded string with proper MIME type. */
10733 char *isds_normalize_mime_type(const char* mime_type
) {
10734 if (!mime_type
) return NULL
;
10736 for (int offset
= 0;
10737 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
10739 if (!xmlStrcmp((const xmlChar
*) mime_type
, extension_map_mime
[offset
]))
10740 return (char *) extension_map_mime
[offset
+ 1];
10743 return (char *) mime_type
;
10747 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10748 struct isds_message **message);
10749 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10750 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10751 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10752 struct isds_address **address);
10754 int isds_message_free(struct isds_message **message);
10755 int isds_address_free(struct isds_address **address);
10759 /* Makes known all relevant namespaces to given XPath context
10760 * @xpath_ctx is XPath context
10761 * @message_ns selects proper message name space. Unsigned and signed
10762 * messages and delivery info's differ in prefix and URI. */
10763 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
10764 const message_ns_type message_ns
) {
10765 const xmlChar
*message_namespace
= NULL
;
10767 if (!xpath_ctx
) return IE_ERROR
;
10769 switch(message_ns
) {
10771 message_namespace
= BAD_CAST ISDS1_NS
; break;
10772 case MESSAGE_NS_UNSIGNED
:
10773 message_namespace
= BAD_CAST ISDS_NS
; break;
10774 case MESSAGE_NS_SIGNED_INCOMING
:
10775 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
10776 case MESSAGE_NS_SIGNED_OUTGOING
:
10777 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
10778 case MESSAGE_NS_SIGNED_DELIVERY
:
10779 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
10784 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
10786 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
10788 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
10790 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
10792 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))