/* * 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 . */ #include "vn-login.h" #include "vn-set.h" #include "vn-gui-private.h" #include #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, "gtk-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 = gvn_value_get_string (db_iterator_get_value (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)) { GValue encoded = G_VALUE_INIT; g_value_init (&encoded, G_TYPE_STRING); g_value_take_string (&encoded, gvn_encode (gvn_param_get_string (self->pass))); db_iterator_set_value (self->login_data, "password", &encoded, NULL); g_value_unset (&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 = gvn_value_get_string (db_iterator_get_value (self->login_data, "plugin")); const gchar * host = gvn_value_get_string (db_iterator_get_value (self->login_data, "host")); const gchar * schema = gvn_value_get_string (db_iterator_get_value (self->login_data, "schema")); const gchar * ssl_ca = gvn_value_get_string (db_iterator_get_value (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 )); }