/* This file is part of GNUnet. Copyright (C) 2010-2014 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/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 /** * Should we show peers that have no connections and * no known (valid, non-expired) addresses? */ static int show_inactive; /** * 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 guint for sorting by connectivity */ 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 we track for each peer outside of the model. */ struct PeerInfo; /** * Information about an address of the peer. */ struct PeerAddress { /** * DLL. */ struct PeerAddress *next; /** * DLL. */ struct PeerAddress *prev; /** * Peer this address belongs to. */ struct PeerInfo *pi; /** * 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; /** * Address in string format. */ char *address_as_string; /** * ccTLD. */ char *country_name; /** * 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; /** * 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; }; /** * 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 `struct 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 transport plugin sessions. */ struct GNUNET_TRANSPORT_PluginMonitor *pm; /** * Monitoring core connectivity. */ static struct GNUNET_CORE_MonitorHandle *cm; /** * 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; /** * Black status led (error) */ static GdkPixbuf *led_black; /** * Yellow status led (connecting) */ static GdkPixbuf *led_yellow; /** * Blue status led (reconnecting/rekeying/special operation) */ static GdkPixbuf *led_blue; /** * Main window tree store. */ static GtkTreeStore *ts; /** * Map of all of our friends. */ static struct GNUNET_CONTAINER_MultiPeerMap *friends; /** * Tree view column for CORE connecitivity. */ static GtkTreeViewColumn *tvc_core_connectivity; /** * Tree view column for TRANSPORT connecitivity. */ static GtkTreeViewColumn *tvc_transport_connectivity; /** * Tree view column for ATS connecitivity. */ static GtkTreeViewColumn *tvc_ats_connectivity; /** * Tree view column for plugin connecitivity. */ static GtkTreeViewColumn *tvc_plugin_connectivity; /** * Tree view column for validation status */ static GtkTreeViewColumn *tvc_validation_status; /** * Tree view column for neighbour_state */ static GtkTreeViewColumn *tvc_neighbour_state; #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); } /** * Free the given peer address which must belong to the * given peer. * * @param info peer the address belongs to * @param pa address to free */ static void free_peer_address (struct PeerInfo *info, struct PeerAddress *pa) { 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_non_null (pa->address_as_string); GNUNET_free_non_null (pa->country_name); GNUNET_free (pa); } /** * 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)) free_peer_address (info, pa); gtk_tree_row_reference_free (info->rr); GNUNET_free (info); return GNUNET_OK; } /** * Task run on shutdown. * * @param cts unused */ static void shutdown_task (void *cts) { 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 != pm ) { GNUNET_TRANSPORT_monitor_plugins_cancel (pm); pm = NULL; } if (NULL != vmc) { GNUNET_TRANSPORT_monitor_validation_entries_cancel (vmc); vmc = NULL; } if (NULL != cm) { GNUNET_CORE_monitor_stop (cm); cm = 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 last call * @param res result of the address to string conversion: * if #GNUNET_OK: address contains address as string * if #GNUNET_SYSERR: address is invalid */ static void peer_address_string_cb (void *cts, const char *address, int res) { struct PeerAddress *pa = cts; GtkTreeIter iter; char *country; const char *colon; const char *dot; if (NULL == address) { pa->tos = NULL; return; } if (GNUNET_SYSERR == res) return; /* don't care */ 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); if (pa->address_ats_active) { get_iter_from_rr (pa->pi->rr, &iter); 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, PEERINFO_MC_PLUGIN_NAME, pa->plugin, -1); } pa->address_as_string = GNUNET_strdup (address); pa->country_name = 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->address, 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]; pa->pi = pi; 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_insert_with_values (ts, &aiter, &iter, -1 /* append */, PEERINFO_MC_PLUGIN_NAME, addr->transport_name, -1); 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); if (NULL != friends) { gtk_tree_store_set (ts, &iter, PEERINFO_MC_IS_FRIEND, GNUNET_CONTAINER_multipeermap_contains (friends, peer), -1); } gtk_tree_store_set (ts, &iter, PEERINFO_MC_PEER_IDENTITY_STRING, GNUNET_i2s (peer), PEERINFO_MC_PEERINFO, info, PEERINFO_MC_SHOW_FRIEND, TRUE, -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; if ( (NULL == hello) || ( (! show_inactive) && (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_HELLO_get_last_expiration (hello)).rel_value_us) ) ) return; info = get_peer_info (peer); GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &peer_address_cb, info); } /** * ATS disconnected, remove ATS status information for all peers. * * @param cls NULL * @param pid peer identity to reset * @param value the `struct PeerInfo` for the peer * @return #GNUNET_OK */ static int reset_ats_status (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) { struct PeerInfo *info = value; GtkTreeIter iter; get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_ATS_CONNECTIVITY_LED, NULL, PEERINFO_MC_ATS_SELECTED_STATUS, FALSE, PEERINFO_MC_BANDWIDTH_IN, 0, PEERINFO_MC_BANDWIDTH_OUT, 0, -1); return GNUNET_OK; } /** * Method called whenever a given peer has a QoS status change. * * @param cts closure * @param address the address, or NULL if service disconnected * @param address_active #GNUNET_YES if this address is actively used * to maintain a connection to a peer; * #GNUNET_NO if the address is not actively used; * #GNUNET_SYSERR if this address is no longer available for ATS * @param bandwidth_in available amount of inbound bandwidth * @param bandwidth_out available amount of outbound bandwidth * @param perf performance data for the address (as far as known) */ static void ats_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_Properties *prop) { struct PeerInfo *info; struct PeerAddress *pa; struct PeerAddress *act; GtkTreeIter iter; if (NULL == address) { /* state 'reset' / disconnect from service, reset everything */ GNUNET_CONTAINER_multipeermap_iterate (peer2info, &reset_ats_status, NULL); return; } 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); if (GNUNET_SYSERR == address_active) { gtk_tree_store_remove (ts, &iter); free_peer_address (info, pa); return; } 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 = (GNUNET_YES == address_active) ? pa : NULL; for (pa = info->pa_head; NULL != pa; pa = pa->next) { if (GNUNET_YES == pa->address_ats_active) { if (NULL == act) { /* first active address found, remember it! */ act = pa; } else if (GNUNET_YES == address_active) { /* second address became active, set BW for first to zero */ get_iter_from_rr (pa->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_BANDWIDTH_IN, (guint) 0, PEERINFO_MC_BANDWIDTH_OUT, (guint) 0, -1); } } } get_iter_from_rr (info->rr, &iter); if (NULL == act) { /* no active address, make sure per-peer state is on 'off' */ 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, PEERINFO_MC_COUNTRY_NAME, NULL, PEERINFO_MC_COUNTRY_FLAG, NULL, PEERINFO_MC_ADDRESS_AS_STRING, NULL, PEERINFO_MC_PLUGIN_NAME, NULL, -1); } else { /* update per-peer state to that of active address */ 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, PEERINFO_MC_COUNTRY_NAME, act->country_name, PEERINFO_MC_COUNTRY_FLAG, GNUNET_PEERINFO_GTK_get_flag (act->country_name), PEERINFO_MC_ADDRESS_AS_STRING, act->address_as_string, PEERINFO_MC_PLUGIN_NAME, act->plugin, -1); } } /** * Transport peer monitor disconnected, reset * transport peer status information for all peers. * * @param cls NULL * @param pid peer identity to reset * @param value the `struct PeerInfo` for the peer * @return #GNUNET_OK */ static int reset_transport_peer_status (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) { struct PeerInfo *info = value; GtkTreeIter iter; struct PeerAddress *pa; get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_NEIGHBOUR_CONNECTED_STATUS, NULL, 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); 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_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); } return GNUNET_OK; } /** * 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; GtkTreeIter iter; gboolean con; const char *tos; struct PeerAddress *pa; GdkPixbuf *led; if (NULL == peer) { /* disconnect notification, reset all state */ GNUNET_CONTAINER_multipeermap_iterate (peer2info, &reset_transport_peer_status, NULL); return; } switch (state) { case GNUNET_TRANSPORT_PS_NOT_CONNECTED: led = led_red; break; case GNUNET_TRANSPORT_PS_INIT_ATS: led = led_yellow; break; case GNUNET_TRANSPORT_PS_SYN_SENT: led = led_yellow; break; case GNUNET_TRANSPORT_PS_SYN_RECV_ATS: led = led_yellow; break; case GNUNET_TRANSPORT_PS_SYN_RECV_ACK: led = led_yellow; break; case GNUNET_TRANSPORT_PS_CONNECTED: led = led_green; break; case GNUNET_TRANSPORT_PS_RECONNECT_ATS: led = led_blue; break; case GNUNET_TRANSPORT_PS_RECONNECT_SENT: led = led_blue; break; case GNUNET_TRANSPORT_PS_SWITCH_SYN_SENT: led = led_green; break; case GNUNET_TRANSPORT_PS_DISCONNECT: led = led_red; break; case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED: led = NULL; break; default: GNUNET_break (0); led = NULL; break; } info = get_peer_info (peer); info->state = state; info->state_timeout = state_timeout; con = (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)); get_iter_from_rr (info->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, led, PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, GNUNET_TRANSPORT_ps2s (state), PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, tos, -1); 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_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); } if (NULL == address) return; pa = get_address (info, address); get_iter_from_rr (pa->rr, &iter); 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, led, PEERINFO_MC_NEIGHBOUR_STATE_AS_STRING, GNUNET_TRANSPORT_ps2s (state), PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING, tos, -1); } /** * Function called by the plugin with information about the * current sessions managed by the plugin (for monitoring). * * @param cls closure * @param session session handle this information is about, * NULL to indicate that we are "in sync" (initial * iteration complete) * @param session_ctx storage location where the application * can store data; will point to NULL on #GNUNET_TRANSPORT_SS_INIT, * and must be reset to NULL on #GNUNET_TRANSPORT_SS_DONE * @param sinfo information about the state of the session, * NULL if @a session is also NULL and we are * merely signalling that the initial iteration is over; * NULL with @a session being non-NULL if the monitor * was being cancelled while sessions were active */ static void transport_plugin_cb (void *cls, struct GNUNET_TRANSPORT_PluginSession *session, void **session_ctx, const struct GNUNET_TRANSPORT_SessionInfo *sinfo) { struct PeerInfo *info; GtkTreeIter iter; const char *tos; struct PeerAddress *pa; GdkPixbuf *led; if ( (NULL == session) && (NULL == sinfo) ) { /* initial iteration completed, ignore */ return; } if (NULL == sinfo) { /* in shutdown, ignore */ return; } switch (sinfo->state) { case GNUNET_TRANSPORT_SS_INIT: led = led_red; break; case GNUNET_TRANSPORT_SS_HANDSHAKE: led = led_yellow; break; case GNUNET_TRANSPORT_SS_UP: led = led_green; break; case GNUNET_TRANSPORT_SS_UPDATE: led = led_green; break; case GNUNET_TRANSPORT_SS_DONE: led = NULL; break; default: GNUNET_break (0); led = NULL; break; } if (NULL == sinfo->address) { GNUNET_break (0); // is this OK? return; } info = get_peer_info (&sinfo->address->peer); tos = GNUNET_STRINGS_absolute_time_to_string (sinfo->session_timeout); if (NULL == led) tos = NULL; pa = get_address (info, sinfo->address); get_iter_from_rr (pa->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_PLUGIN_CONNECTIVITY_STATUS, (led_green == led), PEERINFO_MC_PLUGIN_CONNECTIVITY_LED, led, PEERINFO_MC_PLUGIN_CONNECTIVITY_TIMEOUT_AS_STRING, tos, -1); } /** * Transport address validation monitor disconnected, reset * address validation status information for all peers. * * @param cls NULL * @param pid peer identity to reset * @param value the `struct PeerInfo` for the peer * @return #GNUNET_OK */ static int reset_address_validation_status (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) { struct PeerInfo *info = value; GtkTreeIter iter; struct PeerAddress *pa; 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 GNUNET_OK; } /** * Function to call with validation information about a peer * * @param cts closure * @param address address, NULL for disconnect notification in monitor mode * @param last_validation when was this address last validated * @param valid_until when does this address expire * @param next_validation time of the next validation operation * @param state state in the validation state machine */ static void validation_monitor_cb (void *cts, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute last_validation, struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute next_validation, enum GNUNET_TRANSPORT_ValidationState state) { struct PeerInfo *info; struct PeerAddress *pa; GtkTreeIter iter; const char *tos; gboolean valid; GdkPixbuf *led; if (NULL == address) { /* disconnect notification, reset all validation state */ GNUNET_CONTAINER_multipeermap_iterate (peer2info, &reset_address_validation_status, NULL); return; } switch (state) { case GNUNET_TRANSPORT_VS_NONE: led = led_black; break; case GNUNET_TRANSPORT_VS_NEW: led = led_yellow; break; case GNUNET_TRANSPORT_VS_UPDATE: led = led_blue; break; case GNUNET_TRANSPORT_VS_TIMEOUT: led = led_red; break; case GNUNET_TRANSPORT_VS_REMOVE: led = NULL; break; default: GNUNET_break (0); led = NULL; break; } info = get_peer_info (&address->peer); valid = (GNUNET_TIME_absolute_get_remaining (valid_until).rel_value_us > 0); if (valid) led = led_green; /* UGH: should this not be indicated by 'state' somehow? */ 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, led, -1); } /** * Core disconnected, remove CORE KX status information for all peers. * * @param cls NULL * @param pid peer identity to reset * @param value the `struct PeerInfo` for the peer * @return #GNUNET_OK */ static int reset_core_status (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) { struct PeerInfo *info = value; GtkTreeIter iter; get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_CORE_CONNECTIVITY_LED, NULL, PEERINFO_MC_CORE_CONNECTED_STATUS, (guint) 0, -1); return GNUNET_OK; } /** * Method called whenever CORE KX state changes for a peer. * * @param cts closure * @param peer peer identity this notification is about * @param state current key exchange state of the peer * @param timeout when does the current state expire */ static void handle_core_monitor_event (void *cts, const struct GNUNET_PeerIdentity *peer, enum GNUNET_CORE_KxState state, struct GNUNET_TIME_Absolute timeout) { struct PeerInfo *info; GtkTreeIter iter; GdkPixbuf *led; guint status; switch (state) { case GNUNET_CORE_KX_STATE_DOWN: led = led_black; status = 1; break; case GNUNET_CORE_KX_STATE_KEY_SENT: led = led_yellow; status = 2; break; case GNUNET_CORE_KX_STATE_KEY_RECEIVED: led = led_yellow; status = 2; break; case GNUNET_CORE_KX_STATE_UP: led = led_green; status = 4; break; case GNUNET_CORE_KX_STATE_REKEY_SENT: led = led_blue; status = 4; break; case GNUNET_CORE_KX_PEER_DISCONNECT: led = NULL; status = 1; break; case GNUNET_CORE_KX_ITERATION_FINISHED: return; case GNUNET_CORE_KX_CORE_DISCONNECT: GNUNET_CONTAINER_multipeermap_iterate (peer2info, &reset_core_status, NULL); return; default: GNUNET_break (0); led = NULL; status = -1; break; } info = get_peer_info (peer); get_iter_from_rr (info->rr, &iter); gtk_tree_store_set (ts, &iter, PEERINFO_MC_CORE_CONNECTIVITY_LED, led, PEERINFO_MC_CORE_CONNECTED_STATUS, status, -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 (NULL == friends) { GNUNET_break (0); return; } 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_OS_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) { if (NULL == friends) { GNUNET_break (0); return; } GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multipeermap_put (friends, friend, "true", GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); } /** * Return the tooltip for the given coordinate. * * @param widget our tree view * @param x x-coordinate * @param y y-coordinate * @param keyboard_tip triggered by keyboard? * @param tooltip tooltip object to fill * @param data NULL * @return TRUE if a tooltip was provided */ static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer data) { GtkTreeView *tv = GTK_TREE_VIEW (widget); GtkTreeModel *model = GTK_TREE_MODEL (ts); GtkTreeIter iter; GtkTreePath *path; GtkTreeViewColumn *column; int model_column = -1; gchar *tmp; if (! gtk_tree_view_get_tooltip_context (tv, &x, &y, keyboard_tip, &model, &path, &iter)) return FALSE; if (! gtk_tree_view_get_path_at_pos (tv, x, y, NULL, &column, NULL, NULL)) return FALSE; if (tvc_core_connectivity == column) model_column = -1; else if (tvc_plugin_connectivity == column) model_column = PEERINFO_MC_PLUGIN_CONNECTIVITY_TIMEOUT_AS_STRING; else if (tvc_ats_connectivity == column) model_column = PEERINFO_MC_PEERINFO_ADDRESS_EXPIRATION; else if (tvc_transport_connectivity == column) model_column = PEERINFO_MC_NEIGHBOUR_CONNECTIVITY_TIMEOUT_AS_STRING; else if (tvc_validation_status == column) model_column = PEERINFO_MC_VALIDATION_TIMEOUT_AS_STRING; else if (tvc_neighbour_state == column) model_column = PEERINFO_MC_NEIGHBOUR_STATE_TIMEOUT_AS_STRING; if (-1 == model_column) return FALSE; gtk_tree_model_get (model, &iter, model_column, &tmp, -1); if (NULL == tmp) return FALSE; gtk_tooltip_set_text (tooltip, tmp); gtk_tree_view_set_tooltip_cell (tv, tooltip, path, column, NULL); g_free (tmp); gtk_tree_path_free (path); return TRUE; } /** * Actual main function run right after GNUnet's scheduler * is initialized. Initializes up GTK and Glade. * * @param cts the main loop context */ static void run (void *cts) { GtkWidget *main_window; GtkTreeView *tv; const struct GNUNET_CONFIGURATION_Handle *cfg; int can_edit_friends; ml = cts; if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL)) return; cfg = get_configuration (); led_green = load_led ("green"); led_red = load_led ("red"); led_black = load_led ("black"); led_yellow = load_led ("yellow"); led_blue = load_led ("blue"); GNUNET_GTK_set_icon_search_path (); GNUNET_GTK_setup_nls (); can_edit_friends = (GNUNET_NO == GNUNET_CONFIGURATION_have_value (cfg, "ARM", "SYSTEM_ONLY")) || (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg, "ARM", "SYSTEM_ONLY")); if (can_edit_friends) { friends = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO); if (GNUNET_OK != GNUNET_FRIENDS_parse (cfg, &add_friend, NULL)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to parse list of friends\n")); } else { gtk_tree_view_column_set_visible (GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_friends_treeviewcolumn")), FALSE); } peer2info = GNUNET_CONTAINER_multipeermap_create (256, GNUNET_NO); cm = GNUNET_CORE_monitor_start (cfg, &handle_core_monitor_event, NULL); pnc = GNUNET_PEERINFO_notify (cfg, GNUNET_NO, &peerinfo_processor, NULL); pmc = GNUNET_TRANSPORT_monitor_peers (cfg, NULL, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &transport_peer_cb, NULL); pm = GNUNET_TRANSPORT_monitor_plugins (cfg, &transport_plugin_cb, NULL); vmc = GNUNET_TRANSPORT_monitor_validation_entries (cfg, NULL, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &validation_monitor_cb, NULL); ats = GNUNET_ATS_performance_init (cfg, &ats_status_cb, NULL); tvc_core_connectivity = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_core_connectivity_treeviewcolumn")); tvc_ats_connectivity = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_ats_connectivity_treeviewcolumn")); tvc_plugin_connectivity = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_plugin_connectivity_treeviewcolumn")); tvc_transport_connectivity = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_transport_connectivity_treeviewcolumn")); tvc_validation_status = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_validation_status_treeviewcolumn")); tvc_neighbour_state = GTK_TREE_VIEW_COLUMN (get_object ("GNUNET_PEERINFO_GTK_main_window_neighbour_state_treeviewcolumn")); /* 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"); tv = GTK_TREE_VIEW (get_object ("GNUNET_PEERINFO_GTK_main_window_treeview")); g_object_set (tv, "has-tooltip", TRUE, NULL); g_signal_connect (tv, "query-tooltip", G_CALLBACK (query_tooltip_cb), NULL); #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_shutdown (&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[] = { {'s', "show-inactive", NULL, gettext_noop ("show peers even if the are inactive and we know nothing except their public key"), 0, &GNUNET_GETOPT_set_one, &show_inactive}, {'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 */