cde07a8ac8b1cd93df48a2b9b12fe18ca5e4a3ad
[obnox/wireshark/wip.git] / gtk / export_object.c
1 /* export_object.c
2  * Common routines for tracking & saving objects found in streams of data
3  * Copyright 2007, Stephen Fisher (see AUTHORS file)
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
24  * USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <gtk/gtk.h>
32
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #endif
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #include <epan/packet_info.h>
42 #include <epan/prefs.h>
43 #include <epan/tap.h>
44
45 #include <../alert_box.h>
46 #include <../simple_dialog.h>
47 #include <wsutil/file_util.h>
48
49 #include <gtk/dlg_utils.h>
50 #include <gtk/file_dlg.h>
51 #include <gtk/gui_utils.h>
52 #include <gtk/help_dlg.h>
53 #include <gtk/main.h>
54 #include <gtk/stock_icons.h>
55 #include "gtk/export_object.h"
56
57 enum {
58         EO_PKT_NUM_COLUMN,
59         EO_HOSTNAME_COLUMN,
60         EO_CONTENT_TYPE_COLUMN,
61         EO_BYTES_COLUMN,
62         EO_FILENAME_COLUMN,
63         EO_NUM_COLUMNS /* must be last */
64 };
65
66
67 static void
68 eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path,
69                      GtkTreeIter *iter _U_, gpointer arg)
70 {
71         export_object_list_t *object_list = arg;
72         export_object_entry_t *entry;
73
74         gint *path_index;
75
76         if((path_index = gtk_tree_path_get_indices(path)) == NULL)
77                 /* Row not found in tree - shouldn't happen */
78                 return;
79
80         object_list->row_selected = path_index[0];
81
82         /* Select the corresponding packet in the packet list */
83         entry = g_slist_nth_data(object_list->entries,
84                                  object_list->row_selected);
85         cf_goto_frame(&cfile, entry->pkt_num);
86 }
87
88 static void
89 eo_remember_row_num(GtkTreeSelection *sel, gpointer data)
90 {
91         gtk_tree_selection_selected_foreach(sel, eo_remember_this_row, data);
92 }
93
94
95 /* Called when the Export Object window is closed in any way */
96 static void
97 eo_win_destroy_cb(GtkWindow *win _U_, gpointer data)
98 {
99         export_object_list_t *object_list = data;
100         export_object_entry_t *entry;
101         GSList *slist = object_list->entries;
102
103         protect_thread_critical_region();
104         remove_tap_listener(object_list);
105         unprotect_thread_critical_region();
106
107         /* Free the GSList attributes */
108         while(slist) {
109                 entry = slist->data;
110
111                 g_free(entry->hostname);
112                 g_free(entry->content_type);
113                 g_free(entry->filename);
114                 g_free(entry->payload_data);
115
116                 slist = slist->next;
117                 g_free(entry);
118         }
119
120         /* Free the GSList elements */
121         g_slist_free(object_list->entries);
122         g_free(object_list);
123 }
124
125 static gboolean
126 eo_save_entry(gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err)
127 {
128         int to_fd;
129
130         to_fd = ws_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL |
131                          O_BINARY, 0644);
132         if(to_fd == -1) { /* An error occurred */
133                 if (show_err)
134                         open_failure_alert_box(save_as_filename, errno, TRUE);
135                 g_free(save_as_filename);
136                 return FALSE;
137         }
138
139         if(ws_write(to_fd, entry->payload_data, entry->payload_len) < 0) {
140                 if (show_err)
141                         write_failure_alert_box(save_as_filename, errno);
142                 ws_close(to_fd);
143                 g_free(save_as_filename);
144                 return FALSE;
145         }
146
147         if (ws_close(to_fd) < 0) {
148                 if (show_err)
149                         write_failure_alert_box(save_as_filename, errno);
150                 g_free(save_as_filename);
151                 return FALSE;
152         }
153
154         g_free(save_as_filename);
155         return TRUE;
156 }
157
158
159 static void
160 eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg)
161 {
162         GtkWidget *save_as_w;
163         export_object_list_t *object_list = arg;
164         export_object_entry_t *entry = NULL;
165
166         entry = g_slist_nth_data(object_list->entries,
167                                  object_list->row_selected);
168
169         if(!entry) {
170                 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "No object was selected for saving.  Please click on an object and click save again.");
171                 return;
172         }
173
174         save_as_w = file_selection_new("Wireshark: Save Object As ...",
175                                        FILE_SELECTION_SAVE);
176
177         gtk_window_set_transient_for(GTK_WINDOW(save_as_w),
178                                      GTK_WINDOW(object_list->dlg));
179
180         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w),
181                                           entry->filename);
182
183         if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
184                 eo_save_entry(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w)), entry, TRUE);
185
186         window_destroy(save_as_w);
187 }
188
189 static void
190 eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg)
191 {
192         gchar *save_as_fullpath;
193         export_object_list_t *object_list = arg;
194         export_object_entry_t *entry;
195         GtkWidget *save_in_w;
196         GSList *slist = object_list->entries;
197         gboolean all_saved = TRUE;
198
199         save_in_w = file_selection_new("Wireshark: Save All Objects In ...",
200                                        FILE_SELECTION_CREATE_FOLDER);
201
202         gtk_window_set_transient_for(GTK_WINDOW(save_in_w),
203                                      GTK_WINDOW(object_list->dlg));
204
205         if(gtk_dialog_run(GTK_DIALOG(save_in_w)) == GTK_RESPONSE_ACCEPT) {
206                 while(slist) {
207                         entry = slist->data;
208
209                         save_as_fullpath = g_build_filename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w)), entry->filename, NULL);
210
211                         if (!eo_save_entry(save_as_fullpath, entry, FALSE))
212                                 all_saved = FALSE;
213
214                         slist = slist->next;
215                 }
216         }
217
218         if (!all_saved)
219                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
220                       "Some files could not be saved.");
221
222         window_destroy(save_in_w);
223 }
224
225 /* Runs at the beginning of tapping only */
226 static void
227 eo_reset(void *tapdata)
228 {
229         export_object_list_t *object_list = tapdata;
230
231         object_list->entries = NULL;
232         object_list->iter = NULL;
233         object_list->row_selected = -1;
234 }
235
236 static void
237 eo_draw(void *tapdata)
238 {
239         export_object_list_t *object_list = tapdata;
240         export_object_entry_t *eo_entry;
241
242         GSList *slist = object_list->entries;
243         GtkTreeIter new_iter;
244
245         /*  Free the tree first, since we may get called more than once for the same capture 
246             Not doing so caused duplicate entries and clicking them caused crashes.
247         */
248
249         gtk_tree_store_clear(object_list->store);
250
251         while(slist) {
252                 eo_entry = slist->data;
253
254                 gtk_tree_store_append(object_list->store, &new_iter,
255                                       object_list->iter);
256
257                 gtk_tree_store_set(object_list->store, &new_iter,
258                                    EO_PKT_NUM_COLUMN, eo_entry->pkt_num,
259                                    EO_HOSTNAME_COLUMN, eo_entry->hostname,
260                                    EO_CONTENT_TYPE_COLUMN, eo_entry->content_type,
261                                    EO_BYTES_COLUMN, eo_entry->payload_len,
262                                    EO_FILENAME_COLUMN, eo_entry->filename,
263                                    -1);
264
265                 slist = slist->next;
266         }
267 }
268
269 void
270 export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet)
271 {
272         GtkWidget *sw;
273         GtkCellRenderer *renderer;
274         GtkTreeViewColumn *column;
275         GtkTreeSelection *selection;
276         GtkWidget *vbox, *bbox, *help_bt, *cancel_bt, *save_bt, *save_all_bt;
277         GtkTooltips *button_bar_tips;
278         GString *error_msg;
279         export_object_list_t *object_list;
280         gchar *window_title;
281
282         /* Initialize our object list structure */
283         object_list = g_malloc0(sizeof(export_object_list_t));
284
285         /* Data will be gathered via a tap callback */
286         error_msg = register_tap_listener(tapname, object_list, NULL, 0,
287                                           eo_reset,
288                                           tap_packet,
289                                           eo_draw);
290
291         if (error_msg) {
292                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
293                               "Can't register %s tap: %s\n", name, error_msg->str);
294                 g_string_free(error_msg, TRUE);
295                 return;
296         }
297
298         /* Setup our GUI window */
299         button_bar_tips = gtk_tooltips_new();
300
301         window_title = g_strdup_printf("Wireshark: %s object list", name);
302         object_list->dlg = dlg_window_new(window_title);
303         g_free(window_title);
304
305         gtk_window_set_default_size(GTK_WINDOW(object_list->dlg),
306                                     DEF_WIDTH, DEF_HEIGHT);
307
308         vbox = gtk_vbox_new(FALSE, 5);
309
310         gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
311         gtk_container_add(GTK_CONTAINER(object_list->dlg), vbox);
312
313         sw = scrolled_window_new(NULL, NULL);
314         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
315                                             GTK_SHADOW_IN);
316
317         gtk_container_add(GTK_CONTAINER(vbox), sw);
318
319         object_list->store = gtk_tree_store_new(EO_NUM_COLUMNS,
320                                                  G_TYPE_INT, G_TYPE_STRING,
321                                                  G_TYPE_STRING, G_TYPE_INT,
322                                                  G_TYPE_STRING);
323
324         object_list->tree = tree_view_new(GTK_TREE_MODEL(object_list->store));
325         g_object_unref(G_OBJECT(object_list->store));
326
327         object_list->tree_view = GTK_TREE_VIEW(object_list->tree);
328
329         renderer = gtk_cell_renderer_text_new();
330         column = gtk_tree_view_column_new_with_attributes("Packet num",
331                                                           renderer,
332                                                           "text",
333                                                           EO_PKT_NUM_COLUMN,
334                                                           NULL);
335         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
336         gtk_tree_view_append_column(object_list->tree_view, column);
337
338         renderer = gtk_cell_renderer_text_new();
339         column = gtk_tree_view_column_new_with_attributes("Hostname",
340                                                           renderer,
341                                                           "text",
342                                                           EO_HOSTNAME_COLUMN,
343                                                           NULL);
344         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
345         gtk_tree_view_append_column(object_list->tree_view, column);
346
347         renderer = gtk_cell_renderer_text_new();
348         column = gtk_tree_view_column_new_with_attributes("Content Type",
349                                                           renderer,
350                                                           "text",
351                                                           EO_CONTENT_TYPE_COLUMN,
352                                                           NULL);
353         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
354         gtk_tree_view_append_column(object_list->tree_view, column);
355
356         renderer = gtk_cell_renderer_text_new();
357         column = gtk_tree_view_column_new_with_attributes("Bytes",
358                                                           renderer,
359                                                           "text",
360                                                           EO_BYTES_COLUMN,
361                                                           NULL);
362         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
363         gtk_tree_view_append_column(object_list->tree_view, column);
364
365         renderer = gtk_cell_renderer_text_new();
366         column = gtk_tree_view_column_new_with_attributes("Filename",
367                                                           renderer,
368                                                           "text",
369                                                           EO_FILENAME_COLUMN,
370                                                           NULL);
371         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
372         gtk_tree_view_append_column(object_list->tree_view, column);
373
374         gtk_container_add(GTK_CONTAINER(sw), object_list->tree);
375
376         selection = gtk_tree_view_get_selection(object_list->tree_view);
377         g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), object_list);
378
379
380         bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_SAVE_ALL, GTK_STOCK_SAVE_AS, GTK_STOCK_CANCEL, NULL);
381
382         /* Help button */
383         help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
384         g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_OBJECT_LIST);
385         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), help_bt,
386                              "Show help for this dialog.", NULL);
387
388         /* Save All button */
389         save_all_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_SAVE_ALL);
390         g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb),
391                        object_list);
392         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), save_all_bt,
393                              "Save all listed objects with their displayed "
394                              "filenames.", NULL);
395
396         /* Save As button */
397         save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS);
398         g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), object_list);
399         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), save_bt,
400                              "Saves the currently selected content to a file.",
401                              NULL);
402
403         /* Cancel button */
404         cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
405         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), cancel_bt,
406                              "Cancel this dialog.", NULL);
407
408
409         /* Pack the buttons into the "button box" */
410         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
411         gtk_widget_show(bbox);
412
413         /* Setup cancel/delete/destroy signal handlers */
414         g_signal_connect(object_list->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
415         g_signal_connect(object_list->dlg, "destroy",
416                        G_CALLBACK(eo_win_destroy_cb), object_list);
417         window_set_cancel_button(object_list->dlg, cancel_bt,
418                                  window_cancel_button_cb);
419
420         /* Show the window */
421         gtk_widget_show_all(object_list->dlg);
422         window_present(object_list->dlg);
423
424         cf_retap_packets(&cfile);
425 }