Add libvscremote: XML-RPC based remote extension for libvsccli.
authorMatthias Bolte <matthias.bolte@googlemail.com>
Fri, 17 Apr 2009 00:35:38 +0000 (02:35 +0200)
committerMatthias Bolte <matthias.bolte@googlemail.com>
Fri, 17 Apr 2009 00:35:38 +0000 (02:35 +0200)
Signed-off-by: Matthias Bolte <matthias.bolte@googlemail.com>

Makefile
cli/core.c
include/Makefile
include/libvsccli/types.h
include/libvscremote/Makefile [new file with mode: 0644]
include/libvscremote/libvscremote.h [new file with mode: 0644]
remote/Makefile [new file with mode: 0644]
remote/remote.c [new file with mode: 0644]

index 18cd569..7e44ec9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 #!/usr/bin/make
 #
-# Makefile to build libvsccli and libvscmisc
+# Makefile to build libvsccli, libvscmisc and libvscremote
 #
 # Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
 #
@@ -13,11 +13,11 @@ PWD = $(shell pwd)
 
 .PHONY: all clean install
 
-all: cli-all misc-all
+all: cli-all misc-all remote-all
 
-clean: cli-clean misc-clean
+clean: cli-clean misc-clean remote-clean
 
-install: cli-install misc-install include-install
+install: cli-install misc-install include-install remote-install
 
 %-all:
        @echo $@ | sed 's,\([a-z]*\)-all,echo "building \1"; $(MAKE) -I $(PWD) -C \1 all;,g' | sh
index 1961242..e56e37a 100644 (file)
@@ -220,7 +220,7 @@ vsc_cli_init (struct VscError *error,
        _vsc_cli_command_infos = command_infos;
        _vsc_cli_output_coloring = output_coloring;
 
-       rl_readline_name = "vscmgmt-cli";
+       rl_readline_name = name;
        rl_attempted_completion_function = _readline_completion;
 
        stifle_history (500);
@@ -237,6 +237,13 @@ vsc_cli_cleanup (void)
                return;
        }
 
+       _vsc_cli_name = NULL;
+       _vsc_cli_help = NULL;
+       _vsc_cli_option_infos = NULL;
+       _vsc_cli_command_infos = NULL;
+       _vsc_cli_output_coloring = TRUE;
+       _vsc_cli_interactive = FALSE;
+
        _initialized = FALSE;
 }
 
@@ -409,7 +416,12 @@ vsc_cli_enter_interactive (VscCliErrorFunction error_function,
        _vsc_cli_interactive = TRUE;
 
        while (_vsc_cli_interactive) {
-               prompt_function (prompt, 256, user_data);
+               prompt_function (&error, prompt, 256, user_data);
+
+               if (error.occured) {
+                       error_function (&error, user_data);
+                       goto outer_cleanup;
+               }
 
                input = readline (prompt);
 
index b6f4f0d..87e6f1e 100644 (file)
@@ -11,7 +11,7 @@
 
 .PHONY:  install
 
-install: libvsccli-install libvscmisc-install
+install: libvsccli-install libvscmisc-install libvscremote-install
 
 %-install:
        @echo $@ | sed 's,\([a-z]*\)-install,echo "installing include/\1"; $(MAKE) -C \1 install;,g' | sh
index fa4d776..37b933c 100644 (file)
@@ -45,8 +45,8 @@ typedef void (*VscCliCommandFunction) (struct VscError *error,
 typedef void (*VscCliErrorFunction) (struct VscError *error,
                                      void *user_data);
 
-typedef void (*VscCliPromptFunction) (char *prompt, size_t size,
-                                      void *user_data);
+typedef void (*VscCliPromptFunction) (struct VscError *error, char *prompt,
+                                      size_t size, void *user_data);
 
 struct VscCliOptionInfo {
        const char short_name;
diff --git a/include/libvscremote/Makefile b/include/libvscremote/Makefile
new file mode 100644 (file)
index 0000000..838a20f
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/make
+#
+# Makefile to install the header files of libvscremote
+#
+# Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
+#
+
+include ../../config.mk
+
+HEADERS  = libvscremote.h
+TARGET   = libvscremote
+
+#
+# Rules
+#
+
+.PHONY: install force
+
+install: $(HEADERS)
+
+include ../../rules.mk
+
diff --git a/include/libvscremote/libvscremote.h b/include/libvscremote/libvscremote.h
new file mode 100644 (file)
index 0000000..429c6ee
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * libvscremote.h: XML-RPC based remote extension for libvsccli
+ *
+ * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __VSC_REMOTE_H__
+#define __VSC_REMOTE_H__
+
+#include <libvsccli/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef const char * (*VscRemoteMethodNameLookupFunction)
+   (struct VscError *error, const char *command_name);
+
+typedef const char * (*VscRemoteArgumentNameLookupFunction)
+   (struct VscError *error, const char *command_name,
+    const char *option_long_name);
+
+extern const struct VscCliCommandInfo vsc_remote_login_command_info;
+extern const struct VscCliCommandInfo vsc_remote_logout_command_info;
+
+void
+vsc_remote_init (struct VscError *error, const char *name, const char *version,
+                 const char *remote_url,
+                 VscRemoteMethodNameLookupFunction method_name_lookup_function,
+                 VscRemoteArgumentNameLookupFunction argument_name_lookup_function);
+
+void
+vsc_remote_cleanup (void);
+
+void
+vsc_remote_check (struct VscError *error, int expected_login_state);
+
+const char *
+vsc_remote_get_cookie (struct VscError *error);
+
+const char *
+vsc_remote_get_username (struct VscError *error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VSC_REMOTE_H__ */
diff --git a/remote/Makefile b/remote/Makefile
new file mode 100644 (file)
index 0000000..5a88a2d
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/make
+#
+# Makefile to build libvscremote
+#
+# Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
+#
+
+include ../config.mk
+
+OBJS     = remote.o
+TARGET   = libvscremote.so
+
+#
+# Fancy gcc options
+#
+
+# libvsccli and libvscmisc
+CFLAGS  += -I ../include
+
+#
+# Rules
+#
+
+.PHONY: all clean install
+
+all: $(TARGET)
+
+clean:
+       rm -rf $(TARGET) $(OBJS)
+
+install: install-target-lib
+
+include ../rules.mk
diff --git a/remote/remote.c b/remote/remote.c
new file mode 100644 (file)
index 0000000..93a2278
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * remote.c: XML-RPC based remote extension for libvsccli
+ *
+ * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <termios.h>
+
+#include <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+
+#include <libvscmisc/assert.h>
+#include <libvscmisc/error.h>
+#include <libvscmisc/memory.h>
+#include <libvscmisc/string.h>
+
+#include <libvsccli/option.h>
+
+#include "../include/libvscremote/libvscremote.h"
+
+static int _initialized = FALSE;
+static VscRemoteMethodNameLookupFunction _method_name_lookup_function = NULL;
+static VscRemoteArgumentNameLookupFunction _argument_name_lookup_function = NULL;
+static char *_remote_url = NULL;
+static char *_username = NULL;
+static char *_cookie = NULL;
+
+static void
+_error_from_xmlrpc (struct VscError *error, const xmlrpc_env *env,
+                    const char *file, const char *function, int line)
+{
+       vsc_error_report (error, VSC__ERROR_CODE__XMLRPC_ERROR, file, line,
+                         function, "%s (%d)", env->fault_string,
+                         env->fault_code);
+}
+
+#define _ERROR_FROM_XMLRPC(error, env) \
+       _error_from_xmlrpc (error, env, __FILE__, __FUNCTION__, __LINE__)
+
+static char *
+_request_password (struct VscError *error, const char *username)
+{
+       struct termios old, new;
+       char *password = NULL;
+       size_t size = 256;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       password = vsc_alloc (error, size);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return NULL;
+       }
+
+       if (tcgetattr (fileno (stdin), &old) != 0) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                            "Internal error");
+               return NULL;
+       }
+
+       new = old;
+       new.c_lflag &= ~ECHO;
+
+       if (tcsetattr (fileno (stdin), TCSAFLUSH, &new) != 0) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                            "Internal error");
+               return NULL;
+       }
+
+       printf ("Enter password for %s: ", username);
+
+       if (fgets (password, size, stdin) == NULL) {
+               vsc_free (&password);
+       }
+
+       if (password != NULL) {
+               size = strlen (password);
+
+               if (size > 0) {
+                       password[size - 1] = '\0';
+               }
+       }
+
+       (void) tcsetattr (fileno (stdin), TCSAFLUSH, &old);
+
+       printf ("\n");
+
+       return password;
+}
+
+static const char *
+_lookup_method_name (struct VscError *error, const char *command_name)
+{
+       const char *method_name = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (_method_name_lookup_function != NULL);
+
+       method_name = _method_name_lookup_function (error, command_name);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return NULL;
+       }
+
+       if (method_name == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                            "Internal error");
+               return NULL;
+       }
+
+       return method_name;
+}
+
+static const char *
+_lookup_argument_name (struct VscError *error, const char *command_name,
+                       const char *option_long_name)
+{
+       const char *argument_name = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (_argument_name_lookup_function != NULL);
+
+       argument_name = _argument_name_lookup_function (error, command_name,
+                                                       option_long_name);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return NULL;
+       }
+
+       if (argument_name == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                            "Internal error");
+               return NULL;
+       }
+
+       return argument_name;
+}
+
+/*
+ * Login
+ */
+
+static void
+_login (struct VscError *error, const struct VscCliCommandInfo *command_info,
+        const struct VscCliOption *option_list, void *user_data);
+
+static const struct VscCliOptionInfo _login_option_infos[] = {
+       { -1, "username", "username for the remote system",
+         VSC_CLI__OPTION_TYPE__DATA, TRUE },
+       { -1, "password", "password for the remote system",
+         VSC_CLI__OPTION_TYPE__DATA, FALSE },
+       { -1, NULL, NULL, 0, FALSE },
+};
+
+const struct VscCliCommandInfo vsc_remote_login_command_info = {
+       "login",
+       "login to the remote system",
+       "",
+       _login,
+       _login_option_infos,
+};
+
+static void
+_login (struct VscError *error,
+        const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
+        const struct VscCliOption *option_list,
+        void *user_data VSC__ATTR__UNUSED)
+{
+       const char *method_name;
+       const char *username_name;
+       const char *password_name;
+       const char *username;
+       const char *password;
+       char *password_;
+       xmlrpc_env env;
+       xmlrpc_value *result = NULL;
+       int code;
+       char *message = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       vsc_remote_check (error, FALSE);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       method_name = _lookup_method_name (error, "login");
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       username_name = _lookup_argument_name (error, "login", "username");
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       password_name = _lookup_argument_name (error, "login", "password");
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       VSC__ASSERT (_username == NULL);
+
+       username = vsc_cli_option_get_string (option_list, "username");
+       password = vsc_cli_option_get_string (option_list, "password");
+
+       VSC__ASSERT (username != NULL);
+
+       if (password == NULL) {
+               password_ = _request_password (error, username);
+
+               if (password_ == NULL) {
+                       VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                                    "Could not read passowrd");
+                       return;
+               }
+       } else {
+               password_ = vsc_strdup (error, password);
+
+               if (error->occured) {
+                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                       return;
+               }
+       }
+
+       xmlrpc_env_init (&env);
+
+       result = xmlrpc_client_call (&env, _remote_url, method_name,
+                                    "({s:s,s:s})",
+                                    username_name, username,
+                                    password_name, password_);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               result = NULL;
+               goto cleanup;
+       }
+
+       xmlrpc_decompose_value (&env, result, "{s:i,s:s,s:s,*}",
+                               "code", &code,
+                               "message", &message,
+                               "value", &_cookie);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               message = NULL;
+               goto cleanup;
+       }
+
+       if (code != 0) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
+                            "%s (%d)", message, code);
+               vsc_free (&_cookie);
+               goto cleanup;
+       }
+
+       _username = vsc_strdup (error, username);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto cleanup;
+       }
+
+cleanup:
+       if (result != NULL) {
+               xmlrpc_DECREF (result);
+       }
+
+       vsc_free (&password_);
+       vsc_free (&message);
+
+       xmlrpc_env_clean (&env);
+}
+
+/*
+ * Logout
+ */
+
+static void
+_logout (struct VscError *error, const struct VscCliCommandInfo *command_info,
+         const struct VscCliOption *option_list, void *user_data);
+
+static const struct VscCliOptionInfo _logout_option_infos[] = {
+       { -1, NULL, NULL, 0, FALSE },
+};
+
+const struct VscCliCommandInfo vsc_remote_logout_command_info = {
+       "logout",
+       "logout from the remote system",
+       "",
+       _logout,
+       _logout_option_infos,
+};
+
+static void
+_logout (struct VscError *error,
+         const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
+         const struct VscCliOption *option_list VSC__ATTR__UNUSED,
+         void *user_data VSC__ATTR__UNUSED)
+{
+       const char *method_name = NULL;
+       const char *cookie_name = NULL;
+       xmlrpc_env env;
+       xmlrpc_value *result = NULL;
+       int code;
+       char *message = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       vsc_remote_check (error, TRUE);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       method_name = _lookup_method_name (error, "logout");
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       cookie_name = _lookup_argument_name (error, "logout", "cookie");
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       xmlrpc_env_init (&env);
+
+       result = xmlrpc_client_call (&env, _remote_url, method_name,
+                                    "({s:s})",
+                                    cookie_name, _cookie);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               result = NULL;
+               goto cleanup;
+       }
+
+       xmlrpc_decompose_value (&env, result, "{s:i,s:s,*}",
+                               "code", &code,
+                               "message", &message);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               message = NULL;
+               goto cleanup;
+       }
+
+       if (code != 0) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
+                            "%s (%d)", message, code);
+               goto cleanup;
+       }
+
+       vsc_free (&_cookie);
+       vsc_free (&_username);
+
+cleanup:
+       if (result != NULL) {
+               xmlrpc_DECREF (result);
+       }
+
+       vsc_free (&message);
+
+       xmlrpc_env_clean (&env);
+}
+
+/*
+ * Core
+ */
+
+void
+vsc_remote_init (struct VscError *error, const char *name, const char *version,
+                 const char *remote_url,
+                 VscRemoteMethodNameLookupFunction method_name_lookup_function,
+                 VscRemoteArgumentNameLookupFunction argument_name_lookup_function)
+{
+       xmlrpc_env env;
+       char user_agent[256];
+       struct xmlrpc_clientparms clientParms;
+       struct xmlrpc_curl_xportparms curlParms;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (remote_url != NULL);
+       VSC__ASSERT (method_name_lookup_function != NULL);
+       VSC__ASSERT (argument_name_lookup_function != NULL);
+
+       if (_initialized) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
+                            "Already initialized");
+               return;
+       }
+
+       _method_name_lookup_function = method_name_lookup_function;
+       _argument_name_lookup_function = argument_name_lookup_function;
+
+       _remote_url = vsc_strdup (error, remote_url);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       /*
+        * Configure the XML-RPC-C Curl backend to send a User-Agent.
+        */
+       xmlrpc_env_init (&env);
+
+       memset (&clientParms, 0, sizeof (struct xmlrpc_clientparms));
+       memset (&curlParms, 0, sizeof (struct xmlrpc_curl_xportparms));
+
+       snprintf (user_agent, 256, "%s/%s", name, version);
+
+       curlParms.user_agent = user_agent;
+
+       clientParms.transport = "curl";
+       clientParms.transportparmsP = &curlParms;
+       clientParms.transportparm_size = XMLRPC_CXPSIZE (user_agent);
+
+       xmlrpc_client_init2 (&env, XMLRPC_CLIENT_NO_FLAGS, name, version,
+                            &clientParms, XMLRPC_CPSIZE (transportparm_size));
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       _initialized = TRUE;
+
+cleanup:
+       xmlrpc_env_clean (&env);
+}
+
+void
+vsc_remote_cleanup (void)
+{
+       struct VscError error;
+
+       VSC__ASSERT (_initialized);
+
+       if (! _initialized) {
+               return;
+       }
+
+       /*
+        * FIXME: If an RPC error occured this call segfaults. This only happens if
+        *        the XML-RPC-C library is compiled with -O3. If compiled with -O0
+        *        everything is fine.
+        */
+       xmlrpc_client_cleanup();
+
+       if (_cookie != NULL) {
+               vsc_error_init (&error);
+
+               _logout (&error, NULL, NULL, NULL);
+
+               vsc_error_cleanup (&error);
+       }
+
+       vsc_free (&_remote_url);
+       vsc_free (&_cookie);
+       vsc_free (&_username);
+
+       _method_name_lookup_function = NULL;
+       _argument_name_lookup_function = NULL;
+
+       _initialized = FALSE;
+}
+
+void
+vsc_remote_check (struct VscError *error, int expected_login_state)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       if (! _initialized) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
+                            "Not initialized");
+               return;
+       }
+
+       if (_remote_url == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
+                            "No remote URL specified");
+               return;
+       }
+
+       if (expected_login_state == TRUE && _cookie == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL, "Not logged in");
+               return;
+       }
+
+       if (expected_login_state == FALSE && _cookie != NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
+                            "Already logged in");
+               return;
+       }
+}
+
+const char *
+vsc_remote_get_cookie (struct VscError *error)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       if (! _initialized) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
+                            "Not initialized");
+               return NULL;
+       }
+
+       if (_cookie == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
+                            "No cookie avaiable");
+               return NULL;
+       }
+
+       return _cookie;
+}
+
+const char *
+vsc_remote_get_username (struct VscError *error)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       if (! _initialized) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
+                            "Not initialized");
+               return NULL;
+       }
+
+       return _username;
+}