/* This file is part of GNUnet. (C) 2010 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 2, 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 "gnunet-peerinfo-gtk-flags.h" /** * Columns in the peerinfo model. */ enum PEERINFO_ModelColumns { /** * A gchararray */ PEERINFO_MC_PEER_IDENTITY_STRING = 0, /** * A guint */ PEERINFO_MC_NUMBER_OF_ADDRESSES = 1, /** * A gchararray */ PEERINFO_MC_COUNTRY_NAME = 2, /** * A GdkPixbuf */ PEERINFO_MC_COUNTRY_FLAG = 3, /** * A guint64 */ PEERINFO_MC_BANDWIDTH_IN = 4, /** * A guint64 */ PEERINFO_MC_BANDWIDTH_OUT = 5, /** * A gchararray */ PEERINFO_MC_ADDRESS_AS_STRING = 6 }; /** * 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; /** * Handle for address to string conversion. */ struct GNUNET_TRANSPORT_AddressToStringContext *tos; }; /** * 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_Handle *ats; /** * Map of peer identities to the respective PeerInfo for our view. */ static struct GNUNET_CONTAINER_MultiHashMap *peer2info; /** * Should gnunet-peerinfo-gtk start in tray mode? */ static int tray_only; /** * 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 cls unused * @param key peer identity * @param value the 'struct PeerInfo' * @return GNUNET_OK (continue to iterate) */ static int free_paths (void *cls, const GNUNET_HashCode * key, void *value) { struct PeerInfo *info = value; if (NULL != info->palc) { GNUNET_TRANSPORT_peer_get_active_addresses_cancel (info->palc); info->palc = NULL; } if (NULL != info->tos) { GNUNET_TRANSPORT_address_to_string_cancel (info->tos); info->tos = NULL; } gtk_tree_row_reference_free (info->rr); GNUNET_free (info); return GNUNET_OK; } /** * Task run on shutdown. * * @param cls unused * @param tc scheduler context, unused */ static void shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_PEERINFO_notify_cancel (pnc); pnc = NULL; #if FIXME if (NULL != ats) { GNUNET_ATS_disconnect (ats); ats = NULL; } #endif GNUNET_CONTAINER_multihashmap_iterate (peer2info, &free_paths, NULL); GNUNET_CONTAINER_multihashmap_destroy (peer2info); peer2info = NULL; GNUNET_PEERINFO_GTK_flags_shutdown (); } /** * Function to call with the text format of an address * * @param cls the 'struct PeerInfo' for which this is a valid address * @param address address as a string, NULL on error */ static void peer_address_string_cb (void *cls, const char *address) { struct PeerInfo *info = cls; GtkListStore *ls; GtkTreeModel *tm; GtkTreeIter iter; GtkTreePath *path; char *country; const char *colon; const char *dot; info->tos = NULL; ls = GTK_LIST_STORE (get_object ("GNUNET_PEERINFO_GTK_list_store")); tm = GTK_TREE_MODEL (ls); path = gtk_tree_row_reference_get_path (info->rr); GNUNET_assert (NULL != path); GNUNET_assert (TRUE == gtk_tree_model_get_iter (tm, &iter, path)); gtk_tree_path_free (path); if (NULL == address) { /* error */ gtk_list_store_set (ls, &iter, PEERINFO_MC_NUMBER_OF_ADDRESSES, (guint) 1, PEERINFO_MC_COUNTRY_NAME, NULL, PEERINFO_MC_COUNTRY_FLAG, NULL, PEERINFO_MC_ADDRESS_AS_STRING, "", -1); } else { /* last address, store information in model */ 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_list_store_set (ls, &iter, 1, 1, 2, country, 3, GNUNET_PEERINFO_GTK_get_flag (country), 6, address, -1); GNUNET_free (country); } } /** * Function to call with a binary format of an address * * @param cls the 'struct PeerInfo' for which this is a valid address * @param peer peer the update is about * @param address NULL on disconnect, otherwise 0-terminated printable UTF-8 string */ static void peer_address_cb (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address) { struct PeerInfo *info = cls; GtkListStore *ls; GtkTreeModel *tm; GtkTreeIter iter; GtkTreePath *path; if (NULL == address) { /* disconnect */ ls = GTK_LIST_STORE (get_object ("GNUNET_PEERINFO_GTK_list_store")); tm = GTK_TREE_MODEL (ls); path = gtk_tree_row_reference_get_path (info->rr); GNUNET_assert (NULL != path); GNUNET_assert (TRUE == gtk_tree_model_get_iter (tm, &iter, path)); gtk_tree_path_free (path); gtk_list_store_set (ls, &iter, PEERINFO_MC_NUMBER_OF_ADDRESSES, (guint) 0, PEERINFO_MC_COUNTRY_NAME, NULL, PEERINFO_MC_COUNTRY_FLAG, NULL, PEERINFO_MC_ADDRESS_AS_STRING, "", -1); return; } if (NULL != info->tos) GNUNET_TRANSPORT_address_to_string_cancel (info->tos); info->tos = GNUNET_TRANSPORT_address_to_string (get_configuration (), address, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &peer_address_string_cb, info); } /** * Function called for peers that we know about. * * @param cls 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 *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { GtkListStore *ls; GtkTreeModel *tm; GtkTreeIter iter; char *npid; struct GNUNET_CRYPTO_HashAsciiEncoded enc; struct PeerInfo *info; GtkTreePath *path; ls = GTK_LIST_STORE (get_object ("GNUNET_PEERINFO_GTK_list_store")); if (NULL == ls) { GNUNET_break (0); return; } tm = GTK_TREE_MODEL (ls); info = GNUNET_CONTAINER_multihashmap_get (peer2info, &peer->hashPubKey); if (NULL == info) { GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc); npid = (char *) &enc; npid[4] = '\0'; gtk_list_store_append (ls, &iter); gtk_list_store_set (ls, &iter, PEERINFO_MC_PEER_IDENTITY_STRING, npid, PEERINFO_MC_NUMBER_OF_ADDRESSES, (guint) 0, PEERINFO_MC_COUNTRY_NAME, "", PEERINFO_MC_COUNTRY_FLAG, NULL, PEERINFO_MC_BANDWIDTH_IN, (guint64) 0, PEERINFO_MC_BANDWIDTH_OUT, (guint64) 0, PEERINFO_MC_ADDRESS_AS_STRING, "", -1); path = gtk_tree_model_get_path (tm, &iter); info = GNUNET_malloc (sizeof (struct PeerInfo)); info->rr = gtk_tree_row_reference_new (tm, path); GNUNET_assert (NULL != info->rr); gtk_tree_path_free (path); GNUNET_CONTAINER_multihashmap_put (peer2info, &peer->hashPubKey, info, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); } if (NULL == info->palc) { info->palc = GNUNET_TRANSPORT_peer_get_active_addresses (get_configuration (), peer, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, &peer_address_cb, info); } } #if FIXME /** * Method called whenever a given peer has a status change. * * @param cls closure * @param peer peer identity this notification is about * @param timeout absolute time when this peer will time out * unless we see some further activity from it * @param bandwidth_in available amount of inbound bandwidth * @param bandwidth_out available amount of outbound bandwidth * @param atsi performance data for the connection */ static void status_cb (void *cls, const struct GNUNET_PeerIdentity *peer, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out, struct GNUNET_TIME_Absolute timeout, const struct GNUNET_TRANSPORT_ATS_Information *atsi) { struct PeerInfo *info; GtkListStore *ls; GtkTreeModel *tm; GtkTreeIter iter; GtkTreePath *path; info = GNUNET_CONTAINER_multihashmap_get (peer2info, &peer->hashPubKey); if (NULL == info) return; /* should rarely happen... */ ls = GTK_LIST_STORE (get_object ("GNUNET_PEERINFO_GTK_list_store")); tm = GTK_TREE_MODEL (ls); path = gtk_tree_row_reference_get_path (info->rr); GNUNET_assert (NULL != path); GNUNET_assert (TRUE == gtk_tree_model_get_iter (tm, &iter, path)); gtk_tree_path_free (path); gtk_list_store_set (ls, &iter, PEERINFO_MC_BANDWIDTH_IN, (guint64) ntohl (bandwidth_in.value__), PEERINFO_MC_BANDWIDTH_OUT, (guint64) ntohl (bandwidth_out.value__), -1); } #endif /** * Callback invoked if the application is supposed to exit. */ void GNUNET_PEERINFO_GTK_quit_cb (GObject * object, gpointer user_data) { GNUNET_GTK_tray_icon_destroy (); GNUNET_GTK_main_loop_quit (ml); GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); } /** * Actual main function run right after GNUnet's scheduler * is initialized. Initializes up GTK and Glade. */ static void run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GtkWidget *main_window; ml = cls; if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL)) { return; } GNUNET_GTK_set_icon_search_path (); GNUNET_GTK_setup_nls (); peer2info = GNUNET_CONTAINER_multihashmap_create (256); pnc = GNUNET_PEERINFO_notify (get_configuration (), &peerinfo_processor, NULL); if (pnc == NULL) { fprintf (stderr, _("Failed to initialize communication with peerinfo service!\n")); exit (1); } #if FIXME ats = GNUNET_ATS_connect (get_configuration (), &status_cb, NULL); #endif /* setup main window */ main_window = GTK_WIDGET (get_object ("GNUNET_PEERINFO_GTK_main_window")); gtk_window_maximize (GTK_WINDOW (main_window)); GNUNET_GTK_tray_icon_create (GTK_WINDOW (main_window), "gnunet-gtk" /* FIXME: different icon? */ , "gnunet-peerinfo-gtk"); /* make GUI visible */ if (!tray_only) { gtk_widget_show (main_window); gtk_window_present (GTK_WINDOW (main_window)); } } int main (int argc, char *const *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 (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; return 0; } /* end of gnunet-peerinfo-gtk.c */