1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
12 /* Initialize ISDS library.
13 * Global function, must be called before other functions.
14 * If it failes you can not use ISDS library and must call isds_cleanup() to
15 * free partially inititialized global variables. */
16 isds_error
isds_init(void) {
17 /* NULL global variables */
20 log_facilities
= ILF_NONE
;
24 if (curl_global_init(CURL_GLOBAL_ALL
)) {
28 /* This can _exit() current program. Find not so assertive check. */
31 /* Allocate global variables */
32 if (!(xml_node
= xmlNewNode(NULL
, BAD_CAST
"global-element")))
34 if (!(soap_ns
= xmlNewNs(NULL
, BAD_CAST SOAP_NS
, BAD_CAST
"soap")))
36 if (!(isds_ns
= xmlNewNs(NULL
, BAD_CAST ISDS_NS
, BAD_CAST
"isds")))
43 /* Deinicialize ISDS library.
44 * Global function, must be called as last library function. */
45 isds_error
isds_cleanup(void) {
49 xmlFreeNode(xml_node
);
53 curl_global_cleanup();
59 /* Return text description of ISDS error */
60 char *isds_strerror(const isds_error error
) {
63 return(_("Success")); break;
65 return(_("Unspecified error")); break;
67 return(_("Not supported")); break;
69 return(_("Invalid value")); break;
70 case IE_INVALID_CONTEXT
:
71 return(_("Invalid context")); break;
72 case IE_NOT_LOGGED_IN
:
73 return(_("Not logged in")); break;
74 case IE_CONNECTION_CLOSED
:
75 return(_("Connection closed")); break;
77 return(_("Timed out")); break;
79 return(_("Not exist")); break;
81 return(_("Out of memory")); break;
83 return(_("Network problem")); break;
85 return(_("SOAP problem")); break;
87 return(_("XML problem")); break;
89 return(_("ISDS server problem")); break;
91 return(_("Unknown error"));
96 /* Create ISDS context.
97 * Each context can be used for different sessions to (possibly) differnet
98 * ISDS server with different credentials. */
99 struct isds_ctx
*isds_ctx_create(void) {
100 struct isds_ctx
*context
;
101 context
= malloc(sizeof(*context
));
102 if (context
) memset(context
, 0, sizeof(*context
));
107 /* Destroy ISDS context and free memmory.
108 * @context will be NULLed on success. */
109 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
110 if (!context
|| !*context
) {
111 return IE_INVALID_CONTEXT
;
114 /* Discard credentials */
115 isds_logout(*context
);
117 /* Free other structures */
118 free((*context
)->long_message
);
126 /* Return long message text produced by library fucntion, e.g. detailed error
127 * mesage. Returned pointer is only valid until new library function is
128 * called for the same context. Could be NULL, especially if NULL context is
130 char *isds_long_message(const struct isds_ctx
*context
) {
131 if (!context
) return NULL
;
132 return context
->long_message
;
136 /* Stores message into context' long_message buffer.
137 * Application can pick the message up using isds_long_message().
138 * NULL @message truncates the buffer but does not deallocate it. */
139 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
140 const char *message
) {
144 if (!context
) return IE_INVALID_CONTEXT
;
146 /* FIXME: Check for integer overflow */
147 length
= 1 + ((message
) ? strlen(message
) : 0);
148 buffer
= realloc(context
->long_message
, length
);
149 if (!buffer
) return IE_NOMEM
;
152 strcpy(buffer
, message
);
156 context
->long_message
= buffer
;
161 /* Appends message into context' long_message buffer.
162 * Application can pick the message up using isds_long_message().
163 * NULL message has void effect. */
164 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
165 const char *message
) {
167 size_t old_length
, length
;
169 if (!context
) return IE_INVALID_CONTEXT
;
170 if (!message
) return IE_SUCCESS
;
171 if (!context
->long_message
)
172 return isds_log_message(context
, message
);
174 old_length
= strlen(context
->long_message
);
175 /* FIXME: Check for integer overflow */
176 length
= 1 + old_length
+ strlen(message
);
177 buffer
= realloc(context
->long_message
, length
);
178 if (!buffer
) return IE_NOMEM
;
180 strcpy(buffer
+ old_length
, message
);
182 context
->long_message
= buffer
;
188 * @facilities is bitmask of isds_log_facility values,
189 * @level is verbosity level. */
190 void isds_set_logging(const unsigned int facilities
,
191 const isds_log_level level
) {
192 log_facilities
= facilities
;
197 /* Log @message in class @facility with log @level into global log. @message
198 * is printf(3) formating string, variadic arguments may be neccessary.
199 * For debugging purposes. */
200 _hidden isds_error
isds_log(const isds_log_facility facility
,
201 const isds_log_level level
, const char *message
, ...) {
204 if (level
> log_level
) return IE_SUCCESS
;
205 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
206 if (!message
) return IE_INVAL
;
208 /* TODO: Allow to register output function privided by application
209 * (e.g. fprintf to stderr or copy to text area GUI widget). */
211 va_start(ap
, message
);
212 vfprintf(stderr
, message
, ap
);
214 /* Line buffered printf is default.
221 /* Connect to given url.
222 * It just makes TCP connection to ISDS server found in @url hostname part. */
223 /*int isds_connect(struct isds_ctx *context, const char *url);*/
225 /* Set timeout in miliseconds for each network job like connecting to server
226 * or sending message. Use 0 to disable timeout limits. */
227 isds_error
isds_set_timeout(struct isds_ctx
*context
,
228 const unsigned int timeout
) {
229 if (!context
) return IE_INVALID_CONTEXT
;
231 context
->timeout
= timeout
;
236 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
238 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
240 if (curl_err
) return IE_ERROR
;
247 /* Discard credentials.
248 * Only that. It does not cause log out, connection close or similar. */
249 static isds_error
discard_credentials(struct isds_ctx
*context
) {
250 if(!context
) return IE_INVALID_CONTEXT
;
252 if (context
->username
) {
253 memset(context
->username
, 0, strlen(context
->username
));
254 free(context
->username
);
255 context
->username
= NULL
;
257 if (context
->password
) {
258 memset(context
->password
, 0, strlen(context
->password
));
259 free(context
->password
);
260 context
->password
= NULL
;
267 /* Connect and log in into ISDS server.
268 * @url is address of ISDS web service
269 * @username is user name of ISDS user
270 * @password is user's secret password
271 * @certificate is NULL terminated string with PEM formated client's
272 * certificate. Use NULL if only password autentication should be performed.
273 * @key is private key for client's certificate as (base64 encoded?) NULL
274 * terminated string. Use NULL if only password autentication is desired.
276 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
277 const char *username
, const char *password
,
278 const char *certificate
, const char* key
) {
279 isds_error err
= IE_NOT_LOGGED_IN
;
281 xmlNsPtr isds_ns
= NULL
;
282 xmlNodePtr request
= NULL
;
283 xmlNodePtr response
= NULL
;
285 if (!context
) return IE_INVALID_CONTEXT
;
286 if (!url
|| !username
|| !password
) return IE_INVAL
;
287 if (certificate
|| key
) return IE_NOTSUP
;
289 /* Store configuration */
291 context
->url
= strdup(url
);
295 /* Prepare CURL handle */
296 context
->curl
= curl_easy_init();
297 if (!(context
->curl
))
300 /* Build login request */
301 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
303 isds_log_message(context
, _("Could build ISDS login request"));
306 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
308 isds_log_message(context
, _("Could not create ISDS name space"));
309 xmlFreeNode(request
);
312 xmlSetNs(request
, isds_ns
);
314 /* Store credentials */
315 /* FIXME: mlock password
316 * (I have a library) */
317 discard_credentials(context
);
318 context
->username
= strdup(username
);
319 context
->password
= strdup(password
);
320 if (!(context
->username
&& context
->password
)) {
321 discard_credentials(context
);
322 xmlFreeNode(request
);
326 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
329 /* Send login request */
330 soap_err
= soap(context
, "dz", request
, &response
);
332 /* Remove credentials */
333 discard_credentials(context
);
335 /* Destroy login request */
336 xmlFreeNode(request
);
339 xmlFreeNodeList(response
);
340 close_connection(context
);
344 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
345 * authentication succeeded if soap_err == IE_SUCCESS */
347 /* XXX: Dummy cookie.
348 * Probably, we could remove the cookie from context because CURL
349 * administrer it on its own and soap()/http() does autologin now. */
350 free(context
->cookie
);
351 context
->cookie
= strdup("42");
352 if (!context
->cookie
) {
356 xmlFreeNodeList(response
);
359 isds_log(ILF_ISDS
, ILL_DEBUG
,
360 _("User %s has been logged into server %s successfully\n"),
366 /* Log out from ISDS server discards credentials and connection configuration. */
367 isds_error
isds_logout(struct isds_ctx
*context
) {
368 if (!context
) return IE_INVALID_CONTEXT
;
370 /* Close connection */
372 close_connection(context
);
375 /* Discard credentials for sure. They should no survive isds_login(),
376 * even successful .*/
377 discard_credentials(context
);
381 if (!context
->cookie
)
382 return IE_NOT_LOGGED_IN
;
383 free(context
->cookie
);
384 context
->cookie
= NULL
;
386 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
391 /* Verify connection to ISDS is alive and server is responding.
392 * Sent dumy request to ISDS and expect dummy response. */
393 isds_error
isds_ping(struct isds_ctx
*context
) {
395 xmlNsPtr isds_ns
= NULL
;
396 xmlNodePtr request
= NULL
;
397 xmlNodePtr response
= NULL
;
399 if (!context
) return IE_INVALID_CONTEXT
;
401 /* Check if connection is established */
402 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
405 /* Build dummy request */
406 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
408 isds_log_message(context
, _("Could build ISDS dummy request"));
411 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
413 isds_log_message(context
, _("Could not create ISDS name space"));
414 xmlFreeNode(request
);
417 xmlSetNs(request
, isds_ns
);
419 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
421 /* Sent dummy request */
422 soap_err
= soap(context
, "dz", request
, &response
);
424 /* Destroy login request */
425 xmlFreeNode(request
);
428 isds_log(ILF_ISDS
, ILL_DEBUG
,
429 _("ISDS server could not be contacted\n"));
430 xmlFreeNodeList(response
);
431 close_connection(context
);
435 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
436 * authentication succeeded if soap_err == IE_SUCCESS */
437 /* TODO: ISDS documentation does not specify response body.
438 * However real server sends back DummyOperationResponse */
441 xmlFreeNodeList(response
);
443 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
449 /* Send bogus request to ISDS.
450 * Just for test purposes */
451 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
453 xmlNsPtr isds_ns
= NULL
;
454 xmlNodePtr request
= NULL
;
455 xmlDocPtr response
= NULL
;
456 xmlChar
*code
= NULL
, *message
= NULL
;
458 if (!context
) return IE_INVALID_CONTEXT
;
460 /* Check if connection is established */
461 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
464 /* Build dummy request */
465 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
467 isds_log_message(context
, _("Could build ISDS bogus request"));
470 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
472 isds_log_message(context
, _("Could not create ISDS name space"));
473 xmlFreeNode(request
);
476 xmlSetNs(request
, isds_ns
);
478 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
480 /* Sent bogus request */
481 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
);
483 /* Destroy request */
484 xmlFreeNode(request
);
487 isds_log(ILF_ISDS
, ILL_DEBUG
,
488 _("Processing ISDS response on bogus request failed\n"));
489 xmlFreeDoc(response
);
493 /* Check for response status */
494 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
495 &code
, &message
, NULL
);
497 isds_log(ILF_ISDS
, ILL_DEBUG
,
498 _("ISDS response on bogus request is missing status\n"));
501 xmlFreeDoc(response
);
504 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
505 /* FIXME: Convert UTF-8 into locale */
506 isds_log(ILF_ISDS
, ILL_DEBUG
,
507 _("Server refused bogus request (code=%s, message=%s)\n"),
509 isds_log_message(context
, _((char *)message
));
512 xmlFreeDoc(response
);
519 xmlFreeDoc(response
);
521 isds_log(ILF_ISDS
, ILL_DEBUG
,
522 _("Bogus message accepted by server. This should not happen.\n"));
528 /* Get data about logged in user and his box. */
529 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
530 struct isds_DbOwnerInfo
**db_owner_info
) {
532 xmlNsPtr isds_ns
= NULL
;
533 xmlNodePtr request
= NULL
;
534 xmlDocPtr response
= NULL
;
535 xmlChar
*code
= NULL
, *message
= NULL
;
538 if (!context
) return IE_INVALID_CONTEXT
;
539 if (!db_owner_info
) return IE_INVAL
;
541 /* Check if connection is established */
542 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
545 /* Build GetOwnerInfoFromLogin request */
546 request
= xmlNewNode(NULL
, BAD_CAST
"GetOwnerInfoFromLogin");
548 isds_log_message(context
,
549 _("Could build GetOwnerInfoFromLogin request"));
552 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
554 isds_log_message(context
, _("Could not create ISDS name space"));
555 xmlFreeNode(request
);
558 xmlSetNs(request
, isds_ns
);
559 node
= xmlNewChild(request
, NULL
, BAD_CAST
"dbDummy", NULL
);
561 isds_log_message(context
, _("Could nod add dbDummy Child to "
562 "GetOwnerInfoFromLogin element"));
563 xmlFreeNode(request
);
568 isds_log(ILF_ISDS
, ILL_DEBUG
,
569 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
572 err
= isds(context
, SERVICE_DB_SUPPLEMENTARY
, request
, &response
);
574 /* Destroy request */
575 xmlFreeNode(request
);
578 isds_log(ILF_ISDS
, ILL_DEBUG
,
579 _("Processing ISDS response on GetOwnerInfoFromLogin "
580 "request failed\n"));
581 xmlFreeDoc(response
);
585 /* Check for response status */
586 err
= isds_response_status(context
, SERVICE_DB_SUPPLEMENTARY
, response
,
587 &code
, &message
, NULL
);
589 isds_log(ILF_ISDS
, ILL_DEBUG
,
590 _("ISDS response on GetOwnerInfoFromLogin request is "
591 "missing status\n"));
594 xmlFreeDoc(response
);
597 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
598 /* FIXME: Convert UTF-8 into locale */
599 isds_log(ILF_ISDS
, ILL_DEBUG
,
600 _("Server refused GetOwnerInfoFromLogin request "
601 "(code=%s, message=%s)\n"), code
, message
);
602 isds_log_message(context
, _((char *)message
));
605 xmlFreeDoc(response
);
612 xmlFreeDoc(response
);
614 isds_log(ILF_ISDS
, ILL_DEBUG
,
615 _("GetOwnerInfoFromLogin request processed by server "
622 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
623 struct isds_message **message);
624 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
625 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
626 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
627 struct isds_address **address);
629 int isds_message_free(struct isds_message **message);
630 int isds_address_free(struct isds_address **address);
634 /* Deallocate structure isds_PersonName recursively and NULL it */
635 static void isds_PersonName_free(struct isds_PersonName
**person_name
) {
636 if (!person_name
|| !*person_name
) return;
638 free((*person_name
)->pnFirstName
);
639 free((*person_name
)->pnMiddleName
);
640 free((*person_name
)->pnLastName
);
641 free((*person_name
)->pnLastNameAtBirth
);
647 /* Deallocate structure isds_BirthInfo recursively and NULL it */
648 static void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
649 if (!birth_info
|| !*birth_info
) return;
651 free((*birth_info
)->biDate
);
652 free((*birth_info
)->biCity
);
653 free((*birth_info
)->biCounty
);
654 free((*birth_info
)->biState
);
660 /* Deallocate structure isds_Address recursively and NULL it */
661 static void isds_Address_free(struct isds_Address
**address
) {
662 if (!address
|| !*address
) return;
664 free((*address
)->adCity
);
665 free((*address
)->adStreet
);
666 free((*address
)->adNumberInStreet
);
667 free((*address
)->adNumberInMunicipality
);
668 free((*address
)->adZipCode
);
669 free((*address
)->adState
);
675 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
676 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
677 if (!db_owner_info
|| !*db_owner_info
) return;
679 free((*db_owner_info
)->dbID
);
681 free((*db_owner_info
)->ic
);
682 isds_PersonName_free(&((*db_owner_info
)->personName
));
683 free((*db_owner_info
)->firmName
);
684 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
685 isds_Address_free(&((*db_owner_info
)->address
));
686 free((*db_owner_info
)->nationality
);
687 free((*db_owner_info
)->email
);
688 free((*db_owner_info
)->telNumber
);
689 free((*db_owner_info
)->identifier
);
690 free((*db_owner_info
)->registryCode
);
694 *db_owner_info
= NULL
;
698 /* Makes known all relevant namespaces to give @xpat_ctx */
699 _hidden isds_error
register_namespaces(xmlXPathContextPtr xpath_ctx
) {
700 if (!xpath_ctx
) return IE_ERROR
;
702 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
704 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
706 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))