/* This file is part of GNUnet. Copyright (C) 2010, 2011, 2012, 2013, 2017 GNUnet e.V. GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file src/setup/gnunet-setup.c * @brief Main function of gnunet-setup * @author Christian Grothoff */ #include "gnunet-setup.h" #include "gnunet-setup-options.h" #include #include #if ENABLE_NLS #include #endif /** * Main loop handle. */ static struct GNUNET_GTK_MainLoop *ml; /** * Name of the configuration file. */ const char *option_cfg_name; /** * Our configuration. */ struct GNUNET_CONFIGURATION_Handle *cfg; /** * Global return value (for success/failure of gnunet-setup). */ static int gret; #ifndef MINGW /** * Flag to enable privilege escalation. */ static int do_gksu; #endif /** * Get an object from the main window. * * @param name name of the object * @return NULL on error, otherwise the object */ GObject * GNUNET_SETUP_get_object (const char *name) { if (NULL == ml) return NULL; return GNUNET_GTK_main_loop_get_object (ml, name); } /** * User clicked on some widget, update the help button label and link. * * @param widget widget that was clicked on (unused) * @param event the current event * @param user_data context with the option specification to evaluate * @return FALSE (continue event handling) */ static gboolean help_click_callback (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { const struct GNUNET_SETUP_OptionSpecification *os = user_data; GtkLinkButton *help; if (GDK_BUTTON_PRESS != event->type) return FALSE; help = GTK_LINK_BUTTON (GNUNET_SETUP_get_object ("GNUNET_setup_help_text")); gtk_link_button_set_uri (help, os->help_url); gtk_button_set_label (GTK_BUTTON (help), os->help_text); return FALSE; } /** * The main visible page in our main notebook changed. If the * GNS page is visible, hide the help text, otherwise show it. */ void GNUNET_setup_notebook_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) { GtkWidget *help; GtkWidget *gnu; help = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_help_text")); gnu = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_vbox")); if (gnu == page) gtk_widget_hide (help); else gtk_widget_show (help); } /** * Change the visibility of widgets according to the * value and visibility specification given. * * @param os option specification * @param value current value for the given option */ static void update_visibility (const struct GNUNET_SETUP_OptionSpecification *os, const char *value) { unsigned int i; const struct GNUNET_SETUP_VisibilitySpecification *vs; GtkWidget *widget; regex_t r; if (NULL == os->visibility) return; i = 0; while (os->visibility[i].widget_name != NULL) { vs = &os->visibility[i]; widget = GTK_WIDGET (GNUNET_SETUP_get_object (vs->widget_name)); if (NULL == widget) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Widget `%s' not found\n"), vs->widget_name); } if (NULL != vs->show_value) { if (0 != regcomp (&r, vs->show_value, REG_EXTENDED | REG_ICASE | REG_NOSUB)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid regular expression `%s'\n"), vs->show_value); i++; continue; } if (0 == regexec (&r, value, 0, NULL, 0)) gtk_widget_show (widget); else gtk_widget_hide (widget); regfree (&r); } if (NULL != vs->hide_value) { if (0 != regcomp (&r, vs->hide_value, REG_ICASE | REG_NOSUB)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid regular expression `%s'\n"), vs->show_value); i++; continue; } if (0 == regexec (&r, value, 0, NULL, 0)) gtk_widget_hide (widget); else gtk_widget_show (widget); regfree (&r); } i++; } } /** * Function called whenever a widget changes its state. * * @param os details about the option */ static void widget_state_change_callback (const struct GNUNET_SETUP_OptionSpecification *os) { GObject *widget; char *value; widget = GNUNET_SETUP_get_object (os->widget_name); GNUNET_assert (NULL != os->save_function); if (GNUNET_OK != os->save_function (os->load_save_cls, os->section, os->option, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Failed to obtain option value from widget `%s'\n"), os->widget_name); return; } if (NULL != os->input_validation_function) os->input_validation_function (os->input_validation_function_cls, widget); if ((NULL != os->section) && (NULL != os->option)) { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, os->section, os->option, &value)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, os->section, os->option); value = GNUNET_strdup (""); } } else return; update_visibility (os, value); GNUNET_free (value); } /** * Load options into the main dialog. */ static void load_options () { const struct GNUNET_SETUP_OptionSpecification *os; GObject *widget; char *value; #ifndef LINUX gtk_widget_hide (GTK_WIDGET ( GNUNET_SETUP_get_object ( "GNUNET_setup_gns_hijack_checkbutton"))); #endif for (unsigned int i = 0; NULL != option_specifications[i].widget_name; i++) { os = &option_specifications[i]; widget = GNUNET_SETUP_get_object (os->widget_name); if (NULL == widget) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Widget `%s' not found\n"), os->widget_name); continue; } if (NULL != os->load_function) { if ((NULL == os->section) || (NULL == os->option)) { if (GNUNET_OK != os->load_function (os->load_save_cls, NULL, NULL, NULL, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Failed to initialize widget `%s'\n"), os->widget_name); } } else { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, os->section, os->option, &value)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ( "No default value known for option `%s' in section `%s'\n"), os->option, os->section); } else { if (GNUNET_OK != os->load_function (os->load_save_cls, os->section, os->option, value, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ( "Failed to initialize widget `%s' with value `%s'\n"), os->widget_name, value); } else { update_visibility (os, value); } GNUNET_free (value); } } } if (NULL != os->input_validation_function) os->input_validation_function (os->input_validation_function_cls, widget); if (os->help_text != NULL) { g_signal_connect (widget, "button-press-event", G_CALLBACK (&help_click_callback), (void *) os); } if (NULL != os->change_signal) { GNUNET_assert (NULL != os->save_function); g_signal_connect_swapped (widget, os->change_signal, G_CALLBACK (&widget_state_change_callback), (void *) os); } } } /** * Write final configuration to disk. * * @return #GNUNET_OK on success */ static int write_configuration () { struct GNUNET_CONFIGURATION_Handle *cfgDefault; int ret; cfgDefault = GNUNET_CONFIGURATION_create (); (void) GNUNET_CONFIGURATION_load (cfgDefault, NULL); /* load defaults only */ ret = GNUNET_CONFIGURATION_write_diffs (cfgDefault, cfg, option_cfg_name); GNUNET_CONFIGURATION_destroy (cfgDefault); return ret; } /** * Method run on shutdown. * * @param cls the main loop handle */ static void cleanup_task (void *cls) { if (NULL == ml) { GNUNET_break (0); return; } GNUNET_GTK_main_loop_quit (ml); ml = NULL; if (GNUNET_OK != write_configuration ()) gret = 1; cfg = NULL; } /** * Callback invoked if the application is supposed to exit. */ void GNUNET_SETUP_quit_cb (GObject *object, gpointer user_data) { GNUNET_SCHEDULER_shutdown (); } #ifndef MINGW /** * Try elevating user privileges to run as user 'gnunet' or 'root'. * * @param username user gnunet-setup should be run as * @return #GNUNET_OK on success */ static int try_gksu (const char *username) { struct GNUNET_OS_Process *proc; proc = GNUNET_OS_start_process ( GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, "gksu", "-u", username, "-m", _ ( "Enter YOUR password to run gnunet-setup as user 'gnunet' (assuming 'sudo' allows it)"), "-D", _ ( "Enter YOUR password to run gnunet-setup as user 'gnunet' (assuming 'sudo' allows it)"), NULL); if (NULL == proc) return GNUNET_SYSERR; GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); return GNUNET_OK; } #endif /** * User clicked on the button to edit the list of friends. * Launch gnunet-peerinfo-gtk. */ void GNUNET_setup_launch_edit_friends_button_clicked_cb (GtkButton *button, gpointer *user_data) { struct GNUNET_OS_Process *proc; proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, "gnunet-peerinfo-gtk", "-c", option_cfg_name, NULL); if (NULL == proc) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to launch gnunet-peerinfo-gtk\n")); return; } /* simply yield control, let it run */ GNUNET_free (proc); } /** * Actual main method that sets up the configuration window. * * @param cls the main loop handle */ static void run (void *cls) { GtkWidget *main_window; #ifndef MINGW uid_t my_uid; struct passwd *gnunet_pw; my_uid = getuid (); gnunet_pw = getpwnam ("gnunet"); if ((0 != do_gksu) && (0 != my_uid) && (NULL != gnunet_pw) && (my_uid != gnunet_pw->pw_uid) && (GNUNET_OK == try_gksu ("gnunet"))) { GNUNET_GTK_main_loop_quit (cls); return; } #endif ml = cls; if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL)) return; option_cfg_name = GNUNET_GTK_main_loop_get_configuration_file (ml); cfg = GNUNET_CONFIGURATION_create (); if (GNUNET_YES == GNUNET_DISK_file_test (option_cfg_name)) (void) GNUNET_CONFIGURATION_load (cfg, option_cfg_name); else (void) GNUNET_CONFIGURATION_load (cfg, NULL); if (0 != access (option_cfg_name, W_OK)) { if (ENOENT == errno) { char *dirname; size_t len; (void) GNUNET_DISK_directory_create_for_file (option_cfg_name); dirname = GNUNET_STRINGS_filename_expand (option_cfg_name); len = strlen (dirname) - 1; while ((len > 0) && (dirname[len] != DIR_SEPARATOR)) len--; dirname[len] = '\0'; if (0 != access (dirname, X_OK | W_OK | R_OK)) { if (ENOENT == errno) errno = EACCES; /* really means somewhere access was denied */ } else errno = ENOENT; /* all good now */ } if (ENOENT != errno) { fprintf ( stderr, "Refusing to start as I will be unable to write configuration file `%s': %s\n", option_cfg_name, strerror (errno)); GNUNET_GTK_main_loop_quit (ml); return; } } main_window = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_dialog")); load_options (); GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); gtk_widget_show (main_window); gtk_window_present (GTK_WINDOW (main_window)); } /** * Main function for gnunet-setup. * * @param argc number of arguments * @param argv arguments * @return 0 on success */ int main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { #ifndef MINGW GNUNET_GETOPT_option_flag ( 'e', "elevate-privileges", gettext_noop ( "run as user 'gnunet', if necessary by executing gksu to elevate rights"), &do_gksu), #endif GNUNET_GETOPT_OPTION_END }; int ret; if (GNUNET_OK == GNUNET_GTK_main_loop_start ("gnunet-setup", "gnunet-setup", argc, argv, options, "gnunet_setup_main_window.glade", &run)) ret = gret; else ret = 1; return ret; } /* end of gnunet-setup.c */