617 lines
15 KiB
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
|
|
));
|
|
}
|