2 * Copyright (C) 2007 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include <libgssdp/gssdp.h>
21 #include <libgssdp/gssdp-client-private.h>
22 #include <libsoup/soup.h>
27 #define UI_FILE "gssdp-device-sniffer.ui"
30 static char *interface = NULL;
33 GSSDPResourceBrowser *resource_browser;
35 char *ip_filter = NULL;
36 gboolean capture_packets = TRUE;
38 GOptionEntry entries[] =
40 {"interface", 'i', 0, G_OPTION_ARG_STRING, &interface, "Network interface to listen on", NULL },
46 on_enable_packet_capture_activate (GtkCheckMenuItem *menuitem,
47 G_GNUC_UNUSED gpointer user_data)
49 capture_packets = gtk_check_menu_item_get_active (menuitem);
53 clear_packet_treeview ()
61 treeview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-treeview"));
62 g_assert (treeview != NULL);
63 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
64 more = gtk_tree_model_get_iter_first (model, &iter);
67 gtk_tree_model_get (model,
69 5, &arrival_time, -1);
70 g_free (arrival_time);
71 more = gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
77 on_details_activate (GtkWidget *scrolled_window, GtkCheckMenuItem *menuitem)
81 active = gtk_check_menu_item_get_active (menuitem);
82 g_object_set (G_OBJECT (scrolled_window), "visible", active, NULL);
86 packet_header_to_string (const char *header_name,
87 const char *header_val,
90 g_string_append_printf (*text, "%s: %s\n",
96 clear_textbuffer (GtkTextBuffer *textbuffer)
98 GtkTextIter start, end;
100 gtk_text_buffer_get_bounds (textbuffer, &start, &end);
101 gtk_text_buffer_delete (textbuffer, &start, &end);
105 update_packet_details (char *text, unsigned int len)
108 GtkTextBuffer *textbuffer;
110 textview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-details-textview"));
111 g_assert (textview != NULL);
112 textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
114 clear_textbuffer (textbuffer);
115 gtk_text_buffer_insert_at_cursor (textbuffer, text, len);
119 display_packet (time_t arrival_time, SoupMessageHeaders *packet_headers)
123 text = g_string_new ("");
124 g_string_printf (text, "Received on: %s\nHeaders:\n\n",
125 ctime (&arrival_time));
127 soup_message_headers_foreach (packet_headers,
128 (SoupMessageHeadersForeachFunc)
129 packet_header_to_string,
132 update_packet_details (text->str, text->len);
133 g_string_free (text, TRUE);
137 on_packet_selected (GtkTreeSelection *selection,
138 G_GNUC_UNUSED gpointer user_data)
142 time_t *arrival_time;
144 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
145 SoupMessageHeaders *packet_headers;
147 gtk_tree_model_get (model,
150 5, &arrival_time, -1);
151 display_packet (*arrival_time, packet_headers);
152 g_boxed_free (SOUP_TYPE_MESSAGE_HEADERS, packet_headers);
156 update_packet_details ("", 0);
161 on_clear_packet_capture_activate (G_GNUC_UNUSED GtkMenuItem *menuitem,
162 G_GNUC_UNUSED gpointer user_data)
164 clear_packet_treeview ();
167 static char *message_types[] = {"M-SEARCH", "RESPONSE", "NOTIFY"};
170 packet_to_treeview_data (const gchar *from_ip,
172 _GSSDPMessageType type,
173 SoupMessageHeaders *headers)
179 packet_data = g_malloc (sizeof (char *) * 5);
182 tm = localtime (&arrival_time);
183 packet_data[0] = g_strdup_printf ("%02d:%02d", tm->tm_hour, tm->tm_min);
185 /* Now the Source Address */
186 packet_data[1] = g_strdup (from_ip);
188 /* Now the Packet Type */
189 packet_data[2] = g_strdup (message_types[type]);
191 /* Now the Packet Information */
192 if (type == _GSSDP_DISCOVERY_RESPONSE)
193 target = soup_message_headers_get_one (headers, "ST");
195 target = soup_message_headers_get_one (headers, "NT");
197 packet_data[3] = g_strdup (target);
198 packet_data[4] = NULL;
204 append_packet (const gchar *from_ip,
206 _GSSDPMessageType type,
207 SoupMessageHeaders *headers)
210 GtkListStore *liststore;
214 treeview = GTK_WIDGET(gtk_builder_get_object (builder, "packet-treeview"));
215 g_assert (treeview != NULL);
216 liststore = GTK_LIST_STORE (
217 gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
218 g_assert (liststore != NULL);
220 packet_data = packet_to_treeview_data (from_ip,
224 gtk_list_store_insert_with_values (liststore, &iter, 0,
230 5, g_memdup (&arrival_time, sizeof (time_t)),
232 g_strfreev (packet_data);
236 on_ssdp_message (G_GNUC_UNUSED GSSDPClient *client,
237 G_GNUC_UNUSED const gchar *from_ip,
238 G_GNUC_UNUSED gushort from_port,
239 _GSSDPMessageType type,
240 SoupMessageHeaders *headers,
241 G_GNUC_UNUSED gpointer user_data)
245 arrival_time = time (NULL);
247 if (type == _GSSDP_DISCOVERY_REQUEST)
249 if (ip_filter != NULL && strcmp (ip_filter, from_ip) != 0)
251 if (!capture_packets)
254 append_packet (from_ip, arrival_time, type, headers);
258 find_device (GtkTreeModel *model, const char *uuid, GtkTreeIter *iter)
260 gboolean found = FALSE;
263 more = gtk_tree_model_get_iter_first (model, iter);
266 gtk_tree_model_get (model,
268 0, &device_uuid, -1);
269 if (device_uuid && strcmp (device_uuid, uuid) == 0) {
274 g_free (device_uuid);
275 more = gtk_tree_model_iter_next (model, iter);
282 append_device (const char *uuid,
283 const char *first_notify,
284 const char *device_type,
285 const char *location)
291 treeview = GTK_WIDGET(gtk_builder_get_object (builder, "device-details-treeview"));
292 g_assert (treeview != NULL);
293 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
294 g_assert (model != NULL);
296 if (!find_device (model, uuid, &iter)) {
297 gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
305 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
311 resource_available_cb (G_GNUC_UNUSED GSSDPResourceBrowser *resource_browser,
318 char *device_type = NULL;
323 current_time = time (NULL);
324 tm = localtime (¤t_time);
325 first_notify = g_strdup_printf ("%02d:%02d",
326 tm->tm_hour, tm->tm_min);
328 usn_tokens = g_strsplit (usn, "::", -1);
329 g_assert (usn_tokens != NULL && usn_tokens[0] != NULL);
331 uuid = usn_tokens[0] + 5; /* skip the prefix 'uuid:' */
336 urn_tokens = g_strsplit (usn_tokens[1], ":device:", -1);
339 device_type = g_strdup (urn_tokens[1]);
340 g_strfreev (urn_tokens);
343 /* Device Announcement */
347 (char *) locations->data);
350 g_free (device_type);
351 g_free (first_notify);
352 g_strfreev (usn_tokens);
356 remove_device (const char *uuid)
362 treeview = GTK_WIDGET(gtk_builder_get_object (builder, "device-details-treeview"));
363 g_assert (treeview != NULL);
364 model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
365 g_assert (model != NULL);
367 if (find_device (model, uuid, &iter)) {
368 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
373 resource_unavailable_cb (G_GNUC_UNUSED GSSDPResourceBrowser *resource_browser,
379 usn_tokens = g_strsplit (usn, "::", -1);
380 g_assert (usn_tokens != NULL && usn_tokens[0] != NULL);
381 uuid = usn_tokens[0] + 5; /* skip the prefix 'uuid:' */
383 remove_device (uuid);
385 g_strfreev (usn_tokens);
390 on_use_filter_radiobutton_toggled (GtkToggleButton *togglebutton,
391 G_GNUC_UNUSED gpointer user_data)
393 GtkWidget *filter_hbox;
396 filter_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "address-filter-hbox"));
397 g_assert (filter_hbox != NULL);
399 use_filter = gtk_toggle_button_get_active (togglebutton);
400 gtk_widget_set_sensitive (filter_hbox, use_filter);
410 ip = g_malloc (MAX_IP_LEN);
411 for (i=0; i<4; i++) {
416 sprintf (entry_name, "address-entry%d", i);
417 entry = GTK_WIDGET(gtk_builder_get_object (builder, entry_name));
418 g_assert (entry != NULL);
420 val = atoi (gtk_entry_get_text (GTK_ENTRY (entry)));
421 quad[i] = CLAMP (val, 0, 255);
423 sprintf (ip, "%u.%u.%u.%u", quad[0], quad[1], quad[2], quad[3]);
430 on_address_filter_dialog_response (GtkDialog *dialog,
431 G_GNUC_UNUSED gint response,
432 G_GNUC_UNUSED gpointer user_data)
434 GtkWidget *use_filter_radio;
436 gtk_widget_hide (GTK_WIDGET (dialog));
438 use_filter_radio = GTK_WIDGET(gtk_builder_get_object (builder, "use-filter-radiobutton"));
439 g_assert (use_filter_radio != NULL);
441 if (response != GTK_RESPONSE_OK)
445 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (use_filter_radio))) {
446 ip_filter = get_ip_filter ();
453 static GtkTreeModel *
454 create_packet_treemodel (void)
458 store = gtk_list_store_new (6,
463 SOUP_TYPE_MESSAGE_HEADERS,
466 return GTK_TREE_MODEL (store);
469 static GtkTreeModel *
470 create_device_treemodel (void)
474 store = gtk_list_store_new (4,
480 return GTK_TREE_MODEL (store);
484 setup_treeview (GtkWidget *treeview,
491 for (i=0; headers[i] != NULL; i++) {
492 GtkCellRenderer *renderer;
494 renderer = gtk_cell_renderer_text_new ();
495 gtk_tree_view_insert_column_with_attributes (
496 GTK_TREE_VIEW (treeview),
504 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
511 GtkWidget *treeviews[2];
512 GtkTreeModel *treemodels[2];
513 char *headers[2][6] = { {"Time",
516 "Packet Information",
517 NULL }, {"Unique Identifier",
522 GtkTreeSelection *selection;
525 treeviews[0] = GTK_WIDGET(gtk_builder_get_object (builder,
527 g_assert (treeviews[0] != NULL);
528 treeviews[1] = GTK_WIDGET(gtk_builder_get_object (builder,
529 "device-details-treeview"));
530 g_assert (treeviews[1] != NULL);
532 treemodels[0] = create_packet_treemodel ();
533 g_assert (treemodels[0] != NULL);
534 treemodels[1] = create_device_treemodel ();
535 g_assert (treemodels[1] != NULL);
538 setup_treeview (treeviews[i], treemodels[i], headers[i]);
540 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeviews[0]));
541 g_assert (selection != NULL);
542 g_signal_connect (selection,
544 G_CALLBACK (on_packet_selected),
545 (gpointer *) treeviews[0]);
550 on_delete_event (G_GNUC_UNUSED GtkWidget *widget,
551 G_GNUC_UNUSED GdkEvent *event,
552 G_GNUC_UNUSED gpointer user_data)
560 init_ui (gint *argc, gchar **argv[])
562 GtkWidget *main_window;
563 gint window_width, window_height;
564 gchar *ui_path = NULL;
565 GError *error = NULL;
566 GOptionContext *context;
568 context = g_option_context_new ("- graphical SSDP debug tool");
569 g_option_context_add_main_entries (context, entries, NULL);
570 g_option_context_add_group (context, gtk_get_option_group (TRUE));
571 if (!g_option_context_parse (context, argc, argv, &error)) {
572 g_print ("Failed to parse options: %s\n", error->message);
573 g_error_free (error);
578 /* Try to fetch the ui file from the CWD first */
580 if (!g_file_test (ui_path, G_FILE_TEST_EXISTS)) {
581 /* Then Try to fetch it from the system path */
582 ui_path = UI_DIR "/" UI_FILE;
584 if (!g_file_test (ui_path, G_FILE_TEST_EXISTS))
588 if (ui_path == NULL) {
589 g_critical ("Unable to load the GUI file %s", UI_FILE);
593 builder = gtk_builder_new();
594 if (gtk_builder_add_from_file(builder, ui_path, NULL) == 0)
597 main_window = GTK_WIDGET(gtk_builder_get_object (builder, "main-window"));
598 g_assert (main_window != NULL);
600 /* 80% of the screen but don't get bigger than 1000x800 */
601 window_width = CLAMP ((gdk_screen_width () * 80 / 100), 10, 1000);
602 window_height = CLAMP ((gdk_screen_height () * 80 / 100), 10, 800);
603 gtk_window_set_default_size (GTK_WINDOW (main_window),
607 gtk_builder_connect_signals (builder, NULL);
609 gtk_widget_show_all (main_window);
617 g_object_unref (builder);
626 client = g_initable_new (GSSDP_TYPE_CLIENT,
629 "interface", interface,
632 g_printerr ("Error creating the GSSDP client: %s\n",
635 g_error_free (error);
640 resource_browser = gssdp_resource_browser_new (client,
641 GSSDP_ALL_RESOURCES);
643 g_signal_connect (client,
645 G_CALLBACK (on_ssdp_message),
647 g_signal_connect (resource_browser,
648 "resource-available",
649 G_CALLBACK (resource_available_cb),
651 g_signal_connect (resource_browser,
652 "resource-unavailable",
653 G_CALLBACK (resource_unavailable_cb),
656 gssdp_resource_browser_set_active (resource_browser, TRUE);
664 g_object_unref (resource_browser);
665 g_object_unref (client);
669 main (gint argc, gchar *argv[])
671 if (!init_ui (&argc, &argv)) {