This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/vn/vn-login.c

617 lines
15 KiB
C

/*
* Copyright (C) 2012 - Juan Ferrer Toribio
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "vn-login.h"
#include "vn-set.h"
#include "vn-gui-private.h"
#include <glib-unix.h>
#define LOGIN_UI _GUI_DIR"/login.glade"
#define CONFIG_DB _VN_CONFIG_DIR"/config.db"
#define IS_DEFINED(string) (string && g_strcmp0 (string, ""))
#define BUILDER_GET(self, name) ((gpointer) gtk_builder_get_object (self, name))
typedef struct
{
VnLogin * self;
DbConn * conn;
GThread * thread;
gboolean connected;
GError * error;
}
ConnectData;
void vn_login_on_gui_logout (VnGui * gui, VnLogin * self);
void vn_login_on_gui_exit (VnGui * gui, VnLogin * self);
static gboolean vn_login_on_signal (VnLogin * self);
G_DEFINE_TYPE (VnLogin, vn_login, G_TYPE_OBJECT);
/**
* vn_login_new:
*
* Creates a new Gui object.
*
* Return value: the created #VnLogin
**/
VnLogin * vn_login_new (GtkApplication * application)
{
VnLogin * login = g_object_new (VN_TYPE_LOGIN, "application", application, NULL);
g_unix_signal_add (SIGINT, (GSourceFunc) vn_login_on_signal, login);
g_unix_signal_add (SIGTERM, (GSourceFunc) vn_login_on_signal, login);
g_unix_signal_add (SIGHUP, (GSourceFunc) vn_login_on_signal, login);
return login;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
/*
* Frees the #ConnectData struct
*/
void connect_data_free (ConnectData * connect_data)
{
if (connect_data->error)
g_error_free (connect_data->error);
g_object_unref (connect_data->conn);
g_object_unref (connect_data->self);
g_thread_unref (connect_data->thread);
g_free (connect_data);
}
/*
* Loads the resources used by the login.
*/
static void vn_login_load (VnLogin * self)
{
GError * err = NULL;
gchar * cfg_dir;
gchar * cfg_file;
GFile * src_file;
GFile * dst_file;
// Initializing SQLite connection
cfg_dir = g_build_filename (g_get_user_config_dir (), "hedera", NULL);
cfg_file = g_build_filename (cfg_dir, "config.db", NULL);
src_file = g_file_new_for_path (CONFIG_DB);
dst_file = g_file_new_for_path (cfg_file);
if (!g_file_query_exists (dst_file, NULL))
{
g_mkdir_with_parents (cfg_dir, 0700);
if (!g_file_copy (src_file, dst_file, 0, NULL, NULL, NULL, &err))
goto exit;
}
self->cfg_conn = db_conn_new ();
if (!db_conn_load_plugin (self->cfg_conn, "sqlite", &err)
|| !db_conn_open (self->cfg_conn, NULL, cfg_file, NULL, NULL, &err))
goto exit;
// Loading interface
if (gtk_builder_add_from_file (self->builder, LOGIN_UI, &err))
{
const GList * i;
VnSet * models;
models = BUILDER_GET (self->builder, "models");
if (models)
for (i = vn_set_get_objects (models); i; i = i->next)
db_model_set_conn (i->data, self->cfg_conn);
self->window = BUILDER_GET (self->builder, "window");
self->user = BUILDER_GET (self->builder, "user");
self->pass = BUILDER_GET (self->builder, "password");
self->remember = BUILDER_GET (self->builder, "remember");
self->connect = BUILDER_GET (self->builder, "connect");
self->settings_button = BUILDER_GET (self->builder, "settings");
self->settings_dialog = BUILDER_GET (self->builder, "settings-dialog");
self->login_data = BUILDER_GET (self->builder, "iterator-login");
self->last_conn = BUILDER_GET (self->builder, "iterator-last-conn");
self->connections = BUILDER_GET (self->builder, "connections-grid");
self->settings_button = BUILDER_GET (self->builder, "settings-button");
gtk_builder_connect_signals (self->builder, self);
gtk_application_add_window (self->app, self->window);
}
// Freeing resources
exit:
if (err)
{
g_warning ("VnLogin: %s", err->message);
g_clear_error (&err);
}
g_free (cfg_dir);
g_free (cfg_file);
g_object_unref (src_file);
g_object_unref (dst_file);
}
/*
* Shows the login window to the user.
*/
static void vn_login_show (VnLogin * self)
{
if (!self->window)
vn_login_load (self);
if (self->window)
{
gtk_widget_show_all (GTK_WIDGET (self->window));
gtk_widget_grab_focus (GTK_WIDGET (self->user));
}
}
/*
* Enables/disables the spinner at login dialog.
*/
static void vn_login_set_loading (VnLogin * self, gboolean loading)
{
gtk_widget_set_sensitive (GTK_WIDGET (self->window), !loading);
gtk_button_set_label (self->connect, NULL);
gtk_container_remove (GTK_CONTAINER (self->connect),
gtk_bin_get_child (GTK_BIN (self->connect)));
if (loading)
{
GtkWidget * spinner = gtk_spinner_new ();
gtk_spinner_start (GTK_SPINNER (spinner));
gtk_container_add (GTK_CONTAINER (self->connect), spinner);
gtk_widget_show_all (spinner);
}
else
gtk_button_set_label (self->connect, "Connect");
gtk_widget_set_sensitive (GTK_WIDGET (self->connect), !loading);
}
/*
* Frees the GUI object.
*/
static void vn_login_free_gui (VnLogin * self)
{
if (self->gui)
{
g_object_disconnect (self->gui
,"any_signal", vn_login_on_gui_exit, self
,"any_signal", vn_login_on_gui_logout, self
,NULL
);
g_clear_object (&self->gui);
}
}
/*
* Shows the login dialog when user logout from GUI.
*/
void vn_login_on_gui_logout (VnGui * gui, VnLogin * self)
{
GValue * null_pass = gvn_value_new_null ();
db_iterator_set_value (self->login_data, "password", null_pass, NULL);
db_iterator_perform_operations (self->login_data);
gvn_value_free (null_pass);
vn_login_free_gui (self);
vn_login_show (self);
}
/*
* Destroys the login dialog when user exits from GUI.
*/
void vn_login_on_gui_exit (VnGui * gui, VnLogin * self)
{
vn_login_free_gui (self);
gtk_widget_destroy (GTK_WIDGET (self->window));
}
/*
* Checks if exists a configuration record, if not, creates it.
*/
void vn_login_on_last_conn_ready (DbIterator * iterator, gboolean ready, VnLogin * self)
{
if (ready && db_iterator_get_nrows (iterator) == 0)
db_iterator_insert (iterator);
}
/*
* Sets the default user and password for selected configuration.
*/
void vn_login_on_server_changed (DbIterator * iterator, VnLogin * self)
{
gboolean first_login;
gboolean remember = FALSE;
if (db_iterator_get_row (self->login_data) != -1)
{
const gchar * pass = db_iterator_get_string (self->login_data, "password");
gvn_param_set_value (self->user, db_iterator_get_value (self->login_data, "user"));
if (pass)
{
gchar * decoded = gvn_decode (pass);
gvn_param_set_string (self->pass, decoded);
g_free (decoded);
remember = TRUE;
}
else
gvn_param_set_null (self->pass);
}
else
{
gvn_param_set_null (self->user);
gvn_param_set_null (self->pass);
}
first_login = gvn_param_is_null (self->remember);
gvn_param_set_boolean (self->remember, remember);
if (remember && first_login)
gtk_button_clicked (self->connect);
}
/*
* Handles the SIGHUP, SIGTERM and SIGINT signals, closing normally.
*/
static gboolean vn_login_on_signal (VnLogin * self)
{
g_warning ("Signal received.");
if (self->gui)
vn_gui_logout (self->gui, TRUE);
else
gtk_widget_destroy (GTK_WIDGET (self->window));
return G_SOURCE_REMOVE;
}
/*
* Closes the login window when Escape is pressed.
*/
gboolean vn_login_on_key_pressed (GtkWidget * window, GdkEventKey * event, VnLogin * self)
{
if (event->type == GDK_KEY_PRESS
&& (event->keyval == GDK_KEY_Escape
|| ((event->keyval == GDK_KEY_q || event->keyval == GDK_KEY_Q)
&& (event->state & GDK_CONTROL_MASK))))
{
gtk_widget_destroy (window);
return TRUE;
}
return FALSE;
}
/*
* Closes the application when login window is destroyed.
*/
void vn_login_on_destroyed (GtkWidget * window, VnLogin * self)
{
self->window = NULL;
gtk_main_quit ();
}
void vn_login_on_pass_show (GtkEntry * entry, GtkEntryIconPosition * pos,
GdkEvent * event, VnLogin * self)
{
gtk_entry_set_visibility (entry, TRUE);
}
void vn_login_on_pass_hide (GtkEntry * entry, GtkEntryIconPosition * pos,
GdkEvent * event, VnLogin * self)
{
gtk_entry_set_visibility (entry, FALSE);
}
static void vn_login_on_startup (GApplication * app, VnLogin * self)
{
// g_object_set (gtk_settings_get_default (), "gtk-button-images", TRUE, NULL);
}
static void vn_login_on_activate (GApplication * app, VnLogin * self)
{
if (gtk_main_level () == 0)
{
vn_login_show (self);
gtk_main ();
}
else if (!self->gui)
gtk_window_present (self->window);
}
//------------------------------------ Login process
/*
* Saves the login information and opens the main GUI.
*/
static gboolean vn_login_done (ConnectData * connect_data)
{
VnLogin * self = connect_data->self;
g_signal_handlers_block_by_func (self->login_data,
vn_login_on_server_changed, self);
if (connect_data->connected)
{
db_iterator_set_value (self->login_data, "user", gvn_param_get_value (self->user), NULL);
if (gvn_param_get_boolean (self->remember))
{
gchar * encoded = gvn_encode (gvn_param_get_string (self->pass));
db_iterator_set_string (self->login_data, "password", encoded);
g_free (encoded);
}
db_iterator_perform_operations (self->last_conn);
db_iterator_perform_operations (self->login_data);
gvn_param_set_null (self->pass);
gtk_widget_hide (GTK_WIDGET (self->window));
self->gui = vn_gui_new (self->app, connect_data->conn);
g_object_connect (self->gui
,"signal::logout", vn_login_on_gui_logout, self
,"signal::exit", vn_login_on_gui_exit, self
,NULL
);
vn_gui_open (self->gui);
}
else
{
GtkWidget * dialog;
dialog = gtk_message_dialog_new (self->window
,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
,GTK_MESSAGE_ERROR
,GTK_BUTTONS_OK
,_("Login error")
);
gtk_window_set_title (GTK_WINDOW (dialog), _("Login error"));
if (connect_data->error)
{
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", connect_data->error->message);
if (connect_data->error->code == DB_CONN_ERROR_BAD_LOGIN)
gvn_param_set_null (self->pass);
}
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
vn_login_show (self);
}
g_signal_handlers_unblock_by_func (self->login_data,
vn_login_on_server_changed, self);
vn_login_set_loading (self, FALSE);
return G_SOURCE_REMOVE;
}
/*
* Thread function that tries to open the connection asynchronously.
*/
static void vn_login_thread (ConnectData * connect_data)
{
VnLogin * self = connect_data->self;
// FIXME: Thread unsafe functions
const gchar * user = gvn_param_get_string (self->user);
const gchar * pass = gvn_param_get_string (self->pass);
const gchar * plugin = db_iterator_get_string (self->login_data, "plugin");
const gchar * host = db_iterator_get_string (self->login_data, "host");
const gchar * schema = db_iterator_get_string (self->login_data, "schema");
const gchar * ssl_ca = db_iterator_get_string (self->login_data, "ssl_ca");
if (IS_DEFINED (plugin) && IS_DEFINED (schema))
{
if (db_conn_load_plugin (connect_data->conn, plugin, &connect_data->error))
{
if (IS_DEFINED (ssl_ca))
db_conn_set_ssl (connect_data->conn, ssl_ca);
connect_data->connected = db_conn_open (connect_data->conn
,host
,schema
,user
,pass
,&connect_data->error
);
}
}
else
connect_data->error = g_error_new (
VN_LOGIN_LOG_DOMAIN
,VN_LOGIN_ERR_BAD_SETTINGS
,_("Bad connection settings, please check it.")
);
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc) vn_login_done, connect_data, (GDestroyNotify) connect_data_free);
}
/*
* Opens the connection.
*/
void vn_login_on_connect_clicked (GtkButton * button, VnLogin * self)
{
ConnectData * connect_data;
if (db_iterator_get_row (self->login_data) != -1)
{
vn_login_set_loading (self, TRUE);
connect_data = g_new (ConnectData, 1);
connect_data->self = g_object_ref (self);
connect_data->conn = db_conn_new ();
connect_data->connected = FALSE;
connect_data->error = NULL;
connect_data->thread = g_thread_new ("vn-login",
(GThreadFunc) vn_login_thread, connect_data);
}
else
{
GtkWidget * dialog = gtk_message_dialog_new (self->window
,GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
,GTK_MESSAGE_ERROR
,GTK_BUTTONS_OK
,_("Please select a connection")
);
gtk_window_set_title (GTK_WINDOW (dialog), _("Login error"));
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
}
}
//------------------------------------ Settings dialog
/*
* Shows the settings dialog.
*/
void vn_login_on_settings_clicked (GtkButton * button, VnLogin * self)
{
db_iterator_refresh (self->connections);
gtk_widget_show_all (GTK_WIDGET (self->settings_dialog));
gtk_dialog_run (self->settings_dialog);
}
/*
* Hides the settings dialog.
*/
void vn_login_hide_settings (VnLogin * self)
{
db_iterator_refresh (self->login_data);
gtk_widget_hide (GTK_WIDGET (self->settings_dialog));
}
/*
* Hides the settings dialog when close is clicked.
*/
void vn_login_on_settings_close_clicked (GtkButton * button, VnLogin * self)
{
vn_login_hide_settings (self);
}
/*
* Hides the settings dialog when escape is pressed.
*/
void vn_login_settings_on_delete_event (GtkWidget * settings_dialog, GdkEvent * event, VnLogin * self)
{
vn_login_hide_settings (self);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties
enum
{
PROP_APPLICATION = 1
};
static void vn_login_set_property (VnLogin * self, guint id,
const GValue * value, GParamSpec * pspec)
{
switch (id)
{
case PROP_APPLICATION:
{
self->app = g_value_dup_object (value);
g_object_connect (self->app
,"signal::startup", vn_login_on_startup, self
,"signal::activate", vn_login_on_activate, self
,NULL
);
// self->settings = g_settings_new (
// g_application_get_application_id (G_APPLICATION (self->app)));
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
}
}
static void vn_login_get_property (VnLogin * self, guint id,
GValue * value, GParamSpec * pspec)
{
switch (id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
static void vn_login_init (VnLogin * self)
{
self->gui = NULL;
self->window = NULL;
self->settings = NULL;
self->app = NULL;
self->cfg_conn = NULL;
self->builder = gtk_builder_new ();
}
static void vn_login_finalize (VnLogin * self)
{
vn_login_free_gui (self);
g_object_disconnect (self->app
,"any_signal", vn_login_on_startup, self
,"any_signal", vn_login_on_activate, self
,NULL
);
g_clear_object (&self->builder);
g_clear_object (&self->cfg_conn);
g_clear_object (&self->settings);
g_clear_object (&self->app);
G_OBJECT_CLASS (vn_login_parent_class)->finalize (G_OBJECT (self));
}
static void vn_login_class_init (VnLoginClass * k)
{
GObjectClass * klass = G_OBJECT_CLASS (k);
klass->finalize = (GObjectFinalizeFunc) vn_login_finalize;
klass->set_property = (GObjectSetPropertyFunc) vn_login_set_property;
klass->get_property = (GObjectGetPropertyFunc) vn_login_get_property;
g_object_class_install_property (klass, PROP_APPLICATION,
g_param_spec_object ("application"
,_("Application")
,_("The application")
,GTK_TYPE_APPLICATION
,G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY
));
}