summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Georg <mail@jensge.org>2019-01-20 20:07:43 +0100
committerJens Georg <mail@jensge.org>2019-01-25 18:12:52 +0100
commit2c18c0533daf8e3b33a69e4e891f50a4210534b8 (patch)
tree1f69339ca0e2250f694315a9df48a9c183ce86a0
parent8e23bd2ffd8c4d80e1a170b818235e12fec71911 (diff)
downloadgssdp-wip/sniffer-rewrite.tar.gz
wip: refactor snifferwip/sniffer-rewrite
-rw-r--r--tools/gssdp-device-sniffer-window.c579
-rw-r--r--tools/gssdp-device-sniffer-window.h29
-rw-r--r--tools/gssdp-device-sniffer-window.ui (renamed from tools/gssdp-device-sniffer.ui)300
-rw-r--r--tools/gssdp-device-sniffer.c830
-rw-r--r--tools/gssdp-device-sniffer.gresource.xml6
-rw-r--r--tools/main.c132
-rw-r--r--tools/meson.build20
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 (&current_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) &lt;zeeshanak@gnome.org&gt;</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 (&current_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,