diff options
author | Jens Georg <mail@jensge.org> | 2019-01-20 20:07:43 +0100 |
---|---|---|
committer | Jens Georg <mail@jensge.org> | 2019-01-25 18:12:52 +0100 |
commit | 2c18c0533daf8e3b33a69e4e891f50a4210534b8 (patch) | |
tree | 1f69339ca0e2250f694315a9df48a9c183ce86a0 | |
parent | 8e23bd2ffd8c4d80e1a170b818235e12fec71911 (diff) | |
download | gssdp-wip/sniffer-rewrite.tar.gz |
wip: refactor snifferwip/sniffer-rewrite
-rw-r--r-- | tools/gssdp-device-sniffer-window.c | 579 | ||||
-rw-r--r-- | tools/gssdp-device-sniffer-window.h | 29 | ||||
-rw-r--r-- | tools/gssdp-device-sniffer-window.ui (renamed from tools/gssdp-device-sniffer.ui) | 300 | ||||
-rw-r--r-- | tools/gssdp-device-sniffer.c | 830 | ||||
-rw-r--r-- | tools/gssdp-device-sniffer.gresource.xml | 6 | ||||
-rw-r--r-- | tools/main.c | 132 | ||||
-rw-r--r-- | tools/meson.build | 20 |
7 files changed, 893 insertions, 1003 deletions
diff --git a/tools/gssdp-device-sniffer-window.c b/tools/gssdp-device-sniffer-window.c new file mode 100644 index 0000000..ef7f270 --- /dev/null +++ b/tools/gssdp-device-sniffer-window.c @@ -0,0 +1,579 @@ +/* gssdp-device-sniffer-window.c + * + * Copyright 2019 Jens Georg + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libgssdp/gssdp-client-private.h" +#include "gssdp-device-sniffer-window.h" + +#include <libsoup/soup.h> +#include <libgssdp/gssdp.h> + + +typedef enum { + PACKET_STORE_COLUMN_TIME, + PACKET_STORE_COLUMN_IP, + PACKET_STORE_COLUMN_INTERFACE, + PACKET_STORE_COLUMN_PACKET_TYPE, + PACKET_STORE_COLUMN_TARGET, + PACKET_STORE_COLUMN_HEADERS, + PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME +} PACKET_STORE_COLUMNS; + +typedef enum { + DEVICE_STORE_COLUMN_UUID, + DEVICE_STORE_COLUMN_FIRST_SEEN, + DEVICE_STORE_COLUMN_TYPE, + DEVICE_STORE_COLUMN_LOCATION +} DEVICE_STORE_COLUMNS; + +static void +on_trigger_rescan (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_about (GSimpleAction *action, GVariant *parameter, gpointer user_data); + +static void +on_set_address_filter (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_show_address_filter (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_details_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_clear_packet_capture (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_packet_capture_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data); + +static void +on_ssdp_message (GssdpDeviceSnifferWindow *self, + const gchar *from_ip, + gushort from_port, + _GSSDPMessageType type, + SoupMessageHeaders *headers, + GSSDPClient *client); + +static void +on_resource_available (GssdpDeviceSnifferWindow *self, + const char *usn, + GList *locations, + GSSDPResourceBrowser *ssdp_resource_browser); + +static void +on_resource_unavailable (GssdpDeviceSnifferWindow *self, + const char *usn, + GSSDPResourceBrowser *ssdp_resource_browser); + +static GActionEntry actions[] = { + { "clear-packet-capture", on_clear_packet_capture }, + { "capture-packets", NULL, NULL, "true", on_packet_capture_activate }, + { "trigger-rescan", on_trigger_rescan }, + { "set-address-filter", on_set_address_filter }, + { "show-packet-details", NULL, NULL, "true", on_details_activate }, + { "show-address-filter", on_show_address_filter }, + { "about", on_about } +}; + +struct _GssdpDeviceSnifferWindow +{ + GtkApplicationWindow parent_instance; + + /* Template widgets */ + GtkTreeView *packet_treeview; + GtkTreeView *device_details_treeview; + GtkMenuButton *window_menu; + + GtkTextView *packet_details_textview; + + /* UPnP stuff */ + GSSDPClient *client; + GSSDPResourceBrowser *browser; + char *ip_filter; + gboolean capture_packets; +}; + +G_DEFINE_TYPE (GssdpDeviceSnifferWindow, gssdp_device_sniffer_window, GTK_TYPE_APPLICATION_WINDOW) + +#define SNIFFER_WINDOW_TEMPLATE "/org/gnome/GssdpDeviceSniffer/gssdp-device-sniffer-window.ui" +#define SNIFFER_WINDOW_MENU_RESOURCE "/org/gnome/GssdpDeviceSniffer/window-menu.ui" + +static void +on_trigger_rescan (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GssdpDeviceSnifferWindow *self = GSSDP_DEVICE_SNIFFER_WINDOW (user_data); + + gssdp_resource_browser_rescan (self->browser); +} + +static void +on_about (GSimpleAction *action, GVariant *parameter, gpointer user_data) +{ +#if 0 + GtkWidget *dialog = NULL; + + dialog = GTK_WIDGET (gtk_builder_get_object (builder, + "about-dialog")); + gtk_widget_show (dialog); +#endif +} + +static void +on_set_address_filter (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ +} + +static void +on_show_address_filter (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ +} + +static void +on_details_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ +} + +static void +on_packet_capture_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GssdpDeviceSnifferWindow *self = GSSDP_DEVICE_SNIFFER_WINDOW (user_data); + + self->capture_packets = g_variant_get_boolean (parameter); + g_simple_action_set_state (action, parameter); +} + +static void +on_clear_packet_capture (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + GssdpDeviceSnifferWindow *self = GSSDP_DEVICE_SNIFFER_WINDOW (user_data); + GtkTreeModel *model; + GtkTreeIter iter; + gboolean more; + time_t *arrival_time; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->packet_treeview)); + more = gtk_tree_model_get_iter_first (model, &iter); + + while (more) { + gtk_tree_model_get (model, + &iter, + PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, &arrival_time, -1); + g_free (arrival_time); + more = gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + } +} + +static void +packet_header_to_string (const char *header_name, + const char *header_val, + GString **text) +{ + g_string_append_printf (*text, + "%s: %s\n", + header_name, + header_val); +} + +static void +clear_textbuffer (GtkTextBuffer *textbuffer) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_bounds (textbuffer, &start, &end); + gtk_text_buffer_delete (textbuffer, &start, &end); +} + + +static void +update_packet_details (GssdpDeviceSnifferWindow *self, + const char *text, unsigned int len) +{ + GtkTextBuffer *textbuffer; + + textbuffer = gtk_text_view_get_buffer (self->packet_details_textview); + + clear_textbuffer (textbuffer); + gtk_text_buffer_insert_at_cursor (textbuffer, text, len); +} + + +static void +display_packet (GssdpDeviceSnifferWindow *self, + time_t arrival_time, + SoupMessageHeaders *packet_headers) +{ + GString *text; + + text = g_string_new (""); + g_string_printf (text, "Received on: %s\nHeaders:\n\n", + ctime (&arrival_time)); + + soup_message_headers_foreach (packet_headers, + (SoupMessageHeadersForeachFunc) + packet_header_to_string, + &text); + + update_packet_details (self, text->str, text->len); + g_string_free (text, TRUE); +} + + +static void +on_packet_selected (GssdpDeviceSnifferWindow *self, + GtkTreeSelection *selection) +{ + GtkTreeModel *model; + GtkTreeIter iter; + time_t *arrival_time; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + SoupMessageHeaders *packet_headers; + + gtk_tree_model_get (model, + &iter, + PACKET_STORE_COLUMN_HEADERS, + &packet_headers, + PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, + &arrival_time, + -1); + display_packet (self, *arrival_time, packet_headers); + g_boxed_free (SOUP_TYPE_MESSAGE_HEADERS, packet_headers); + } + + else + update_packet_details (self, "", 0); +} + +static gboolean +on_treeview_popup_menu (GtkWidget *tv, GdkEventButton *event, gpointer user_data) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + gtk_menu_popup_at_pointer (GTK_MENU (user_data), (GdkEvent *)event); + } + + return FALSE; +} + + +static void +gssdp_device_sniffer_window_class_init (GssdpDeviceSnifferWindowClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, + SNIFFER_WINDOW_TEMPLATE); + gtk_widget_class_bind_template_child (widget_class, + GssdpDeviceSnifferWindow, + packet_treeview); + gtk_widget_class_bind_template_child (widget_class, + GssdpDeviceSnifferWindow, + device_details_treeview); + gtk_widget_class_bind_template_child (widget_class, + GssdpDeviceSnifferWindow, + window_menu); + gtk_widget_class_bind_template_child (widget_class, + GssdpDeviceSnifferWindow, + packet_details_textview); +} + +static void +gssdp_device_sniffer_window_init (GssdpDeviceSnifferWindow *self) +{ + GtkBuilder *builder; + GError *error = NULL; + GtkTreeSelection *selection; + GtkWidget *menu; + + gtk_widget_init_template (GTK_WIDGET (self)); + + builder = gtk_builder_new (); + gtk_builder_add_from_resource (builder, SNIFFER_WINDOW_MENU_RESOURCE, &error); + + gtk_menu_button_set_menu_model (self->window_menu, + G_MENU_MODEL (gtk_builder_get_object (builder, + "sniffer-window-menu"))); + g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self); + + + selection = gtk_tree_view_get_selection (self->packet_treeview); + g_signal_connect_swapped (selection, + "changed", + G_CALLBACK (on_packet_selected), + self); + + menu = gtk_menu_new_from_model (G_MENU_MODEL (gtk_builder_get_object (builder, + "sniffer-context-menu"))); + g_object_unref (builder); + + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self->packet_treeview), NULL); + g_signal_connect (G_OBJECT (self->packet_treeview), + "button-press-event", + G_CALLBACK (on_treeview_popup_menu), + menu); + + /* SSDP initialization */ + self->client = g_initable_new (GSSDP_TYPE_CLIENT, + NULL, + &error, + //"address-family", G_SOCKET_FAMILY_IPV4, + //"interface", NULL, + NULL); + self->browser = gssdp_resource_browser_new (self->client, + GSSDP_ALL_RESOURCES); + + g_signal_connect_swapped (self->client, + "message-received", + G_CALLBACK (on_ssdp_message), + self); + g_signal_connect_swapped (self->browser, + "resource-available", + G_CALLBACK (on_resource_available), + self); + + g_signal_connect_swapped (self->browser, + "resource-unavailable", + G_CALLBACK (on_resource_unavailable), + self); + + self->capture_packets = TRUE; + gssdp_resource_browser_set_active (self->browser, TRUE); +} + +static const char *message_types[] = {"M-SEARCH", "RESPONSE", "NOTIFY"}; + +static char ** +packet_to_treeview_data (GssdpDeviceSnifferWindow *self, + const gchar *from_ip, + time_t arrival_time, + _GSSDPMessageType type, + SoupMessageHeaders *headers) +{ + char **packet_data; + const char *target; + struct tm *tm; + + packet_data = g_new0 (char *, 6); + + /* Set the Time */ + tm = localtime (&arrival_time); + packet_data[0] = g_strdup_printf ("%02d:%02d", tm->tm_hour, tm->tm_min); + + /* Now the Source Address */ + packet_data[1] = g_strdup (from_ip); + + packet_data[2] = g_strdup (gssdp_client_get_interface (self->client)); + + /* Now the Packet Type */ + packet_data[3] = g_strdup (message_types[type]); + + /* Now the Packet Information */ + if (type == _GSSDP_DISCOVERY_RESPONSE) + target = soup_message_headers_get_one (headers, "ST"); + else + target = soup_message_headers_get_one (headers, "NT"); + + packet_data[4] = g_strdup (target); + packet_data[5] = NULL; + + return packet_data; +} + + +static void +append_packet (GssdpDeviceSnifferWindow *self, + const gchar *from_ip, + time_t arrival_time, + _GSSDPMessageType type, + SoupMessageHeaders *headers) +{ + GtkListStore *liststore; + GtkTreeIter iter; + char **packet_data; + + liststore = GTK_LIST_STORE ( + gtk_tree_view_get_model (self->packet_treeview)); + + packet_data = packet_to_treeview_data (self, + from_ip, + arrival_time, + type, + headers); + + gtk_list_store_insert_with_values (liststore, &iter, 0, + PACKET_STORE_COLUMN_TIME, packet_data[0], + PACKET_STORE_COLUMN_IP, packet_data[1], + PACKET_STORE_COLUMN_INTERFACE, packet_data[2], + PACKET_STORE_COLUMN_PACKET_TYPE, packet_data[3], + PACKET_STORE_COLUMN_TARGET, packet_data[4], + PACKET_STORE_COLUMN_HEADERS, headers, + PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, + g_memdup (&arrival_time, sizeof (time_t)), + -1); + g_strfreev (packet_data); +} + + +static void +on_ssdp_message (GssdpDeviceSnifferWindow *self, + const gchar *from_ip, + gushort from_port, + _GSSDPMessageType type, + SoupMessageHeaders *headers, + GSSDPClient *client) +{ + time_t arrival_time; + + arrival_time = time (NULL); + + if (type == _GSSDP_DISCOVERY_REQUEST) + return; + + if (self->ip_filter != NULL && strcmp (self->ip_filter, from_ip) != 0) + return; + + if (!self->capture_packets) + return; + + append_packet (self, from_ip, arrival_time, type, headers); +} + +static gboolean +find_device (GtkTreeModel *model, const char *uuid, GtkTreeIter *iter) +{ + gboolean found = FALSE; + gboolean more; + + more = gtk_tree_model_get_iter_first (model, iter); + while (more) { + char *device_uuid; + gtk_tree_model_get (model, + iter, + 0, &device_uuid, -1); + found = g_strcmp0 (device_uuid, uuid) == 0; + g_free (device_uuid); + + if (found) + break; + more = gtk_tree_model_iter_next (model, iter); + } + + return found; +} + +static void +append_device (GssdpDeviceSnifferWindow *self, + const char *uuid, + const char *first_notify, + const char *device_type, + const char *location) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (self->device_details_treeview); + + if (!find_device (model, uuid, &iter)) { + gtk_list_store_insert_with_values (GTK_LIST_STORE (model), + &iter, 0, + DEVICE_STORE_COLUMN_UUID, uuid, + DEVICE_STORE_COLUMN_FIRST_SEEN, first_notify, + DEVICE_STORE_COLUMN_LOCATION, location, -1); + } + + if (device_type) { + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + DEVICE_STORE_COLUMN_TYPE, device_type, -1); + } +} + + +static void +on_resource_available (GssdpDeviceSnifferWindow *self, + const char *usn, + GList *locations, + GSSDPResourceBrowser *ssdp_resource_browser) +{ + char **usn_tokens; + char *uuid; + char *device_type = NULL; + time_t current_time; + struct tm *tm; + char *first_notify; + + current_time = time (NULL); + tm = localtime (¤t_time); + first_notify = g_strdup_printf ("%02d:%02d", + tm->tm_hour, tm->tm_min); + + usn_tokens = g_strsplit (usn, "::", -1); + g_assert (usn_tokens != NULL && usn_tokens[0] != NULL); + + uuid = usn_tokens[0] + 5; /* skip the prefix 'uuid:' */ + + if (usn_tokens[1] && strlen(usn_tokens[1]) != 0) { + char **urn_tokens; + + urn_tokens = g_strsplit (usn_tokens[1], ":device:", -1); + + if (urn_tokens[1]) + device_type = g_strdup (urn_tokens[1]); + g_strfreev (urn_tokens); + } + + /* Device Announcement */ + append_device (self, + uuid, + first_notify, + device_type, + (char *) locations->data); + + g_free (device_type); + g_free (first_notify); + g_strfreev (usn_tokens); +} + +static void +on_resource_unavailable (GssdpDeviceSnifferWindow *self, + const char *usn, + GSSDPResourceBrowser *ssdp_resource_browser) +{ +} diff --git a/tools/gssdp-device-sniffer-window.h b/tools/gssdp-device-sniffer-window.h new file mode 100644 index 0000000..f45db0f --- /dev/null +++ b/tools/gssdp-device-sniffer-window.h @@ -0,0 +1,29 @@ +/* gssdp-device-sniffer-window.h + * + * Copyright 2019 Jens Georg + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GSSDP_DEVICE_SNIFFER_TYPE_WINDOW (gssdp_device_sniffer_window_get_type()) + +G_DECLARE_FINAL_TYPE (GssdpDeviceSnifferWindow, gssdp_device_sniffer_window, GSSDP_DEVICE_SNIFFER, WINDOW, GtkApplicationWindow) + +G_END_DECLS diff --git a/tools/gssdp-device-sniffer.ui b/tools/gssdp-device-sniffer-window.ui index f1acc7c..ab578b2 100644 --- a/tools/gssdp-device-sniffer.ui +++ b/tools/gssdp-device-sniffer-window.ui @@ -2,9 +2,38 @@ <!-- Generated with glade 3.22.1 --> <interface> <requires lib="gtk+" version="3.20"/> - <object class="GtkWindow" id="main-window"> + <object class="GtkListStore" id="device_liststore"> + <columns> + <!-- column-name uuid --> + <column type="gchararray"/> + <!-- column-name first_seen --> + <column type="gchararray"/> + <!-- column-name type --> + <column type="gchararray"/> + <!-- column-name location --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkListStore" id="packet_liststore"> + <columns> + <!-- column-name time --> + <column type="gchararray"/> + <!-- column-name ip --> + <column type="gchararray"/> + <!-- column-name interface --> + <column type="gchararray"/> + <!-- column-name packet_type --> + <column type="gchararray"/> + <!-- column-name target --> + <column type="gchararray"/> + <!-- column-name headers --> + <column type="SoupMessageHeaders"/> + <!-- column-name raw_arrival_time --> + <column type="gpointer"/> + </columns> + </object> + <template class="GssdpDeviceSnifferWindow" parent="GtkApplicationWindow"> <property name="can_focus">False</property> - <signal name="delete-event" handler="on_delete_event" swapped="no"/> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> @@ -21,7 +50,7 @@ <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="tooltip_text" translatable="yes">Clear packet capture</property> - <signal name="clicked" handler="on_clear_packet_capture_activate" swapped="no"/> + <property name="action_name">win.clear-packet-capture</property> <child> <object class="GtkImage"> <property name="visible">True</property> @@ -43,7 +72,7 @@ <property name="receives_default">True</property> <property name="tooltip_text" translatable="yes">Toggle packet capture</property> <property name="active">True</property> - <signal name="toggled" handler="on_enable_packet_capture_activate" object="capture-image" swapped="no"/> + <property name="action_name">win.capture-packets</property> <child> <object class="GtkImage" id="capture-image"> <property name="visible">True</property> @@ -94,7 +123,7 @@ </object> </child> <child> - <object class="GtkMenuButton" id="window-menu"> + <object class="GtkMenuButton" id="window_menu"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> @@ -130,12 +159,68 @@ <property name="can_focus">True</property> <property name="shadow_type">in</property> <child> - <object class="GtkTreeView" id="packet-treeview"> + <object class="GtkTreeView" id="packet_treeview"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="model">packet_liststore</property> <child internal-child="selection"> <object class="GtkTreeSelection"/> </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Time</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Source Address</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Interface</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Packet Type</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">3</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Packet Information</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">4</attribute> + </attributes> + </child> + </object> + </child> </object> </child> </object> @@ -151,7 +236,7 @@ <property name="can_focus">True</property> <property name="shadow_type">in</property> <child> - <object class="GtkTextView" id="packet-details-textview"> + <object class="GtkTextView" id="packet_details_textview"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">False</property> @@ -180,12 +265,57 @@ <property name="margin_bottom">6</property> <property name="shadow_type">in</property> <child> - <object class="GtkTreeView" id="device-details-treeview"> + <object class="GtkTreeView" id="device_details_treeview"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="model">device_liststore</property> <child internal-child="selection"> <object class="GtkTreeSelection"/> </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Unique Identifier</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">First Notify</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Device Type</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Location</property> + <child> + <object class="GtkCellRendererText"/> + <attributes> + <attribute name="text">3</attribute> + </attributes> + </child> + </object> + </child> </object> </child> </object> @@ -197,157 +327,5 @@ </child> </object> </child> - </object> - <object class="GtkAboutDialog" id="about-dialog"> - <property name="can_focus">False</property> - <property name="resizable">False</property> - <property name="type_hint">normal</property> - <property name="transient_for">main-window</property> - <property name="copyright" translatable="yes">Copyright © 2007 Zeeshan Ali (Khattak)</property> - <property name="comments" translatable="yes">A Device Sniffer tool based on GSSDP framework. -Inspired by Intel Tools for UPnP.</property> - <property name="authors">Zeeshan Ali (Khattak) <zeeshanak@gnome.org></property> - <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property> - <property name="logo_icon_name"/> - <property name="license_type">lgpl-2-1</property> - <signal name="delete-event" handler="gtk_widget_hide" object="about-dialog" swapped="yes"/> - <signal name="response" handler="gtk_widget_hide" object="about-dialog" swapped="yes"/> - <child type="titlebar"> - <placeholder/> - </child> - <child internal-child="vbox"> - <object class="GtkBox" id="aboutdialog-vbox1"> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <child internal-child="action_area"> - <object class="GtkButtonBox" id="aboutdialog-action_area1"> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkDialog" id="address-filter-dialog"> - <property name="can_focus">False</property> - <property name="border_width">7</property> - <property name="resizable">False</property> - <property name="type_hint">dialog</property> - <property name="transient_for">main-window</property> - <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/> - <signal name="response" handler="on_address_filter_dialog_response" swapped="no"/> - <child type="titlebar"> - <placeholder/> - </child> - <child internal-child="vbox"> - <object class="GtkBox" id="dialog-vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">8</property> - <child internal-child="action_area"> - <object class="GtkButtonBox" id="dialog-action_area1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="closebutton1"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkBox" id="vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkRadioButton" id="dont-use-filter-radiobutton"> - <property name="label" translatable="yes">No filter, capture all traffic</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="halign">start</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="use-filter-radiobutton"> - <property name="label" translatable="yes">Use IP address filter</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="halign">start</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">dont-use-filter-radiobutton</property> - <signal name="toggled" handler="on_use_filter_radiobutton_toggled" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="address-entry0"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="margin_left">23</property> - <property name="max_length">46</property> - <property name="activates_default">True</property> - <property name="width_chars">47</property> - <property name="caps_lock_warning">False</property> - <property name="placeholder_text" translatable="yes">IP address for filter</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - <action-widgets> - <action-widget response="-5">closebutton1</action-widget> - </action-widgets> - </object> + </template> </interface> diff --git a/tools/gssdp-device-sniffer.c b/tools/gssdp-device-sniffer.c deleted file mode 100644 index 6eeceba..0000000 --- a/tools/gssdp-device-sniffer.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright (C) 2007 Zeeshan Ali (Khattak) <zeeshanak@gnome.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <libgssdp/gssdp.h> -#include <libgssdp/gssdp-client-private.h> -#include <libsoup/soup.h> -#include <gtk/gtk.h> -#include <string.h> -#include <stdlib.h> - -#define UI_RESOURCE "/org/gupnp/GSSDP/DeviceSniffer.ui" -#define MENU_RESOURCE "/org/gupnp/GSSDP/WindowMenu.ui" - -static char *interface = NULL; - -typedef enum { - PACKET_STORE_COLUMN_TIME, - PACKET_STORE_COLUMN_IP, - PACKET_STORE_COLUMN_INTERFACE, - PACKET_STORE_COLUMN_PACKET_TYPE, - PACKET_STORE_COLUMN_TARGET, - PACKET_STORE_COLUMN_HEADERS, - PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME -} PACKET_STORE_COLUMNS; - -typedef enum { - DEVICE_STORE_COLUMN_UUID, - DEVICE_STORE_COLUMN_FIRST_SEEN, - DEVICE_STORE_COLUMN_TYPE, - DEVICE_STORE_COLUMN_LOCATION -} DEVICE_STORE_COLUMNS; - - -GtkBuilder *builder; -GSSDPResourceBrowser *resource_browser; -GSSDPClient *client; -char *ip_filter = NULL; -gboolean capture_packets = TRUE; -gboolean prefer_v6 = FALSE; - -GOptionEntry entries[] = -{ - {"interface", 'i', 0, G_OPTION_ARG_STRING, &interface, "Network interface to listen on", NULL }, - { "prefer-v6", '6', 0, G_OPTION_ARG_NONE, &prefer_v6, "Prefer IPv6 for the client", NULL }, - { "prefer-v4", '4', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &prefer_v6, "Prefer IPv4 for the client", NULL }, - { NULL } -}; - -void -on_enable_packet_capture_activate (GtkToggleButton *menuitem, - gpointer user_data); - -void -on_address_filter_dialog_response (GtkDialog *dialog, - gint response, - gpointer user_data); - -gboolean -on_delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data); - -G_MODULE_EXPORT void -on_enable_packet_capture_activate (GtkToggleButton *menuitem, - gpointer user_data) -{ - const gchar *icon_name = NULL; - - capture_packets = gtk_toggle_button_get_active (menuitem); - icon_name = capture_packets - ? "media-playback-stop-symbolic" - : "media-playback-start-symbolic"; - - gtk_image_set_from_icon_name (GTK_IMAGE (user_data), - icon_name, - GTK_ICON_SIZE_BUTTON); -} - -static void -clear_packet_treeview (void) -{ - GtkWidget *treeview; - GtkTreeModel *model; - GtkTreeIter iter; - gboolean more; - time_t *arrival_time; - - treeview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-treeview")); - g_assert (treeview != NULL); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); - more = gtk_tree_model_get_iter_first (model, &iter); - - while (more) { - gtk_tree_model_get (model, - &iter, - PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, &arrival_time, -1); - g_free (arrival_time); - more = gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } -} - -static void -on_details_activate (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) -{ - GtkWidget *scrolled_window = NULL; - - scrolled_window = GTK_WIDGET (gtk_builder_get_object (builder, - "packet-details-scrolledwindow")); - - g_object_set (G_OBJECT (scrolled_window), "visible", g_variant_get_boolean (parameter), NULL); - g_simple_action_set_state (action, parameter); -} - -static void -packet_header_to_string (const char *header_name, - const char *header_val, - GString **text) -{ - g_string_append_printf (*text, "%s: %s\n", - header_name, - header_val); -} - -static void -clear_textbuffer (GtkTextBuffer *textbuffer) -{ - GtkTextIter start, end; - - gtk_text_buffer_get_bounds (textbuffer, &start, &end); - gtk_text_buffer_delete (textbuffer, &start, &end); -} - -static void -update_packet_details (const char *text, unsigned int len) -{ - GtkWidget *textview; - GtkTextBuffer *textbuffer; - - textview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-details-textview")); - g_assert (textview != NULL); - textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); - - clear_textbuffer (textbuffer); - gtk_text_buffer_insert_at_cursor (textbuffer, text, len); -} - -static void -display_packet (time_t arrival_time, SoupMessageHeaders *packet_headers) -{ - GString *text; - - text = g_string_new (""); - g_string_printf (text, "Received on: %s\nHeaders:\n\n", - ctime (&arrival_time)); - - soup_message_headers_foreach (packet_headers, - (SoupMessageHeadersForeachFunc) - packet_header_to_string, - &text); - - update_packet_details (text->str, text->len); - g_string_free (text, TRUE); -} - -static void -on_packet_selected (GtkTreeSelection *selection, - G_GNUC_UNUSED gpointer user_data) -{ - GtkTreeModel *model; - GtkTreeIter iter; - time_t *arrival_time; - - if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - SoupMessageHeaders *packet_headers; - - gtk_tree_model_get (model, - &iter, - PACKET_STORE_COLUMN_HEADERS, - &packet_headers, - PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, - &arrival_time, - -1); - display_packet (*arrival_time, packet_headers); - g_boxed_free (SOUP_TYPE_MESSAGE_HEADERS, packet_headers); - } - - else - update_packet_details ("", 0); -} - -static const char *message_types[] = {"M-SEARCH", "RESPONSE", "NOTIFY"}; - -static char ** -packet_to_treeview_data (const gchar *from_ip, - time_t arrival_time, - _GSSDPMessageType type, - SoupMessageHeaders *headers) -{ - char **packet_data; - const char *target; - struct tm *tm; - - packet_data = g_new0 (char *, 6); - - /* Set the Time */ - tm = localtime (&arrival_time); - packet_data[0] = g_strdup_printf ("%02d:%02d", tm->tm_hour, tm->tm_min); - - /* Now the Source Address */ - packet_data[1] = g_strdup (from_ip); - - packet_data[2] = g_strdup (gssdp_client_get_interface (client)); - - /* Now the Packet Type */ - packet_data[3] = g_strdup (message_types[type]); - - /* Now the Packet Information */ - if (type == _GSSDP_DISCOVERY_RESPONSE) - target = soup_message_headers_get_one (headers, "ST"); - else - target = soup_message_headers_get_one (headers, "NT"); - - packet_data[4] = g_strdup (target); - packet_data[5] = NULL; - - return packet_data; -} - -static void -append_packet (const gchar *from_ip, - time_t arrival_time, - _GSSDPMessageType type, - SoupMessageHeaders *headers) -{ - GtkWidget *treeview; - GtkListStore *liststore; - GtkTreeIter iter; - char **packet_data; - - treeview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-treeview")); - g_assert (treeview != NULL); - liststore = GTK_LIST_STORE ( - gtk_tree_view_get_model (GTK_TREE_VIEW (treeview))); - g_assert (liststore != NULL); - - packet_data = packet_to_treeview_data (from_ip, - arrival_time, - type, - headers); - - gtk_list_store_insert_with_values (liststore, &iter, 0, - PACKET_STORE_COLUMN_TIME, packet_data[0], - PACKET_STORE_COLUMN_IP, packet_data[1], - PACKET_STORE_COLUMN_INTERFACE, packet_data[2], - PACKET_STORE_COLUMN_PACKET_TYPE, packet_data[3], - PACKET_STORE_COLUMN_TARGET, packet_data[4], - PACKET_STORE_COLUMN_HEADERS, headers, - PACKET_STORE_COLUMN_RAW_ARRIVAL_TIME, - g_memdup (&arrival_time, sizeof (time_t)), - -1); - g_strfreev (packet_data); -} - -static void -on_ssdp_message (G_GNUC_UNUSED GSSDPClient *ssdp_client, - G_GNUC_UNUSED const gchar *from_ip, - G_GNUC_UNUSED gushort from_port, - _GSSDPMessageType type, - SoupMessageHeaders *headers, - G_GNUC_UNUSED gpointer user_data) -{ - time_t arrival_time; - - arrival_time = time (NULL); - - if (type == _GSSDP_DISCOVERY_REQUEST) - return; - if (ip_filter != NULL && strcmp (ip_filter, from_ip) != 0) - return; - if (!capture_packets) - return; - - append_packet (from_ip, arrival_time, type, headers); -} - -static gboolean -find_device (GtkTreeModel *model, const char *uuid, GtkTreeIter *iter) -{ - gboolean found = FALSE; - gboolean more; - - more = gtk_tree_model_get_iter_first (model, iter); - while (more) { - char *device_uuid; - gtk_tree_model_get (model, - iter, - 0, &device_uuid, -1); - found = g_strcmp0 (device_uuid, uuid) == 0; - g_free (device_uuid); - - if (found) - break; - more = gtk_tree_model_iter_next (model, iter); - } - - return found; -} - -static void -append_device (const char *uuid, - const char *first_notify, - const char *device_type, - const char *location) -{ - GtkWidget *treeview; - GtkTreeModel *model; - GtkTreeIter iter; - - treeview = GTK_WIDGET(gtk_builder_get_object (builder, "device-details-treeview")); - g_assert (treeview != NULL); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); - g_assert (model != NULL); - - if (!find_device (model, uuid, &iter)) { - gtk_list_store_insert_with_values (GTK_LIST_STORE (model), - &iter, 0, - DEVICE_STORE_COLUMN_UUID, uuid, - DEVICE_STORE_COLUMN_FIRST_SEEN, first_notify, - DEVICE_STORE_COLUMN_LOCATION, location, -1); - } - - if (device_type) { - gtk_list_store_set (GTK_LIST_STORE (model), &iter, - DEVICE_STORE_COLUMN_TYPE, device_type, -1); - } -} - -static void -resource_available_cb (G_GNUC_UNUSED GSSDPResourceBrowser *ssdp_resource_browser, - const char *usn, - GList *locations) -{ - - char **usn_tokens; - char *uuid; - char *device_type = NULL; - time_t current_time; - struct tm *tm; - char *first_notify; - - current_time = time (NULL); - tm = localtime (¤t_time); - first_notify = g_strdup_printf ("%02d:%02d", - tm->tm_hour, tm->tm_min); - - usn_tokens = g_strsplit (usn, "::", -1); - g_assert (usn_tokens != NULL && usn_tokens[0] != NULL); - - uuid = usn_tokens[0] + 5; /* skip the prefix 'uuid:' */ - - if (usn_tokens[1] && strlen(usn_tokens[1]) != 0) { - char **urn_tokens; - - urn_tokens = g_strsplit (usn_tokens[1], ":device:", -1); - - if (urn_tokens[1]) - device_type = g_strdup (urn_tokens[1]); - g_strfreev (urn_tokens); - } - - /* Device Announcement */ - append_device (uuid, - first_notify, - device_type, - (char *) locations->data); - - if (device_type) - g_free (device_type); - g_free (first_notify); - g_strfreev (usn_tokens); -} - -static void -remove_device (const char *uuid) -{ - GtkWidget *treeview; - GtkTreeModel *model; - GtkTreeIter iter; - - treeview = GTK_WIDGET(gtk_builder_get_object (builder, "device-details-treeview")); - g_assert (treeview != NULL); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); - g_assert (model != NULL); - - if (find_device (model, uuid, &iter)) { - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); - } -} - -static void -resource_unavailable_cb (G_GNUC_UNUSED GSSDPResourceBrowser *ssdp_resource_browser, - const char *usn) -{ - char **usn_tokens; - char *uuid; - - usn_tokens = g_strsplit (usn, "::", -1); - g_assert (usn_tokens != NULL && usn_tokens[0] != NULL); - uuid = usn_tokens[0] + 5; /* skip the prefix 'uuid:' */ - - remove_device (uuid); - - g_strfreev (usn_tokens); -} - -G_MODULE_EXPORT -void -on_use_filter_radiobutton_toggled (GtkToggleButton *togglebutton, - G_GNUC_UNUSED gpointer user_data) -{ - GtkWidget *filter_hbox; - gboolean use_filter; - - filter_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "address-entry0")); - g_assert (filter_hbox != NULL); - - use_filter = gtk_toggle_button_get_active (togglebutton); - gtk_widget_set_sensitive (filter_hbox, use_filter); -} - -static char * -get_ip_filter (void) -{ - GtkEntry *entry; - GInetAddress *addr; - - entry = GTK_ENTRY (gtk_builder_get_object (builder, "address-entry0")); - addr = g_inet_address_new_from_string (gtk_entry_get_text (entry)); - - if (addr == NULL) { - g_warning ("Filter not a valid IP address"); - - return NULL; - } - g_object_unref (addr); - - return g_strdup (gtk_entry_get_text (entry)); -} - -G_MODULE_EXPORT -void -on_address_filter_dialog_response (GtkDialog *dialog, - G_GNUC_UNUSED gint response, - G_GNUC_UNUSED gpointer user_data) -{ - GtkWidget *use_filter_radio; - - gtk_widget_hide (GTK_WIDGET (dialog)); - - use_filter_radio = GTK_WIDGET(gtk_builder_get_object (builder, "use-filter-radiobutton")); - g_assert (use_filter_radio != NULL); - - if (response != GTK_RESPONSE_OK) - return; - - g_free (ip_filter); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (use_filter_radio))) { - ip_filter = get_ip_filter (); - } - - else - ip_filter = NULL; -} - -static GtkTreeModel * -create_packet_treemodel (void) -{ - GtkListStore *store; - - store = gtk_list_store_new (7, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - SOUP_TYPE_MESSAGE_HEADERS, - G_TYPE_POINTER); - - return GTK_TREE_MODEL (store); -} - -static GtkTreeModel * -create_device_treemodel (void) -{ - GtkListStore *store; - - store = gtk_list_store_new (4, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); - - return GTK_TREE_MODEL (store); -} - -static void -setup_treeview (GtkWidget *treeview, - GtkTreeModel *model, - const char *headers[]) -{ - int i; - - /* Set-up columns */ - for (i=0; headers[i] != NULL; i++) { - GtkCellRenderer *renderer; - - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_insert_column_with_attributes ( - GTK_TREE_VIEW (treeview), - -1, - headers[i], - renderer, - "text", i, - NULL); - } - - gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), - model); -} - -static gboolean -on_treeview_popup_menu (GtkWidget *tv, GdkEventButton *event, gpointer user_data) -{ - if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - gtk_menu_popup_at_pointer (GTK_MENU (user_data), (GdkEvent *)event); - } - - return FALSE; -} - -static void -setup_treeviews (void) -{ - GtkWidget *treeviews[2]; - GtkTreeModel *treemodels[2]; - const char *headers[2][7] = { {"Time", - "Source Address", - "Interface", - "Packet Type", - "Packet Information", - NULL }, {"Unique Identifier", - "First Notify", - "Device Type", - "Location", - NULL } }; - GtkTreeSelection *selection; - GtkWidget *menu = NULL; - int i; - - treeviews[0] = GTK_WIDGET(gtk_builder_get_object (builder, - "packet-treeview")); - g_assert (treeviews[0] != NULL); - treeviews[1] = GTK_WIDGET(gtk_builder_get_object (builder, - "device-details-treeview")); - g_assert (treeviews[1] != NULL); - - treemodels[0] = create_packet_treemodel (); - g_assert (treemodels[0] != NULL); - treemodels[1] = create_device_treemodel (); - g_assert (treemodels[1] != NULL); - - for (i=0; i<2; i++) - setup_treeview (treeviews[i], treemodels[i], headers[i]); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeviews[0])); - g_assert (selection != NULL); - g_signal_connect (selection, - "changed", - G_CALLBACK (on_packet_selected), - (gpointer *) treeviews[0]); - menu = gtk_menu_new_from_model (G_MENU_MODEL (gtk_builder_get_object (builder, - "sniffer-context-menu"))); - gtk_menu_attach_to_widget (GTK_MENU (menu), treeviews[0], NULL); - g_signal_connect (G_OBJECT (treeviews[0]), - "button-press-event", - G_CALLBACK (on_treeview_popup_menu), menu); -} - -G_MODULE_EXPORT -gboolean -on_delete_event (G_GNUC_UNUSED GtkWidget *widget, - G_GNUC_UNUSED GdkEvent *event, - G_GNUC_UNUSED gpointer user_data) -{ - gtk_main_quit (); - - return TRUE; -} - -static void -on_show_address_filter (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) -{ - GtkWidget *dialog = NULL; - - dialog = GTK_WIDGET (gtk_builder_get_object (builder, - "address-filter-dialog")); - gtk_widget_show (dialog); -} - -static void -on_set_address_filter (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) -{ - GtkWidget *treeview = NULL; - GtkTreeSelection *selection = NULL; - GtkTreeModel *model = NULL; - GtkTreeIter iter; - GtkWidget *use_filter_radio; - - treeview = GTK_WIDGET (gtk_builder_get_object (builder, "packet-treeview")); - use_filter_radio = GTK_WIDGET(gtk_builder_get_object (builder, "use-filter-radiobutton")); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); - gtk_tree_selection_get_selected (selection, &model, &iter); - g_free (ip_filter); - gtk_tree_model_get (model, &iter, - PACKET_STORE_COLUMN_IP, &ip_filter, -1); - gtk_entry_set_text (GTK_ENTRY (gtk_builder_get_object (builder, "address-entry0")), - ip_filter); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (use_filter_radio), TRUE); -} - -static void -on_about (GSimpleAction *action, GVariant *parameter, gpointer user_data) -{ - GtkWidget *dialog = NULL; - - dialog = GTK_WIDGET (gtk_builder_get_object (builder, - "about-dialog")); - gtk_widget_show (dialog); -} - - -G_MODULE_EXPORT -void -on_clear_packet_capture_activate (G_GNUC_UNUSED GtkMenuItem *menuitem, - G_GNUC_UNUSED gpointer user_data) -{ - clear_packet_treeview (); -} - -static void -on_trigger_rescan (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) -{ - gssdp_resource_browser_rescan (resource_browser); -} - -static GActionEntry actions[] = { - { "trigger-rescan", on_trigger_rescan }, - { "set-address-filter", on_set_address_filter }, - { "show-packet-details", NULL, NULL, "true", on_details_activate }, - { "show-address-filter", on_show_address_filter }, - { "about", on_about } -}; - -static gboolean -init_ui (gint *argc, gchar **argv[]) -{ - GtkWidget *main_window; - gint window_width, window_height; - GError *error = NULL; - GOptionContext *context; - double w, h; - GSimpleActionGroup *group = NULL; - - context = g_option_context_new ("- graphical SSDP debug tool"); - g_option_context_add_main_entries (context, entries, NULL); - g_option_context_add_group (context, gtk_get_option_group (TRUE)); - if (!g_option_context_parse (context, argc, argv, &error)) { - g_print ("Failed to parse options: %s\n", error->message); - g_error_free (error); - - return FALSE; - } - - builder = gtk_builder_new(); - if (gtk_builder_add_from_resource(builder, UI_RESOURCE, NULL) == 0) - return FALSE; - - if (gtk_builder_add_from_resource (builder, MENU_RESOURCE, NULL) == 0) - return FALSE; - - gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (gtk_builder_get_object (builder, "window-menu")), - G_MENU_MODEL (gtk_builder_get_object (builder, "sniffer-window-menu"))); - - main_window = GTK_WIDGET(gtk_builder_get_object (builder, "main-window")); - g_assert (main_window != NULL); - - group = g_simple_action_group_new (); - gtk_widget_insert_action_group (GTK_WIDGET (main_window), "win", G_ACTION_GROUP (group)); - g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), NULL); - - -#if GTK_CHECK_VERSION(3,22,0) - gtk_widget_realize (main_window); - { - GdkWindow *window = gtk_widget_get_window (main_window); - GdkDisplay *display = gdk_display_get_default (); - GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, - window); - GdkRectangle rectangle; - - gdk_monitor_get_geometry (monitor, &rectangle); - w = rectangle.width * 0.75; - h = rectangle.height * 0.75; - } - -#else - w = gdk_screen_width () * 0.75; - h = gdk_screen_height () * 0.75; -#endif - - window_width = CLAMP ((int) w, 10, 1000); - window_height = CLAMP ((int) h, 10, 800); - gtk_window_set_default_size (GTK_WINDOW (main_window), - window_width, - window_height); - - gtk_builder_connect_signals (builder, NULL); - setup_treeviews (); - gtk_widget_show_all (main_window); - - return TRUE; -} - -static void -deinit_ui (void) -{ - g_object_unref (builder); -} - -static gboolean -init_upnp (void) -{ - GError *error; - - error = NULL; - client = g_initable_new (GSSDP_TYPE_CLIENT, - NULL, - &error, - "address-family", prefer_v6 ? G_SOCKET_FAMILY_IPV6 : G_SOCKET_FAMILY_IPV4, - "interface", interface, - NULL); - if (error) { - g_printerr ("Error creating the GSSDP client: %s\n", - error->message); - - g_error_free (error); - - return FALSE; - } - - resource_browser = gssdp_resource_browser_new (client, - GSSDP_ALL_RESOURCES); - - g_signal_connect (client, - "message-received", - G_CALLBACK (on_ssdp_message), - NULL); - g_signal_connect (resource_browser, - "resource-available", - G_CALLBACK (resource_available_cb), - NULL); - g_signal_connect (resource_browser, - "resource-unavailable", - G_CALLBACK (resource_unavailable_cb), - NULL); - - gssdp_resource_browser_set_active (resource_browser, TRUE); - - return TRUE; -} - -static void -deinit_upnp (void) -{ - g_object_unref (resource_browser); - g_object_unref (client); -} - -gint -main (gint argc, gchar *argv[]) -{ - if (!init_ui (&argc, &argv)) { - return -2; - } - - if (!init_upnp ()) { - return -3; - } - - gtk_main (); - - deinit_upnp (); - deinit_ui (); - - return 0; -} diff --git a/tools/gssdp-device-sniffer.gresource.xml b/tools/gssdp-device-sniffer.gresource.xml index ec8dc93..0cda3c4 100644 --- a/tools/gssdp-device-sniffer.gresource.xml +++ b/tools/gssdp-device-sniffer.gresource.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <gresources> - <gresource prefix="/org/gupnp/GSSDP"> - <file preprocess="xml-stripblanks" alias="DeviceSniffer.ui">gssdp-device-sniffer.ui</file> - <file alias="WindowMenu.ui">window-menu.ui</file> + <gresource prefix="/org/gnome/GssdpDeviceSniffer"> + <file>gssdp-device-sniffer-window.ui</file> + <file>window-menu.ui</file> </gresource> </gresources> diff --git a/tools/main.c b/tools/main.c new file mode 100644 index 0000000..c8f9fdc --- /dev/null +++ b/tools/main.c @@ -0,0 +1,132 @@ +/* main.c + * + * Copyright 2019 Jens Georg + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib/gi18n.h> +#include <libsoup/soup.h> + +#include "gssdp-device-sniffer-window.h" + +static void +on_activate (GtkApplication *app) +{ + GtkWindow *window; + double w, h; + gint window_width, window_height; + + g_type_ensure (soup_message_headers_get_type ()); + + /* It's good practice to check your parameters at the beginning of the + * function. It helps catch errors early and in development instead of + * by your users. + */ + g_assert (GTK_IS_APPLICATION (app)); + + /* Get the current window or create one if necessary. */ + window = gtk_application_get_active_window (app); + + if (window == NULL) { +#if GTK_CHECK_VERSION(3,22,0) + { + gint px, py; + GdkScreen *pointer_screen; + GdkDisplay *display; + GdkDevice *pointer; + GdkMonitor *monitor; + GdkRectangle rectangle; + + display = gdk_display_get_default (); + pointer = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); + + gdk_device_get_position (pointer, + &pointer_screen, + &px, &py); + + monitor = gdk_display_get_monitor_at_point (display, px, py); + + + gdk_monitor_get_geometry (monitor, &rectangle); + w = rectangle.width * 0.75; + h = rectangle.height * 0.75; + } + +#else + w = gdk_screen_width () * 0.75; + h = gdk_screen_height () * 0.75; +#endif + + window_width = CLAMP ((int) w, 10, 1000); + window_height = CLAMP ((int) h, 10, 800); + + window = g_object_new (GSSDP_DEVICE_SNIFFER_TYPE_WINDOW, + "application", app, + "default-width", window_width, + "default-height", window_height, + NULL); + } + + /* Ask the window manager/compositor to present the window. */ + gtk_window_present (window); +} + +int +main (int argc, + char *argv[]) +{ + GtkApplication * app = NULL; + int ret; + +#if 0 + /* Set up gettext translations */ + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + /* + * Create a new GtkApplication. The application manages our main loop, + * application windows, integration with the window manager/compositor, and + * desktop features such as file opening and single-instance applications. + */ + app = gtk_application_new ("org.gnome.GssdpDeviceSniffer", G_APPLICATION_FLAGS_NONE); + + /* + * We connect to the activate signal to create a window when the application + * has been lauched. Additionally, this signal notifies us when the user + * tries to launch a "second instance" of the application. When they try + * to do that, we'll just present any existing window. + * + * Because we can't pass a pointer to any function type, we have to cast + * our "on_activate" function to a GCallback. + */ + g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); + + /* + * Run the application. This function will block until the applicaiton + * exits. Upon return, we have our exit code to return to the shell. (This + * is the code you see when you do `echo $?` after running a command in a + * terminal. + * + * Since GtkApplication inherits from GApplication, we use the parent class + * method "run". But we need to cast, which is what the "G_APPLICATION()" + * macro does. + */ + ret = g_application_run (G_APPLICATION (app), argc, argv); + + g_object_unref (G_OBJECT (app)); + + return ret; +} diff --git a/tools/meson.build b/tools/meson.build index bfd44d2..bea75d9 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -1,14 +1,16 @@ -resource = gnome.compile_resources( - 'org.gupnp.GSSDP.DeviceSniffer', - 'gssdp-device-sniffer.gresource.xml' +gssdp_device_sniffer_sources = [ + 'main.c', + 'gssdp-device-sniffer-window.c', +] + +gnome = import('gnome') + +gssdp_device_sniffer_sources += gnome.compile_resources('gssdp-device-sniffer-resources', + 'gssdp-device-sniffer.gresource.xml', + c_name: 'gssdp_device_sniffer' ) -sniffer = executable( - 'gssdp-device-sniffer', - [ - 'gssdp-device-sniffer.c', - resource - ], +executable('gssdp-device-sniffer', gssdp_device_sniffer_sources, dependencies : [gssdp, gtk], install: true, export_dynamic : true, |