/* This file is part of GNUnet. Copyright (C) 2010-2013 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/fs/gnunet-fs-gtk.c * @brief Main function of gnunet-fs-gtk * @author Christian Grothoff */ #include "gnunet-fs-gtk.h" #include "gnunet-fs-gtk_common.h" #include "gnunet-fs-gtk_event-handler.h" #include "gnunet-fs-gtk_open-uri.h" #include "gnunet/gnunet_namestore_service.h" #include #if HAVE_LIBUNIQUE #include #endif #include /** * How many block requests can we have outstanding in parallel at a time by * default? */ #define DEFAULT_MAX_PARALLEL_REQUESTS 100000 /** * How many downloads can we have outstanding in parallel at a time by default? */ #define DEFAULT_MAX_PARALLEL_DOWNLOADS 128 /** * The GNUNET_GTK_file_sharing_downloads_tree_store with the * information about active (or completed) downloads. */ GtkTreeStore *downloads_treestore; /** * Handle to our main loop. */ static struct GNUNET_GTK_MainLoop *ml; /** * Handle for file-sharing operations. */ static struct GNUNET_FS_Handle *fs; /** * Handle for ARM monitoring. */ static struct GNUNET_ARM_MonitorHandle *armon; /** * Handle for ARM controlling. */ static struct GNUNET_ARM_Handle *arm; /** * Ongoing identity operation. */ static struct GNUNET_IDENTITY_Operation *iop; /** * Context for main window. */ static struct GNUNET_GTK_MainWindowContext main_context; /** * Identity combo box in the main window. */ static GtkComboBox *id_combo_box; /** * Currently selected entry in #id_liststore. */ static GtkTreeIter id_iter; /** * List of all known egos. */ static GtkListStore *id_liststore; /** * Status label in main window. */ static GtkLabel *status_label; /** * Are we shutting down? */ static int in_shutdown = 0; /** * Columns in the id list store. */ enum ID_COLUMNS { /** * A gchararray */ ID_LS_NAME = 0, /** * A `struct GNUNET_IDENTITY_Ego` */ ID_LS_EGO = 1 }; #if HAVE_LIBUNIQUE static UniqueApp *unique_app; #endif struct GNUNET_GTK_MainWindowContext * GNUNET_FS_GTK_get_main_context () { return &main_context; } /** * Return handle for file-sharing operations. * * @return NULL on error */ struct GNUNET_FS_Handle * GNUNET_FS_GTK_get_fs_handle () { return fs; } /** * Get an object from the main window. * * @param name name of the object * @return NULL on error, otherwise the object */ static GObject * get_object (const char *name) { if (NULL == ml) return NULL; return GNUNET_GTK_main_loop_get_object (ml, name); } /** * Remember FS handle if we don't have one yet. * * @param fsh file sharing handle to use */ void GNUNET_FS_GTK_set_fs_handle (struct GNUNET_FS_Handle *fsh) { if (NULL == fs) fs = fsh; } /** * Get our configuration. * * @return configuration handle */ const struct GNUNET_CONFIGURATION_Handle * GNUNET_FS_GTK_get_configuration () { return GNUNET_GTK_main_loop_get_configuration (ml); } /** * Get an object from the main window. * * @param name name of the object * @return NULL on error */ GObject * GNUNET_FS_GTK_get_main_window_object (const char *name) { return GNUNET_GTK_main_loop_get_object (ml, name); } /** * Return the list store with anonymity levels. * * @return the list store */ GtkTreeModel * GNUNET_FS_GTK_get_anonymity_level_list_store () { return GTK_TREE_MODEL ( GNUNET_FS_GTK_get_main_window_object ("anonymity_level_liststore")); } /** * Obtains main window position and size before it's destroyed * and saves these into user's config file. * * @param main_window main window widget */ static void main_window_save_position (GtkWidget *main_window) { GdkWindow *main_window_gdk; gint window_x; gint window_y; gint window_width; gint window_height; int maximized; GdkWindowState window_state; struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_CONFIGURATION_Handle *cfgDefault; cfg = (struct GNUNET_CONFIGURATION_Handle *) GNUNET_GTK_main_loop_get_configuration (ml); main_window_gdk = gtk_widget_get_window (main_window); maximized = GNUNET_YES; if (NULL != main_window_gdk) { window_state = gdk_window_get_state (main_window_gdk); if (! (window_state & GDK_WINDOW_STATE_MAXIMIZED)) maximized = GNUNET_NO; } gtk_window_get_position (GTK_WINDOW (main_window), &window_x, &window_y); gtk_window_get_size (GTK_WINDOW (main_window), &window_width, &window_height); GNUNET_CONFIGURATION_set_value_number (cfg, "gnunet-fs-gtk", "MAIN_WINDOW_X", window_x); GNUNET_CONFIGURATION_set_value_number (cfg, "gnunet-fs-gtk", "MAIN_WINDOW_Y", window_y); GNUNET_CONFIGURATION_set_value_number (cfg, "gnunet-fs-gtk", "MAIN_WINDOW_WIDTH", window_width); GNUNET_CONFIGURATION_set_value_number (cfg, "gnunet-fs-gtk", "MAIN_WINDOW_HEIGHT", window_height); GNUNET_CONFIGURATION_set_value_string (cfg, "gnunet-fs-gtk", "MAIN_WINDOW_MAXIMIZED", (maximized == GNUNET_YES) ? "YES" : "NO"); cfgDefault = GNUNET_CONFIGURATION_create (); (void) GNUNET_CONFIGURATION_load (cfgDefault, NULL); /* load defaults only */ GNUNET_CONFIGURATION_write_diffs (cfgDefault, cfg, GNUNET_GTK_main_loop_get_configuration_file ( ml)); GNUNET_CONFIGURATION_destroy (cfgDefault); } /** * Obtains main window position and size before it's destroyed * and saves these into user's config file. * * @param main_window main window widget * @param event the event that we are handling * @param user_data main window context */ gboolean GNUNET_GTK_main_window_configure_event_cb (GtkWidget *main_window, GdkEventConfigure *event, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_context = user_data; main_window_save_position (main_context->main_window); return FALSE; } /** * Task run on shutdown. * * @param cls NULL */ static void shutdown_task (void *cls) { struct SearchLookup *sl; struct PseuLookupContext *lctx; struct SearchResult *sr; in_shutdown = 1; while (NULL != (sl = main_context.sl_head)) abort_search_lookup (sl); while (NULL != (lctx = main_context.lctx_head)) abort_pseu_lookup (lctx); while (NULL != (sr = pl_head)) { GNUNET_FS_probe_stop (sr->probe); sr->probe = NULL; GNUNET_CONTAINER_DLL_remove (pl_head, pl_tail, sr); } if (NULL != fs) { GNUNET_FS_stop (fs); fs = NULL; } if (NULL != iop) { GNUNET_IDENTITY_cancel (iop); iop = NULL; } if (NULL != main_context.identity) { GNUNET_IDENTITY_disconnect (main_context.identity); main_context.identity = NULL; } if (NULL != main_context.zm) { GNUNET_NAMESTORE_zone_monitor_stop (main_context.zm); main_context.zm = NULL; } if (NULL != main_context.gns) { GNUNET_GNS_disconnect (main_context.gns); main_context.gns = NULL; } if (NULL != armon) { GNUNET_ARM_monitor_stop (armon); armon = NULL; } if (NULL != arm) { GNUNET_ARM_disconnect (arm); arm = NULL; } GNUNET_FS_GTK_close_uri_tab_ (); if (NULL != ml) { GNUNET_GTK_main_loop_quit (ml); ml = NULL; } } /** * Callback invoked if the application is supposed to exit. * * @param object origin of the quit event, unused * @param user_data global builder instance, unused */ void GNUNET_FS_GTK_menu_quit_activate_cb (GtkMenuItem *object, gpointer user_data) { GNUNET_SCHEDULER_shutdown (); } /** * Callback invoked if the application is supposed to exit. * * @param object origin of the quit event, unused * @param event the delete event * @param user_data global builder instance, unused */ void GNUNET_FS_GTK_delete_event_cb (GtkWidget *object, GdkEvent *event, gpointer user_data) { /* GNUNET_FS_GTK_delete_event_cb will eventually be called if we shut down * the scheduler, because shutting it down will make GTK delete the main * window. On the other hand, deleting the main window first (clicking on X * button) will not trigger scheduler shutdown, unlike * GNUNET_FS_GTK_menu_quit_activate_cb(). So we shut it down here again, * just to be sure it is dead (if it isn't, application will hang up). */ GNUNET_SCHEDULER_shutdown (); } /** * Text was pasted into the main window. Check if it is a URL * and perform the appropriate action. * * @param cb source clipboard * @param text pasted text * @param data NULL */ static void process_paste (GtkClipboard *cb, const gchar *text, gpointer data) { struct GNUNET_FS_Uri *kskuri; char *emsg; if (strlen (text) == 0) return; if (GNUNET_OK == GNUNET_FS_GTK_handle_uri_string (text, 1)) return; emsg = NULL; kskuri = GNUNET_FS_uri_ksk_create (text, &emsg); if (NULL == kskuri) { GNUNET_free (emsg); return; } GNUNET_FS_GTK_handle_uri (kskuri, 1); GNUNET_FS_uri_destroy (kskuri); } /** * We got an event in the main window. Check for clipboard action. * * @param widget the main window * @param event the event, we only care about button events * @param user_data the 'struct SearchTab' the widget is in * @return FALSE if no menu could be popped up, * TRUE if there is now a pop-up menu */ gboolean GNUNET_FS_GTK_main_window_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { GdkEventButton *event_button = (GdkEventButton *) event; GtkClipboard *cb; if ((GDK_BUTTON_PRESS != event->type) || (2 != event_button->button)) return FALSE; cb = gtk_clipboard_get (gdk_atom_intern ("PRIMARY", FALSE)); gtk_clipboard_request_text (cb, &process_paste, NULL); return FALSE; } #if HAVE_LIBUNIQUE /** * Function called whenever a second gnunet-fs-gtk process is started * with additional arguments for us. * * @param app unique handle * @param command command that was given * @param message_data command line message data * @param time_ timestamp of the event * @param user_data our 'main context' * @return response code for original process */ static UniqueResponse unique_app_message_cb (UniqueApp *app, gint command, UniqueMessageData *message_data, guint time_, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_context = user_data; /* raise the window */ if (GTK_IS_WINDOW (main_context->main_window)) gtk_window_present_with_time (GTK_WINDOW (main_context->main_window), time_); switch (command) { case UNIQUE_NEW: /* this is unexpected... */ GNUNET_break (0); break; case UNIQUE_OPEN: { gchar **uris; gint n_uris; gint i; uris = unique_message_data_get_uris (message_data); n_uris = g_strv_length (uris); for (i = 0; i < n_uris; i++) { if (GNUNET_OK != GNUNET_FS_GTK_handle_uri_string (uris[i], 1 /* anonymity level */)) return UNIQUE_RESPONSE_PASSTHROUGH; } g_strfreev (uris); } break; case UNIQUE_ACTIVATE: break; default: break; } return UNIQUE_RESPONSE_OK; } #endif static char * format_service_list (unsigned int count, const struct GNUNET_ARM_ServiceInfo *list) { size_t len = 0; int i; const char *header = _ ("GNUnet node appears to be on."); const char *listheader = _ ("Currently running services:\n"); char *result; char *p; int r; len = strlen (header) + 1; if (list) { len += strlen (listheader); for (i = 0; i < count; i++) len += strlen (list[i].name) + 1; } len += 1; result = p = GNUNET_malloc (len * sizeof (char)); r = GNUNET_snprintf (p, (size_t) (result + len - p), "%s\n", header); if (r < 0) { GNUNET_free (result); return NULL; } p += r; if (list) { r = GNUNET_snprintf (p, (size_t) (result + len - p), "%s", listheader); if (r < 0) { GNUNET_free (result); return NULL; } p += r; for (i = 0; i < count; i++) { size_t l = strlen (list[i].name); memcpy (p, list[i].name, l); p += l; if (i + 1 < count) { p[0] = '\n'; p += 1; } } } p[0] = '\0'; return result; } static void service_list_callback (void *cls, enum GNUNET_ARM_RequestStatus rs, unsigned int count, const struct GNUNET_ARM_ServiceInfo *list) { char *service_list; if ((GNUNET_ARM_REQUEST_SENT_OK != rs) || (NULL == list)) return; service_list = format_service_list (count, list); GNUNET_FS_GTK_update_connection_indicator (cls, TRUE, service_list); GNUNET_free (service_list); } static void arm_connection_state_change (void *cls, int connected) { char *service_list; if (connected) { service_list = format_service_list (0, NULL); GNUNET_FS_GTK_update_connection_indicator (cls, TRUE, service_list); GNUNET_free (service_list); GNUNET_ARM_request_service_list (arm, &service_list_callback, cls); } else GNUNET_FS_GTK_update_connection_indicator ( cls, FALSE, _ ("Can't connect to the Automatic Restart Manager service.")); } static void service_status_change (void *cls, const char *service, enum GNUNET_ARM_ServiceMonitorStatus status) { /* Very crude, we can probably do better. * Maybe keep a list of running services, and modify it in response * to service status changes, then update the indicator, * without requesting a list from ARM every goddamned time? */ GNUNET_ARM_request_service_list (arm, &service_list_callback, cls); } static void monitor_zone_error (void *cls) { GtkListStore *ls; ls = GTK_LIST_STORE ( GNUNET_FS_GTK_get_main_window_object ("namespace_label_liststore")); gtk_list_store_clear (ls); gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_combobox"))); } static void monitor_zone_sync (void *cls) { gtk_widget_show (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_combobox"))); } /** * Process a record that was stored in the namestore in the * "sks_zone". Adds (or removes) the respective label to the * drop-down menu for the SKS search by manipulating the * "namespace_label_liststore". * * @param cls closure * @param zone private key of the zone; NULL on disconnect * @param label label of the records; NULL on disconnect * @param rd_count number of entries in @a rd array; 0 on removal * @param rd array of records for the label */ static void monitor_zone_records (void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { GtkListStore *ls; GtkTreeModel *tm; GtkTreeIter iter; gchar *id; gchar *label_gnu; GNUNET_NAMESTORE_zone_monitor_next (main_context.zm, 1); ls = GTK_LIST_STORE ( GNUNET_FS_GTK_get_main_window_object ("namespace_label_liststore")); label_gnu = g_strdup_printf ("%s.%s", label, "gnu"); if (0 == rd_count) { tm = GTK_TREE_MODEL (ls); if (gtk_tree_model_get_iter_first (tm, &iter)) do { gtk_tree_model_get (tm, &iter, 0, &id, -1); if (0 == strcmp (id, label_gnu)) { GNUNET_assert (gtk_list_store_remove (ls, &iter)); g_free (id); g_free (label_gnu); return; } g_free (id); } while (gtk_tree_model_iter_next (tm, &iter)); GNUNET_break (0); return; } gtk_list_store_insert_with_values (ls, &iter, -1, 0, label_gnu, -1); g_free (label_gnu); } /** * The identity service has given us the ego we should use for resolving * namepace names. * * @param cls closure * @param ego ego handle, NULL for none * @param ctx context for application to store data for this ego * (during the lifetime of this process, initially NULL) * @param name name assigned by the user for this ego, * NULL if the user just deleted the ego and it * must thus no longer be used */ static void handle_sks_zone_identity (void *cls, struct GNUNET_IDENTITY_Ego *ego) { main_context.id_op = NULL; if (NULL == ego) { GNUNET_log ( GNUNET_ERROR_TYPE_WARNING, _ ( "No default ego specified for `fs-sks` service, will not enable namespace search.\n")); return; } main_context.sks_zone = ego; gtk_widget_show (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_label"))); gtk_widget_show (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_combobox"))); if (NULL != main_context.zm) { GNUNET_NAMESTORE_zone_monitor_stop(main_context.zm); main_context.zm = NULL; } main_context.zm = GNUNET_NAMESTORE_zone_monitor_start (main_context.cfg, GNUNET_IDENTITY_ego_get_private_key ( main_context.sks_zone), GNUNET_YES, &monitor_zone_error, NULL, &monitor_zone_records, NULL, &monitor_zone_sync, NULL); } /** * The user selected another identity in the combobox. Load it. * * @param widget the combo box where the selection was changed * @param user_data the builder, unused */ void gnunet_fs_gtk_id_combobox_changed_cb (GtkComboBox *widget, gpointer user_data) { GtkTreeIter iter; struct GNUNET_IDENTITY_Ego *ego; char *name; (void) user_data; if (! gtk_combo_box_get_active_iter (id_combo_box, &iter)) { return; } id_iter = iter; /* clang-format off */ gtk_tree_model_get (GTK_TREE_MODEL (id_liststore), &iter, ID_LS_NAME, &name, ID_LS_EGO, &ego, -1); /* clang-format on */ handle_sks_zone_identity (name, ego); } /** * Method called to inform about the egos of this peer. Updates the * `zone_liststore`. * * When used with #GNUNET_IDENTITY_connect, this function is * initially called for all egos and then again whenever a * ego's name changes or if it is deleted. At the end of * the initial pass over all egos, the function is once called * with 'NULL' for @a ego. That does NOT mean that the callback won't * be invoked in the future or that there was an error. * * If @a ego is non-NULL and if '*ctx' is set in those callbacks, the * value WILL be passed to a subsequent call to the identity callback * of #GNUNET_IDENTITY_connect (if that one was not NULL). * * When an identity is renamed, this function is called with the * (known) @a ego but the NEW @a name. * * When an identity is deleted, this function is called with the * (known) ego and "NULL" for the @a name. In this case, * the @a ego is henceforth invalid (and the @a ctx should also be * cleaned up). * * @param cls closure * @param ego ego handle * @param ctx context for application to store data for this ego * (during the lifetime of this process, initially NULL) * @param name name assigned by the user for this ego, * NULL if the user just deleted the ego and it * must thus no longer be used */ static void identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx, const char *name) { GtkTreeRowReference *rr; GtkTreeIter iter; GtkTreePath *path; if (in_shutdown) return; if ((NULL == ego) && (NULL == name) && (NULL == ctx)) { /* end of initial iteration, trigger loading selected zone */ gnunet_fs_gtk_id_combobox_changed_cb (id_combo_box, ml); return; } rr = *ctx; if (NULL == rr) { /* clang-format off */ gtk_list_store_insert_with_values (id_liststore, &iter, 0, ID_LS_NAME, name, ID_LS_EGO, ego, -1); /* clang-format on */ gtk_combo_box_set_active_iter (id_combo_box, &iter); gtk_widget_set_sensitive (GTK_WIDGET (id_combo_box), TRUE); path = gtk_tree_model_get_path (GTK_TREE_MODEL (id_liststore), &iter); rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (id_liststore), path); *ctx = rr; gtk_tree_path_free (path); return; } path = gtk_tree_row_reference_get_path (rr); GNUNET_break ( gtk_tree_model_get_iter (GTK_TREE_MODEL (id_liststore), &iter, path)); gtk_tree_path_free (path); if (NULL == name) { GtkTreeIter act_iter; /* identity was removed, remove from list */ gtk_list_store_remove (id_liststore, &iter); if (! gtk_combo_box_get_active_iter (id_combo_box, &act_iter)) { if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (id_liststore), &act_iter)) { /* make sure combo box remains selected if possible */ gtk_combo_box_set_active (id_combo_box, 0); } else { /* make combo box insensitive if nothing can be selected */ gtk_widget_set_sensitive (GTK_WIDGET (id_combo_box), FALSE); } } return; } /* identity was renamed, rename in model */ gtk_list_store_set (id_liststore, &iter, ID_LS_NAME, name, -1); } /** * Actual main function run right after GNUnet's scheduler * is initialized. Initializes up GTK and Glade. * * @param cls handle to the main loop (`struct GNUNET_GTK_MainLoop`) */ static void run (void *cls) { unsigned long long dl_parallel; unsigned long long req_parallel; unsigned long long window_x; unsigned long long window_y; unsigned long long window_width; unsigned long long window_height; int maximized; ml = cls; /* setup main context */ if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, &main_context)) return; main_context.builder = GNUNET_GTK_main_loop_get_builder (ml); main_context.cfg = GNUNET_GTK_main_loop_get_configuration (ml); main_context.search_ns_treestore = GTK_TREE_STORE (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_treestore")); main_context.main_window = GTK_WIDGET ( GNUNET_FS_GTK_get_main_window_object ("GNUNET_GTK_main_window")); main_context.ns_selector_treeview = GTK_TREE_VIEW ( GNUNET_FS_GTK_get_main_window_object ("namespace_selector_treeview")); main_context.ns_selector_window = GTK_WIDGET ( GNUNET_FS_GTK_get_main_window_object ("namespace_selector_window")); main_context.ns_dropdown_button = GTK_TOGGLE_BUTTON (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_namespace_dropdown_button")); main_context.search_ns_label = GTK_LABEL (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_selected_namespace_label")); main_context.search_entry = GTK_ENTRY ( GNUNET_FS_GTK_get_main_window_object ("main_window_search_entry")); downloads_treestore = GTK_TREE_STORE (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_file_sharing_downloads_tree_store")); main_context.anonymity_combo = GTK_COMBO_BOX (GNUNET_FS_GTK_get_main_window_object ( "main_window_search_anonymity_combobox")); main_context.anonymity_level_liststore = GTK_LIST_STORE ( GNUNET_FS_GTK_get_main_window_object ("anonymity_level_liststore")); main_context.preview_image = GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_main_window_preview_image")); main_context.md_liststore = GTK_LIST_STORE ( GNUNET_FS_GTK_get_main_window_object ("GNUNET_GTK_meta_data_list_store")); main_context.md_treeview = GTK_TREE_VIEW (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_main_window_metadata_treeview")); main_context.ns_discovery_handle = NULL; main_context.download_location_chooser = GTK_FILE_CHOOSER (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_search_frame_download_location_chooser")); main_context.download_name_entry = GTK_ENTRY (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_search_frame_download_filename_entry")); main_context.download_anonymity_combo = GTK_COMBO_BOX (GNUNET_FS_GTK_get_main_window_object ( "main_window_download_anonymity_combobox")); main_context.download_recursive_checkbutton = GTK_CHECK_BUTTON (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_search_frame_download_recursive_checkbox")); main_context.download_download_button = GTK_BUTTON (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_search_frame_download_download_button")); main_context.download_panel = GTK_BOX (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_GTK_search_frame_download_vbox")); main_context.notebook = GTK_NOTEBOOK ( GNUNET_FS_GTK_get_main_window_object ("GNUNET_GTK_main_window_notebook")); main_context.connection_indicator = GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object ( "GNUNET_FS_GTK_main_window_connection_indicator")); status_label = GTK_LABEL (get_object ( "gnunet_fs_gtk_status_label")); id_combo_box = GTK_COMBO_BOX (get_object ("gnunet_fs_gtk_id_combobox")); id_liststore = GTK_LIST_STORE (get_object ("id_liststore")); GNUNET_GTK_set_icon_search_path (); GNUNET_GTK_setup_nls (); /* Make sure button class is realized */ g_type_class_unref (g_type_class_ref (GTK_TYPE_BUTTON)); /* GNUnet main window assumes that images on buttons are visible, * override the theme's gtkrc setting */ g_object_set (gtk_settings_get_default (), "gtk-button-images", TRUE, NULL); /* setup main window */ maximized = GNUNET_CONFIGURATION_get_value_yesno (main_context.cfg, "gnunet-fs-gtk", "MAIN_WINDOW_MAXIMIZED"); if (GNUNET_SYSERR == maximized) maximized = GNUNET_YES; if ((GNUNET_NO == maximized) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAIN_WINDOW_X", &window_x)) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAIN_WINDOW_Y", &window_y)) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAIN_WINDOW_WIDTH", &window_width)) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAIN_WINDOW_HEIGHT", &window_height))) { gtk_window_move (GTK_WINDOW (main_context.main_window), window_x, window_y); gtk_window_resize (GTK_WINDOW (main_context.main_window), window_width, window_height); } else { /* If anything is wrong - play safe and show it maximized */ gtk_window_maximize (GTK_WINDOW (main_context.main_window)); } /* Allow multiple selection in metadata view; */ /* FIXME-GTK3: this can be done within (modern versions of) glade (However, this needs GTK >= 3.2, we currently target 3.0) */ gtk_tree_selection_set_mode (gtk_tree_view_get_selection ( main_context.md_treeview), GTK_SELECTION_MULTIPLE); /* FIXME: should these '1's be here? Maybe better to put them into * default config files? */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAX_PARALLEL_DOWNLOADS", &dl_parallel)) dl_parallel = DEFAULT_MAX_PARALLEL_DOWNLOADS; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (main_context.cfg, "gnunet-fs-gtk", "MAX_PARALLEL_REQUESTS", &req_parallel)) req_parallel = DEFAULT_MAX_PARALLEL_REQUESTS; /* initialize file-sharing */ fs = GNUNET_FS_start (main_context.cfg, "gnunet-fs-gtk", &GNUNET_GTK_fs_event_handler, NULL, GNUNET_FS_FLAGS_PERSISTENCE | GNUNET_FS_FLAGS_DO_PROBES, GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM, (unsigned int) dl_parallel, GNUNET_FS_OPTIONS_REQUEST_PARALLELISM, (unsigned int) req_parallel, GNUNET_FS_OPTIONS_END); if (NULL == fs) { GNUNET_GTK_main_loop_quit (cls); return; } arm = GNUNET_ARM_connect (main_context.cfg, &arm_connection_state_change, &main_context); armon = GNUNET_ARM_monitor_start (main_context.cfg, &service_status_change, &main_context); main_context.gns = GNUNET_GNS_connect (main_context.cfg); main_context.identity = GNUNET_IDENTITY_connect (main_context.cfg, &identity_cb, NULL); #if HAVE_LIBUNIQUE unique_app_watch_window (unique_app, GTK_WINDOW (main_context.main_window)); g_signal_connect (unique_app, "message-received", G_CALLBACK (unique_app_message_cb), &main_context); #endif /* make GUI visible */ gtk_widget_show (main_context.main_window); gtk_window_present (GTK_WINDOW (main_context.main_window)); { char *const *argv; int argc; int i; GNUNET_GTK_main_loop_get_args (ml, &argc, &argv); for (i = 0; i < argc; i++) { if (GNUNET_OK != GNUNET_FS_GTK_handle_uri_string (argv[i], 1 /* anonymity level */)) fprintf (stderr, "Invalid URI `%s'\n", argv[i]); } } GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); } int main (int argc, char **argv) { static struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_OPTION_END }; #if HAVE_LIBUNIQUE int arge; gtk_init (&argc, &argv); unique_app = unique_app_new ("org.gnunet.gnunet-fs-gtk", NULL); if (unique_app_is_running (unique_app)) { UniqueResponse response; arge = GNUNET_GETOPT_run ("gnunet-fs-gtk", options, argc, argv); response = unique_app_send_message (unique_app, UNIQUE_ACTIVATE, NULL); while (arge < argc) { UniqueMessageData *msg; msg = unique_message_data_new (); unique_message_data_set_text (msg, argv[arge], strlen (argv[arge]) + 1); if (UNIQUE_RESPONSE_OK == response) response = unique_app_send_message (unique_app, UNIQUE_OPEN, msg); unique_message_data_free (msg); arge++; } g_object_unref (unique_app); return (UNIQUE_RESPONSE_OK == response) ? 0 : 1; } #endif if (GNUNET_OK != GNUNET_GTK_main_loop_start ("gnunet-fs-gtk", "GTK GUI for GNUnet", argc, argv, options, "gnunet_fs_gtk_main_window.glade", &run)) { #if HAVE_LIBUNIQUE g_object_unref (unique_app); #endif return 1; } #if HAVE_LIBUNIQUE g_object_unref (unique_app); #endif return 0; } /* end of gnunet-fs-gtk.c */