/* This file is part of GNUnet. (C) 2010-2014 Christian Grothoff (and other contributing authors) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/peerinfo/gnunet-peerinfo-gtk.c * @brief Main function of gnunet-peerinfo-gtk * @author Christian Grothoff */ #include "gnunet_gtk.h" #include #include #include #include #include #include "gnunet-peerinfo-gtk-flags.h" #if HAVE_LIBUNIQUE #include #endif /** * Columns in the peerinfo model. */ enum PEERINFO_ModelColumns { /** * A gchararray */ PEERINFO_MC_PEER_IDENTITY_STRING = 0, /** * A gchararray */ PEERINFO_MC_COUNTRY_NAME = 1, /** * A GdkPixbuf */ PEERINFO_MC_COUNTRY_FLAG = 2, /** * A guint */ PEERINFO_MC_BANDWIDTH_IN = 3, /** * A guint */ PEERINFO_MC_BANDWIDTH_OUT = 4, /** * A GdkPixbuf */ PEERINFO_MC_ATS_CONNECTIVITY_LED = 5, /** * A gboolean */ PEERINFO_MC_ATS_SELECTED_STATUS = 6, /** * A GdkPixbuf */ PEERINFO_MC_CORE_CONNECTIVITY_LED = 7, /** * A gboolean */ PEERINFO_MC_CORE_CONNECTED_STATUS = 8, /** * A gboolean */ PEERINFO_MC_IS_FRIEND = 9, /** * A `struct PeerInfo *` */ PEERINFO_MC_PEERINFO = 10, /** * A gchararray */ PEERINFO_MC_PLUGIN_NAME = 11, /** * A gchararray */ PEERINFO_MC_ADDRESS_AS_STRING = 12, /** * A GdkPixbuf */ PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_LED = 13, /** * A gboolean */ PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS = 14, /** * A gchararray */ PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING = 15, /** * A gchararray */ PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING = 16, /** * A gchararray */ PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING = 17, /** * A GdkPixbuf */ PEERINFO_MC_VALIDATION_STATE_LED = 18, /** * A gboolean */ PEERINFO_MC_VALIDATION_IS_VALID = 19, /** * A gchararray */ PEERINFO_MC_VALIDATION_TIMEOUT_AS_STRING = 20, /** * A GdkPixbuf */ PEERINFO_MC_PLUGIN_CONNECTIVITY_LED = 21, /** * A gboolean */ PEERINFO_MC_PLUGIN_CONNECTIVITY_STATUS = 22, /** * A gchararray */ PEERINFO_MC_PLUGIN_CONNECTIVITY_TIMEOUT_AS_STRING = 23, /** * A gboolean */ PEERINFO_MC_SHOW_FRIEND = 24, /** * A gchararray */ PEERINFO_MC_PEERINFO_ADDRESS_EXPIRATION = 25 }; /** * Information about an address of the peer. */ struct PeerAddress { /** * DLL. */ struct PeerAddress *next; /** * DLL. */ struct PeerAddress *prev; /** * Reference to the peer in the view. */ GtkTreeRowReference *rr; /** * Handle for address to string conversion. */ struct GNUNET_TRANSPORT_AddressToStringContext *tos; /** * Binary address, allocated at the end of the struct. */ const void *addr; /** * Name of the plugin. */ char *plugin; /** * Timeout for the current state in the state machine. */ struct GNUNET_TIME_Absolute state_timeout; /** * State of this address in the transport state machine. */ enum GNUNET_TRANSPORT_PeerState state; /** * Number of bytes in @e addr. */ size_t addr_len; /** * ATS bandwidth in for this address. */ guint ats_in; /** * ATS bandwidth out for this address. */ guint ats_out; /** * Is this address active in ATS? */ gboolean address_ats_active; }; /** * Information we track for each peer outside of the model. */ struct PeerInfo { /** * Reference to the peer in the view. */ GtkTreeRowReference *rr; /** * Handle to an active lookup for addresses of this peer, or NULL. */ struct GNUNET_TRANSPORT_PeerIterateContext *palc; /** * Identity of the peer for this entry. */ struct GNUNET_PeerIdentity pid; /** * Head of DLL with addresses of this peer. */ struct PeerAddress *pa_head; /** * Tail of DLL with addresses of this peer. */ struct PeerAddress *pa_tail; }; /** * Handle to our main loop. */ static struct GNUNET_GTK_MainLoop *ml; /** * Handle for our notifications from peerinfo about new peers. */ static struct GNUNET_PEERINFO_NotifyContext *pnc; /** * Handle to ATS service. */ static struct GNUNET_ATS_PerformanceHandle *ats; /** * Map of peer identities to the respective PeerInfo for our view. */ static struct GNUNET_CONTAINER_MultiPeerMap *peer2info; /** * Monitoring transport neighbours */ static struct GNUNET_TRANSPORT_PeerMonitoringContext *pmc; /** * Monitoring transport validation operations. */ static struct GNUNET_TRANSPORT_ValidationMonitoringContext *vmc; /** * Monitoring core connectivity. */ static struct GNUNET_CORE_Handle *core; /** * Should gnunet-peerinfo-gtk start in tray mode? */ static int tray_only; /** * Green status led (connected) */ static GdkPixbuf *led_green; /** * Red status led (disconnected) */ static GdkPixbuf *led_red; /** * Main window tree store. */ static GtkTreeStore *ts; /** * Map of all of our friends. */ static struct GNUNET_CONTAINER_MultiPeerMap *friends; #if HAVE_LIBUNIQUE static UniqueApp *unique_app; #endif /** * Get cfg. */ static const struct GNUNET_CONFIGURATION_Handle * 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 */ static GObject * get_object (const char *name) { return GNUNET_GTK_main_loop_get_object (ml, name); } /** * Function called on each entry in the #peer2info map * to free the associated path. * * @param cts unused * @param key peer identity * @param value the `struct PeerInfo` * @return #GNUNET_OK (continue to iterate) */ static int free_paths (void *cts, const struct GNUNET_PeerIdentity *key, void *value) { struct PeerInfo *info = value; struct PeerAddress *pa; while (NULL != (pa = info->pa_head)) { GNUNET_CONTAINER_DLL_remove (info->pa_head, info->pa_tail, pa); if (NULL != pa->tos) { GNUNET_TRANSPORT_address_to_string_cancel (pa->tos); pa->tos = NULL; } gtk_tree_row_reference_free (pa->rr); GNUNET_free_non_null (pa->plugin); GNUNET_free (pa); } gtk_tree_row_reference_free (info->rr); GNUNET_free (info); return GNUNET_OK; } /** * Task run on shutdown. * * @param cts unused * @param tc scheduler context, unused */ static void shutdown_task (void *cts, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_GTK_tray_icon_destroy (); GNUNET_GTK_main_loop_quit (ml); ml = NULL; if (NULL != pnc) { GNUNET_PEERINFO_notify_cancel (pnc); pnc = NULL; } if (NULL != ats) { GNUNET_ATS_performance_done (ats); ats = NULL; } if (NULL != pmc) { GNUNET_TRANSPORT_monitor_peers_cancel (pmc); pmc = NULL; } if (NULL != vmc) { GNUNET_TRANSPORT_monitor_validation_entries_cancel (vmc); vmc = NULL; } if (NULL != core) { GNUNET_CORE_disconnect (core); core = NULL; } GNUNET_CONTAINER_multipeermap_iterate (peer2info, &free_paths, NULL); GNUNET_CONTAINER_multipeermap_destroy (peer2info); peer2info = NULL; GNUNET_PEERINFO_GTK_flags_shutdown (); } /** * Convert a row reference to an iter. * * @param rr reference to a row (in our #ts) * @param iter set to the iter corresponding to @a rr */ static void get_iter_from_rr (GtkTreeRowReference *rr, GtkTreeIter *iter) { GtkTreePath *path; path = gtk_tree_row_reference_get_path (rr); GNUNET_assert (NULL != path); GNUNET_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (ts), iter, path)); gtk_tree_path_free (path); } /** * Function to call with the text format of an address * * @param cts the `struct PeerAddress` for the address * @param address address as a string, NULL on error */ static void peer_address_string_cb (void *cts, const char *address) { struct PeerAddress *pa = cts; GtkTreeIter iter; char *country; const char *colon; const char *dot; if (NULL == address) { pa->tos = NULL; return; } get_iter_from_rr (pa->rr, &iter); country = NULL; colon = strstr (address, ":"); if (NULL != colon) { for (dot = colon - 1; dot != address; dot--) if ('.' == *dot) break; if ('.' == *dot) country = GNUNET_strndup (&dot[1], (colon - dot) - 1); } gtk_tree_store_set (ts, &iter, PEERINFO_MC_COUNTRY_NAME, country, PEERINFO_MC_COUNTRY_FLAG, GNUNET_PEERINFO_GTK_get_flag (country), PEERINFO_MC_ADDRESS_AS_STRING, address, -1); GNUNET_free_non_null (country); } /** * Obtain the address entry for the given address at the given * peer. If the address entry does not yet exist, create it. * * @param pi peer info of the peer * @param addr peer's address * @return address entry for the given address */ static struct PeerAddress * get_address (struct PeerInfo *pi, const struct GNUNET_HELLO_Address *addr) { static struct PeerAddress *pa; GtkTreeIter iter; GtkTreeIter aiter; GtkTreePath *path; for (pa = pi->pa_head; NULL != pa; pa = pa->next) if ( (addr->address_length == pa->addr_len) && (0 == memcmp (addr, pa->addr, addr->address_length)) && (0 == strcmp (addr->transport_name, pa->plugin)) ) return pa; pa = GNUNET_malloc (sizeof (struct PeerAddress) + addr->address_length); pa->plugin = GNUNET_strdup (addr->transport_name); pa->addr = &pa[1]; memcpy (&pa[1], addr->address, addr->address_length); pa->addr_len = addr->address_length; GNUNET_CONTAINER_DLL_insert (pi->pa_head, pi->pa_tail, pa); path = gtk_tree_row_reference_get_path (pi->rr); GNUNET_assert (NULL != path); GNUNET_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (ts), &iter, path)); gtk_tree_path_free (path); gtk_tree_store_append (ts, &aiter, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (ts), &aiter); pa->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts), path); GNUNET_assert (NULL != pa->rr); gtk_tree_path_free (path); pa->tos = GNUNET_TRANSPORT_address_to_string (get_configuration (), addr, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &peer_address_string_cb, pa); return pa; } /** * Function to call with a binary format of an address * * @param cts the `struct PeerInfo` for which this is a valid address * @param address an address of the peer * @param expiration expiration time for that address * @return #GNUNET_OK (keep iterating) */ static int peer_address_cb (void *cts, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute expiration) { struct PeerInfo *info = cts; struct PeerAddress *pa; GtkTreeIter iter; GtkTreePath *path; path = gtk_tree_row_reference_get_path (info->rr); GNUNET_assert (NULL != path); GNUNET_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (ts), &iter, path)); gtk_tree_path_free (path); pa = get_address (info, address); get_iter_from_rr (pa->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_PEERINFO_ADDRESS_EXPIRATION, GNUNET_STRINGS_absolute_time_to_string (expiration), -1); return GNUNET_OK; } /** * Obtain the `struct PeerInfo` for the given peer; if it does * not yet exist, add it. * * @param peer peer identity to get the `struct PeerInfo` for * @return the corresponding struct */ static struct PeerInfo * get_peer_info (const struct GNUNET_PeerIdentity *peer) { GtkTreeIter iter; GtkTreePath *path; struct PeerInfo *info; info = GNUNET_CONTAINER_multipeermap_get (peer2info, peer); if (NULL != info) return info; info = GNUNET_new (struct PeerInfo); info->pid = *peer; gtk_tree_store_append (ts, &iter, NULL); gtk_tree_store_set (ts, &iter, PEERINFO_MC_PEER_IDENTITY_STRING, GNUNET_i2s (peer), PEERINFO_MC_IS_FRIEND, GNUNET_CONTAINER_multipeermap_contains (friends, peer), PEERINFO_MC_PEERINFO, info, PEERINFO_MC_SHOW_FRIEND, TRUE, PEERINFO_MC_CORE_CONNECTIVITY_LED, led_red, -1); path = gtk_tree_model_get_path (GTK_TREE_MODEL (ts), &iter); info->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts), path); GNUNET_assert (NULL != info->rr); gtk_tree_path_free (path); GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (peer2info, peer, info, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); return info; } /** * Function called for peers that we know about. * * @param cts closure * @param peer id of the peer, NULL for last call * @param hello hello message for the peer (can be NULL) * @param err_msg NULL if successful, otherwise contains error message */ static void peerinfo_processor (void *cts, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { struct PeerInfo *info; info = get_peer_info (peer); GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &peer_address_cb, info); } /** * Method called whenever a given peer has a QoS status change. * * @param cts closure * @param address the address * @param address_active is this address actively used to maintain a connection * to a peer * @param bandwidth_in available amount of inbound bandwidth * @param bandwidth_out available amount of outbound bandwidth * @param ats performance data for the address (as far as known) * @param ats_count number of performance records in @a ats */ static void status_cb (void *cts, const struct GNUNET_HELLO_Address *address, int address_active, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out, const struct GNUNET_ATS_Information *ats, uint32_t ats_count) { struct PeerInfo *info; struct PeerAddress *pa; struct PeerAddress *act; GtkTreeIter iter; info = get_peer_info (&address->peer); pa = get_address (info, address); pa->address_ats_active = address_active; pa->ats_in = (guint) ntohl (bandwidth_in.value__); pa->ats_out = (guint) ntohl (bandwidth_out.value__); get_iter_from_rr (pa->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_ATS_CONNECTIVITY_LED, (pa->address_ats_active) ? led_green : led_red, PEERINFO_MC_ATS_SELECTED_STATUS, pa->address_ats_active, PEERINFO_MC_BANDWIDTH_IN, pa->ats_in, PEERINFO_MC_BANDWIDTH_OUT, pa->ats_out, -1); act = NULL; for (pa = info->pa_head; NULL != pa; pa = pa->next) if (pa->address_ats_active) { GNUNET_break (NULL == act); act = pa; } get_iter_from_rr (info->rr, &iter); if (NULL == act) { gtk_tree_store_set (ts, &iter, PEERINFO_MC_ATS_CONNECTIVITY_LED, led_red, PEERINFO_MC_ATS_SELECTED_STATUS, FALSE, PEERINFO_MC_BANDWIDTH_IN, (guint) 0, PEERINFO_MC_BANDWIDTH_OUT, (guint) 0, -1); } else { gtk_tree_store_set (ts, &iter, PEERINFO_MC_ATS_CONNECTIVITY_LED, led_green, PEERINFO_MC_ATS_SELECTED_STATUS, TRUE, PEERINFO_MC_BANDWIDTH_IN, act->ats_in, PEERINFO_MC_BANDWIDTH_OUT, act->ats_out, -1); } } /** * Function to call with information about a peer * * @param cts closure * @param peer peer this update is about, * NULL if this is the final last callback for a iteration operation * @param address address, NULL for disconnect notification in monitor mode * @param state current state this peer is in * @param state_timeout timeout for the current state of the peer */ static void transport_peer_cb (void *cts, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, enum GNUNET_TRANSPORT_PeerState state, struct GNUNET_TIME_Absolute state_timeout) { struct PeerInfo *info; struct PeerAddress *pa; struct PeerAddress *act; struct PeerAddress *pre; GtkTreeIter iter; gboolean con; const char *tos; info = get_peer_info (peer); if (NULL == address) { GNUNET_break (0); return; } pa = get_address (info, address); pa->state = state; pa->state_timeout = state_timeout; con = (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)); get_iter_from_rr (pa->rr, &iter); tos = GNUNET_STRINGS_absolute_time_to_string (state_timeout); gtk_tree_store_set (ts, &iter, PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS, con, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING, con ? tos : NULL, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_LED, (con ? led_green : led_red), PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, GNUNET_TRANSPORT_p2s (state), PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, tos, -1); act = NULL; pre = NULL; for (pa = info->pa_head; NULL != pa; pa = pa->next) { if (GNUNET_YES == GNUNET_TRANSPORT_is_connected (pa->state)) { GNUNET_break (NULL == act); act = pa; } else if (GNUNET_TRANSPORT_DISCONNECT_FINISHED != pa->state) { /* remember that we're at least still in the neighbours table */ pre = pa; } } get_iter_from_rr (info->rr, &iter); if (NULL == act) { if (NULL == pre) { /* peer is not even in neighbours table; remove the LED entirely */ gtk_tree_store_set (ts, &iter, PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS, FALSE, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING, NULL, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_LED, NULL, PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, NULL, PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, NULL, -1); } else { /* mark peer as down with red LED */ gtk_tree_store_set (ts, &iter, PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS, FALSE, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING, NULL, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_LED, led_red, PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, NULL, PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, NULL, -1); } } else { /* mark peer as up, show details on top-level */ tos = GNUNET_STRINGS_absolute_time_to_string (state_timeout); gtk_tree_store_set (ts, &iter, PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS, TRUE, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING, tos, PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_LED, led_green, PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, GNUNET_TRANSPORT_p2s (act->state), PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, tos, -1); } } /** * Function to call with validation information about a peer * * @param cts closure * @param peer peer this update is about, * NULL if this is the final last callback for a iteration operation * @param address address, NULL for disconnect notification in monitor mode * @param valid_until when does this address expire * @param next_validation time of the next validation operation * */ static void validation_monitor_cb (void *cts, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute next_validation) { struct PeerInfo *info; struct PeerAddress *pa; GtkTreeIter iter; const char *tos; gboolean valid; GNUNET_assert (NULL != peer); info = get_peer_info (peer); if (NULL == address) { /* disconnect, mark all as down */ for (pa = info->pa_head; NULL != pa; pa = pa->next) { get_iter_from_rr (pa->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_VALIDATION_IS_VALID, FALSE, PEERINFO_MC_VALIDATION_TIMEOUT_AS_STRING, NULL, PEERINFO_MC_VALIDATION_STATE_LED, NULL, -1); } return; } valid = GNUNET_TIME_absolute_get_remaining (valid_until).rel_value_us > 0; pa = get_address (info, address); get_iter_from_rr (pa->rr, &iter); tos = GNUNET_STRINGS_absolute_time_to_string (valid_until); gtk_tree_store_set (ts, &iter, PEERINFO_MC_VALIDATION_IS_VALID, valid, PEERINFO_MC_VALIDATION_TIMEOUT_AS_STRING, tos, PEERINFO_MC_VALIDATION_STATE_LED, (valid ? led_green : led_red), -1); } /** * Method called whenever a given peer connects. * * @param cts closure * @param peer peer identity this notification is about */ static void handle_core_connect (void *cts, const struct GNUNET_PeerIdentity *peer) { struct PeerInfo *info; GtkTreeIter iter; info = get_peer_info (peer); get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_CORE_CONNECTIVITY_LED, led_green, PEERINFO_MC_CORE_CONNECTED_STATUS, TRUE, -1); } /** * Method called whenever a peer disconnects. * * @param cts closure * @param peer peer identity this notification is about */ static void handle_core_disconnect (void *cts, const struct GNUNET_PeerIdentity *peer) { struct PeerInfo *info; GtkTreeIter iter; info = get_peer_info (peer); get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_CORE_CONNECTIVITY_LED, led_red, PEERINFO_MC_CORE_CONNECTED_STATUS, FALSE, -1); } /** * Write a friend to the friends file. * * @param cts the `struct GNUNET_FRIENDS_Writer` * @param friend friend to write to file * @param value unused * @return #GNUNET_OK if the writing succeeded */ static int write_friend (void *cts, const struct GNUNET_PeerIdentity *friend, void *value) { struct GNUNET_FRIENDS_Writer *w = cts; return GNUNET_FRIENDS_write (w, friend); } /** * Write an updated friends file out to disk. */ static void write_friends () { struct GNUNET_FRIENDS_Writer *w; w = GNUNET_FRIENDS_write_start (get_configuration ()); if (NULL == w) { GNUNET_break (0); return; } GNUNET_CONTAINER_multipeermap_iterate (friends, &write_friend, w); if (GNUNET_OK != GNUNET_FRIENDS_write_stop (w)) { GNUNET_break (0); return; } } /** * The user has toggled the 'is friend' column for one of the peers. * Update everything. * * @param cell_renderer the cell renderer that issued the toggle signal * @param path which cell was toggled * @param user_data our main window builder */ void GNUNET_PEERINFO_GTK_main_window_friends_cellrenderertoggle_toggled_cb (GtkCellRendererToggle *cell_renderer, gchar *path, gpointer user_data) { GtkTreeIter old; struct PeerInfo *info; gboolean oldvalue; if (! gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ts), &old, path)) { GNUNET_break (0); return; } gtk_tree_model_get (GTK_TREE_MODEL (ts), &old, PEERINFO_MC_PEERINFO, &info, PEERINFO_MC_IS_FRIEND, &oldvalue, -1); gtk_tree_store_set (ts, &old, PEERINFO_MC_IS_FRIEND, ! oldvalue, -1); if (oldvalue) { GNUNET_break (1 == GNUNET_CONTAINER_multipeermap_remove_all (friends, &info->pid)); } else { GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multipeermap_put (friends, &info->pid, "true", GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); } write_friends (); } /** * Callback invoked if the application is supposed to exit. * * @param object * @param user_data unused */ void GNUNET_PEERINFO_GTK_quit_cb (GObject * object, gpointer user_data) { GNUNET_SCHEDULER_shutdown (); } /** * Load LED image from resource file. * * @param color color of the LED to load * @return the image as a GdkPixbuf */ static GdkPixbuf * load_led (const char *color) { GdkPixbuf *pixbuf; char *dir; char *fn; dir = GNUNET_GTK_installation_get_path (GNUNET_OS_IPK_DATADIR); GNUNET_asprintf (&fn, "%s%s.png", dir, color); GNUNET_free (dir); pixbuf = gdk_pixbuf_new_from_file (fn, NULL); GNUNET_free (fn); return pixbuf; } /** * Add a friend to our friends peer map. * * @param cts NULL * @param friend the friend to add */ static void add_friend (void *cts, const struct GNUNET_PeerIdentity *friend) { GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multipeermap_put (friends, friend, "true", GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); } /** * Actual main function run right after GNUnet's scheduler * is initialized. Initializes up GTK and Glade. * * @param cts NULL * @param tc schedule context */ static void run (void *cts, const struct GNUNET_SCHEDULER_TaskContext *tc) { GtkWidget *main_window; ml = cts; if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL)) return; led_green = load_led ("green"); led_red = load_led ("red"); GNUNET_GTK_set_icon_search_path (); GNUNET_GTK_setup_nls (); friends = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO); if (GNUNET_OK != GNUNET_FRIENDS_parse (get_configuration (), &add_friend, NULL)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to parse list of friends\n")); peer2info = GNUNET_CONTAINER_multipeermap_create (256, GNUNET_NO); core = GNUNET_CORE_connect (get_configuration (), NULL, NULL, &handle_core_connect, &handle_core_disconnect, NULL, GNUNET_NO, NULL, GNUNET_NO, NULL); pnc = GNUNET_PEERINFO_notify (get_configuration (), GNUNET_NO, &peerinfo_processor, NULL); pmc = GNUNET_TRANSPORT_monitor_peers (get_configuration (), NULL, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &transport_peer_cb, NULL); vmc = GNUNET_TRANSPORT_monitor_validation_entries (get_configuration (), NULL, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &validation_monitor_cb, NULL); ats = GNUNET_ATS_performance_init (get_configuration (), &status_cb, NULL); /* setup main window */ main_window = GTK_WIDGET (get_object ("GNUNET_PEERINFO_GTK_main_window")); main_window = GNUNET_GTK_plug_me ("GNUNET_PEERINFO_GTK_PLUG", main_window); ts = GTK_TREE_STORE (get_object ("GNUNET_PEERINFO_GTK_tree_store")); GNUNET_assert (NULL != ts); gtk_window_maximize (GTK_WINDOW (main_window)); if (NULL == getenv ("GNUNET_PEERINFO_GTK_PLUG")) GNUNET_GTK_tray_icon_create (ml, GTK_WINDOW (main_window), "gnunet-gtk" /* FIXME: different icon? */ , "gnunet-peerinfo-gtk"); #if HAVE_LIBUNIQUE unique_app_watch_window (unique_app, GTK_WINDOW (main_window)); #endif /* make GUI visible */ if (!tray_only) { gtk_widget_show (main_window); gtk_window_present (GTK_WINDOW (main_window)); } GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL); } /** * Main function of gnunet-peerinfo-gtk. * * @param argc number of arguments * @param argv arguments * @return 0 on success */ int main (int argc, char **argv) { static struct GNUNET_GETOPT_CommandLineOption options[] = { {'t', "tray", NULL, gettext_noop ("start in tray mode"), 0, &GNUNET_GETOPT_set_one, &tray_only}, GNUNET_GETOPT_OPTION_END }; #if HAVE_LIBUNIQUE gtk_init (&argc, &argv); unique_app = unique_app_new ("org.gnunet.gnunet-peerinfo-gtk", NULL); if (unique_app_is_running (unique_app)) { UniqueResponse response; response = unique_app_send_message (unique_app, UNIQUE_ACTIVATE, NULL); g_object_unref (unique_app); return (UNIQUE_RESPONSE_OK == response) ? 0 : 1; } #endif if (GNUNET_OK != GNUNET_GTK_main_loop_start ("gnunet-peerinfo-gtk", "GTK GUI for inspecting GNUnet Peers", argc, argv, options, "gnunet_peerinfo_gtk_main_window.glade", &run)) return 1; #if HAVE_LIBUNIQUE g_object_unref (unique_app); #endif return 0; } /* end of gnunet-peerinfo-gtk.c */