[gssdp/wip/sniffer-rewrite] wip: refactor sniffer



commit 2c18c0533daf8e3b33a69e4e891f50a4210534b8
Author: Jens Georg <mail jensge org>
Date:   Sun Jan 20 20:07:43 2019 +0100

    wip: refactor sniffer

 tools/gssdp-device-sniffer-window.c                | 579 ++++++++++++++
 tools/gssdp-device-sniffer-window.h                |  29 +
 ...e-sniffer.ui => gssdp-device-sniffer-window.ui} | 300 ++++----
 tools/gssdp-device-sniffer.c                       | 830 ---------------------
 tools/gssdp-device-sniffer.gresource.xml           |   6 +-
 tools/main.c                                       | 132 ++++
 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 (&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
similarity index 51%
rename from tools/gssdp-device-sniffer.ui
rename to 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.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,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]