Check for NULL argument of isds_GetOwnerInfoFromLogin()
[libisds.git] / src / isds.c
blobbb250aa7c59f41e9b9b2001933a4d6ed3e6ee06c
1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include "isds_priv.h"
7 #include "utils.h"
8 #include "soap.h"
9 #include "validator.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 */
18 xml_node = NULL;
19 soap_ns = NULL;
20 log_facilities = ILF_NONE;
21 log_level = ILL_NONE;
23 /* Initialize CURL */
24 if (curl_global_init(CURL_GLOBAL_ALL)) {
25 return IE_ERROR;
28 /* This can _exit() current program. Find not so assertive check. */
29 LIBXML_TEST_VERSION;
31 /* Allocate global variables */
32 if (!(xml_node = xmlNewNode(NULL, BAD_CAST "global-element")))
33 return IE_ERROR;
34 if (!(soap_ns = xmlNewNs(NULL, BAD_CAST SOAP_NS, BAD_CAST "soap")))
35 return IE_ERROR;
36 if (!(isds_ns = xmlNewNs(NULL, BAD_CAST ISDS_NS, BAD_CAST "isds")))
37 return IE_ERROR;
39 return IE_SUCCESS;
43 /* Deinicialize ISDS library.
44 * Global function, must be called as last library function. */
45 isds_error isds_cleanup(void) {
46 /* XML */
47 xmlFreeNs(isds_ns);
48 xmlFreeNs(soap_ns);
49 xmlFreeNode(xml_node);
50 xmlCleanupParser();
52 /* Curl */
53 curl_global_cleanup();
55 return IE_SUCCESS;
59 /* Return text description of ISDS error */
60 char *isds_strerror(const isds_error error) {
61 switch (error) {
62 case IE_SUCCESS:
63 return(_("Success")); break;
64 case IE_ERROR:
65 return(_("Unspecified error")); break;
66 case IE_NOTSUP:
67 return(_("Not supported")); break;
68 case IE_INVAL:
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;
76 case IE_TIMED_OUT:
77 return(_("Timed out")); break;
78 case IE_NOEXIST:
79 return(_("Not exist")); break;
80 case IE_NOMEM:
81 return(_("Out of memory")); break;
82 case IE_NETWORK:
83 return(_("Network problem")); break;
84 case IE_SOAP:
85 return(_("SOAP problem")); break;
86 case IE_XML:
87 return(_("XML problem")); break;
88 case IE_ISDS:
89 return(_("ISDS server problem")); break;
90 default:
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));
103 return 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);
120 free(*context);
121 *context = NULL;
122 return IE_SUCCESS;
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
129 * supplied. */
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) {
141 char *buffer;
142 size_t length;
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;
151 if (message)
152 strcpy(buffer, message);
153 else
154 *buffer = '\0';
156 context->long_message = buffer;
157 return IE_SUCCESS;
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) {
166 char *buffer;
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;
183 return IE_SUCCESS;
187 /* Set logging up.
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;
193 log_level = level;
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, ...) {
202 va_list ap;
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);
213 va_end(ap);
214 /* Line buffered printf is default.
215 * fflush(stderr);*/
217 return IE_SUCCESS;
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;
233 if (context->curl) {
234 CURLcode curl_err;
236 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
237 if (!curl_err)
238 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
239 context->timeout);
240 if (curl_err) return IE_ERROR;
243 return IE_SUCCESS;
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;
263 return IE_SUCCESS;
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.
275 * */
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;
280 isds_error soap_err;
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 */
290 free(context->url);
291 context->url = strdup(url);
292 if (!(context->url))
293 return IE_NOMEM;
295 /* Prepare CURL handle */
296 context->curl = curl_easy_init();
297 if (!(context->curl))
298 return IE_ERROR;
300 /* Build login request */
301 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
302 if (!request) {
303 isds_log_message(context, _("Could build ISDS login request"));
304 return IE_ERROR;
306 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
307 if(!isds_ns) {
308 isds_log_message(context, _("Could not create ISDS name space"));
309 xmlFreeNode(request);
310 return IE_ERROR;
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);
323 return IE_NOMEM;
326 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
327 username, url);
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);
338 if (soap_err) {
339 xmlFreeNodeList(response);
340 close_connection(context);
341 return soap_err;
344 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
345 * authentication succeeded if soap_err == IE_SUCCESS */
346 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) {
353 err = IE_NOMEM;
356 xmlFreeNodeList(response);
358 if (!err)
359 isds_log(ILF_ISDS, ILL_DEBUG,
360 _("User %s has been logged into server %s successfully\n"),
361 username, url);
362 return err;
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 */
371 if (context->curl) {
372 close_connection(context);
375 /* Discard credentials for sure. They should no survive isds_login(),
376 * even successful .*/
377 discard_credentials(context);
378 free(context->url);
379 context->url = NULL;
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"));
387 return IE_SUCCESS;
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) {
394 isds_error soap_err;
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");
407 if (!request) {
408 isds_log_message(context, _("Could build ISDS dummy request"));
409 return IE_ERROR;
411 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
412 if(!isds_ns) {
413 isds_log_message(context, _("Could not create ISDS name space"));
414 xmlFreeNode(request);
415 return IE_ERROR;
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);
427 if (soap_err) {
428 isds_log(ILF_ISDS, ILL_DEBUG,
429 _("ISDS server could not be contacted\n"));
430 xmlFreeNodeList(response);
431 close_connection(context);
432 return soap_err;
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"));
445 return IE_SUCCESS;
449 /* Send bogus request to ISDS.
450 * Just for test purposes */
451 isds_error isds_bogus_request(struct isds_ctx *context) {
452 isds_error err;
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");
466 if (!request) {
467 isds_log_message(context, _("Could build ISDS bogus request"));
468 return IE_ERROR;
470 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
471 if(!isds_ns) {
472 isds_log_message(context, _("Could not create ISDS name space"));
473 xmlFreeNode(request);
474 return IE_ERROR;
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);
486 if (err) {
487 isds_log(ILF_ISDS, ILL_DEBUG,
488 _("Processing ISDS response on bogus request failed\n"));
489 xmlFreeDoc(response);
490 return err;
493 /* Check for response status */
494 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
495 &code, &message, NULL);
496 if (err) {
497 isds_log(ILF_ISDS, ILL_DEBUG,
498 _("ISDS response on bogus request is missing status\n"));
499 free(code);
500 free(message);
501 xmlFreeDoc(response);
502 return err;
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"),
508 code, message);
509 isds_log_message(context, _((char *)message));
510 free(code);
511 free(message);
512 xmlFreeDoc(response);
513 return IE_ISDS;
517 free(code);
518 free(message);
519 xmlFreeDoc(response);
521 isds_log(ILF_ISDS, ILL_DEBUG,
522 _("Bogus message accepted by server. This should not happen.\n"));
524 return IE_SUCCESS;
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) {
531 isds_error err;
532 xmlNsPtr isds_ns = NULL;
533 xmlNodePtr request = NULL;
534 xmlDocPtr response = NULL;
535 xmlChar *code = NULL, *message = NULL;
536 xmlNodePtr node;
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");
547 if (!request) {
548 isds_log_message(context,
549 _("Could build GetOwnerInfoFromLogin request"));
550 return IE_ERROR;
552 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
553 if(!isds_ns) {
554 isds_log_message(context, _("Could not create ISDS name space"));
555 xmlFreeNode(request);
556 return IE_ERROR;
558 xmlSetNs(request, isds_ns);
559 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
560 if (!node) {
561 isds_log_message(context, _("Could nod add dbDummy Child to "
562 "GetOwnerInfoFromLogin element"));
563 xmlFreeNode(request);
564 return IE_ERROR;
568 isds_log(ILF_ISDS, ILL_DEBUG,
569 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
571 /* Sent request */
572 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
574 /* Destroy request */
575 xmlFreeNode(request);
577 if (err) {
578 isds_log(ILF_ISDS, ILL_DEBUG,
579 _("Processing ISDS response on GetOwnerInfoFromLogin "
580 "request failed\n"));
581 xmlFreeDoc(response);
582 return err;
585 /* Check for response status */
586 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
587 &code, &message, NULL);
588 if (err) {
589 isds_log(ILF_ISDS, ILL_DEBUG,
590 _("ISDS response on GetOwnerInfoFromLogin request is "
591 "missing status\n"));
592 free(code);
593 free(message);
594 xmlFreeDoc(response);
595 return err;
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));
603 free(code);
604 free(message);
605 xmlFreeDoc(response);
606 return IE_ISDS;
610 free(code);
611 free(message);
612 xmlFreeDoc(response);
614 isds_log(ILF_ISDS, ILL_DEBUG,
615 _("GetOwnerInfoFromLogin request processed by server "
616 "successfully.\n"));
618 return IE_SUCCESS;
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);
643 *person_name = NULL;
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);
656 *birth_info = NULL;
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);
671 *address = NULL;
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);
680 /* dbType */
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);
691 /* dbState */
692 /* dbEffectiveOVM */
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))
703 return IE_ERROR;
704 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
705 return IE_ERROR;
706 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
707 return IE_ERROR;
708 return IE_SUCCESS;