Update object export code:
[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 <stephentfisher@yahoo.com>
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 <glib.h>
32 #include <gtk/gtk.h>
33
34 /* This feature has not been ported to GTK1 */
35 #if GTK_MAJOR_VERSION >= 2
36
37 #include <alert_box.h>
38 #include <simple_dialog.h>
39
40 #include <epan/packet_info.h>
41 #include <epan/prefs.h>
42 #include <epan/tap.h>
43 #include <gtk/compat_macros.h>
44 #include <gtk/dlg_utils.h>
45 #include <gtk/file_dlg.h>
46 #include <gtk/gui_utils.h>
47 #include <gtk/help_dlg.h>
48 #include <gtk/main.h>
49 #include <wiretap/file_util.h>
50
51 #ifdef HAVE_FCNTL_H
52 #include <fcntl.h>
53 #endif
54
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58
59 #include "export_object.h"
60
61 enum {
62         EO_PKT_NUM_COLUMN,
63         EO_HOSTNAME_COLUMN,
64         EO_CONTENT_TYPE_COLUMN,
65         EO_BYTES_COLUMN,
66         EO_FILENAME_COLUMN,
67         EO_NUM_COLUMNS /* must be last */
68 };
69
70
71 static void
72 eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path,
73                      GtkTreeIter *iter _U_, gpointer arg)
74 {
75         export_object_list_t *object_list = arg;
76         export_object_entry_t *entry;
77
78         gint *path_index;
79
80         if((path_index = gtk_tree_path_get_indices(path)) == NULL)
81                 return;
82
83         object_list->row_selected = path_index[0];
84
85         entry = g_slist_nth_data(object_list->entries,
86                                  object_list->row_selected);
87        
88         cf_goto_frame(&cfile, entry->pkt_num);
89 }
90
91 static void
92 eo_remember_row_num(GtkTreeSelection *sel, gpointer data)
93 {
94         gtk_tree_selection_selected_foreach(sel, eo_remember_this_row, data);
95 }
96
97
98 static void
99 eo_win_destroy_cb(GtkWindow *win _U_, gpointer data)
100 {
101         export_object_list_t *object_list = data;
102
103         protect_thread_critical_region();
104         remove_tap_listener(object_list);
105         unprotect_thread_critical_region();
106
107         g_free(object_list);
108 }
109
110 static void
111 eo_save_entry(gchar *save_as_filename, export_object_entry_t *entry)
112 {
113         int to_fd;
114
115         to_fd = eth_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL |
116                          O_BINARY, 0644);
117         if(to_fd == -1) { /* An error occurred */
118                 open_failure_alert_box(save_as_filename, errno, TRUE);
119                 g_free(save_as_filename);
120                 return;
121         }
122
123         if(eth_write(to_fd, entry->payload_data, entry->payload_len) < 0) {
124                 write_failure_alert_box(save_as_filename, errno);
125                 eth_close(to_fd);
126                 g_free(save_as_filename);
127                 return;
128         }
129
130         if (eth_close(to_fd) < 0) {
131                 write_failure_alert_box(save_as_filename, errno);
132                 g_free(save_as_filename);
133                 return;
134         }
135
136         g_free(save_as_filename);
137 }
138
139
140 static void
141 eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg)
142 {
143         GtkWidget *save_as_w;
144         export_object_list_t *object_list = arg;
145         export_object_entry_t *entry = NULL;
146
147         entry = g_slist_nth_data(object_list->entries,
148                                  object_list->row_selected);
149
150         if(!entry) {
151                 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "No object was selected for saving.  Please click on an object and click save again.");
152                 return;
153         }
154
155         save_as_w = file_selection_new("Wireshark: Save Object As ...",
156                                        FILE_SELECTION_SAVE);
157
158         gtk_window_set_transient_for(GTK_WINDOW(save_as_w),
159                                      GTK_WINDOW(object_list->dlg));
160
161         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w),
162                                           entry->filename);
163
164         if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
165                 eo_save_entry(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w)), entry);
166
167         window_destroy(save_as_w);
168 }
169
170 static void
171 eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg)
172 {
173         gchar *save_as_fullpath;
174         export_object_list_t *object_list = arg;
175         export_object_entry_t *entry;
176         GtkWidget *save_in_w;
177         GSList *last_slist_entry;
178         gint last_slist_entry_num, i;
179
180         save_in_w = file_selection_new("Wireshark: Save All Objects In ...",
181                                        FILE_SELECTION_CREATE_FOLDER);
182
183         gtk_window_set_transient_for(GTK_WINDOW(save_in_w),
184                                      GTK_WINDOW(object_list->dlg));
185
186         if(gtk_dialog_run(GTK_DIALOG(save_in_w)) == GTK_RESPONSE_ACCEPT) {
187
188                 /* Find the last entry in the SList, then start at the beginning
189                    saving each one until we reach the last entry. */
190                 last_slist_entry = g_slist_last(object_list->entries);
191                 last_slist_entry_num = g_slist_position(object_list->entries,
192                                                         last_slist_entry);
193
194                 for(i = 0; i <= last_slist_entry_num; i++) {
195                         
196                         entry = g_slist_nth_data(object_list->entries, i);
197
198                         save_as_fullpath = g_build_filename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w)), entry->filename, NULL);
199                         
200                         eo_save_entry(save_as_fullpath, entry);
201                 }
202         }
203
204         window_destroy(save_in_w);
205 }
206
207 static void
208 eo_reset(void *tapdata)
209 {
210         export_object_list_t *object_list = tapdata;
211
212         if(object_list->entries) {
213                 g_slist_free(object_list->entries);
214                 object_list->entries = NULL;
215         }
216
217         object_list->iter = NULL;
218         object_list->row_selected = -1;
219 }
220
221 static void
222 eo_draw(void *tapdata)
223 {
224         export_object_list_t *object_list = tapdata;
225         export_object_entry_t *eo_entry;
226
227         GSList *slist_entry = NULL;
228         GSList *last_slist_entry = NULL;
229         gint last_slist_entry_num;
230         GtkTreeIter new_iter;
231         gchar *column_text[EO_NUM_COLUMNS];
232
233         last_slist_entry = g_slist_last(object_list->entries);
234         last_slist_entry_num = g_slist_position(object_list->entries,
235                                                 last_slist_entry);
236
237         while(object_list->slist_pos <= last_slist_entry_num &&
238               last_slist_entry_num != -1) {
239                 slist_entry = g_slist_nth(object_list->entries,
240                                           object_list->slist_pos);
241                 eo_entry = slist_entry->data;
242                 
243                 column_text[0] = g_strdup_printf("%u", eo_entry->pkt_num);
244                 column_text[1] = g_strdup_printf("%s", eo_entry->hostname);
245                 column_text[2] = g_strdup_printf("%s", eo_entry->content_type);
246                 column_text[3] = g_strdup_printf("%u", eo_entry->payload_len);
247                 column_text[4] = g_strdup_printf("%s", eo_entry->filename);
248
249                 gtk_tree_store_append(object_list->store, &new_iter,
250                                       object_list->iter);
251
252                 gtk_tree_store_set(object_list->store, &new_iter,
253                                    EO_PKT_NUM_COLUMN, column_text[0],
254                                    EO_HOSTNAME_COLUMN, column_text[1],
255                                    EO_CONTENT_TYPE_COLUMN, column_text[2],
256                                    EO_BYTES_COLUMN, column_text[3],
257                                    EO_FILENAME_COLUMN, column_text[4],
258                                    -1);
259
260                 g_free(column_text[0]);
261                 g_free(column_text[1]);
262                 g_free(column_text[2]);
263                 g_free(column_text[3]);
264                 g_free(column_text[4]);
265
266                 object_list->slist_pos++;
267         }
268 }
269
270 void
271 export_object_window(gchar *tapname, tap_packet_cb tap_packet)
272 {
273         GtkWidget *sw;
274         GtkCellRenderer *renderer;
275         GtkTreeViewColumn *column;
276         GtkTreeSelection *selection;
277         GtkWidget *vbox, *bbox, *help_bt, *close_bt, *save_bt, *save_all_bt;
278         GtkTooltips *button_bar_tips;
279         GString *error_msg;
280         export_object_list_t *object_list;
281         gchar *window_title;
282
283         /* Initialize our object list structure */
284         object_list = g_malloc0(sizeof(export_object_list_t));
285
286         /* Data will be gathered via a tap callback */
287         error_msg = register_tap_listener(tapname, object_list, NULL,
288                                           eo_reset,
289                                           tap_packet,
290                                           eo_draw);
291
292         if (error_msg) {
293                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
294                               "Can't register http tap: %s\n", error_msg->str);
295                 g_string_free(error_msg, TRUE);
296                 return;
297         }
298
299         /* Set up our GUI window */
300         button_bar_tips = gtk_tooltips_new();
301
302         window_title = g_strdup_printf("Wireshark: %s object list", tapname);
303         object_list->dlg = dlg_window_new(window_title);
304         g_free(window_title);
305
306         gtk_window_set_default_size(GTK_WINDOW(object_list->dlg),
307                                     DEF_WIDTH, DEF_HEIGHT);
308
309         vbox = gtk_vbox_new(FALSE, 5);
310
311         gtk_container_border_width(GTK_CONTAINER(vbox), 5);
312         gtk_container_add(GTK_CONTAINER(object_list->dlg), vbox);
313
314         sw = scrolled_window_new(NULL, NULL);
315         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
316                                             GTK_SHADOW_IN);
317
318         gtk_container_add(GTK_CONTAINER(vbox), sw);
319
320         object_list->store = gtk_tree_store_new(EO_NUM_COLUMNS,
321                                                  G_TYPE_STRING, G_TYPE_STRING,
322                                                  G_TYPE_STRING, G_TYPE_STRING,
323                                                  G_TYPE_STRING);
324
325         object_list->tree = tree_view_new(GTK_TREE_MODEL(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         SIGNAL_CONNECT(selection, "changed", eo_remember_row_num, object_list);
378
379
380         bbox = gtk_hbox_new(FALSE, 5);
381
382         /* Help button */
383         help_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_HELP);
384         SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_EXPORT_OBJECT_LIST);
385         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), help_bt,
386                              "Show help on this dialog", NULL);
387         gtk_box_pack_start(GTK_BOX(bbox), help_bt, FALSE, FALSE, 0);
388
389         /* Save All button */
390         save_all_bt = gtk_button_new_with_mnemonic("Save _All");
391         SIGNAL_CONNECT(save_all_bt, "clicked", eo_save_all_clicked_cb,
392                        object_list);
393         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), save_all_bt,
394                              "Save all listed objects with their displayed "
395                              "filenames.", NULL);
396         gtk_box_pack_end(GTK_BOX(bbox), save_all_bt, FALSE, FALSE, 0);
397
398         /* Save button */
399         save_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
400         SIGNAL_CONNECT(save_bt, "clicked", eo_save_clicked_cb, object_list);
401         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), save_bt,
402                              "Saves the currently selected content to a file.",
403                              NULL);
404         gtk_box_pack_end(GTK_BOX(bbox), save_bt, FALSE, FALSE, 0);
405
406         /* Close button */
407         close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
408         GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
409         gtk_tooltips_set_tip(GTK_TOOLTIPS(button_bar_tips), close_bt,
410                              "Close this dialog.", NULL);
411         gtk_box_pack_end(GTK_BOX(bbox), close_bt, FALSE, FALSE, 0);
412
413         /* Pack the buttons into the "button box" */
414         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
415         gtk_widget_show(bbox);
416
417         /* Setup cancel/delete/destroy signal handlers */
418         SIGNAL_CONNECT(object_list->dlg, "delete_event",
419                        window_delete_event_cb, NULL);
420         SIGNAL_CONNECT(object_list->dlg, "destroy",
421                        eo_win_destroy_cb, NULL);
422         window_set_cancel_button(object_list->dlg, close_bt,
423                                  window_cancel_button_cb);
424
425         /* Show the window */
426         gtk_widget_show_all(object_list->dlg);
427         window_present(object_list->dlg);
428
429         cf_retap_packets(&cfile, FALSE);
430 }
431
432 #endif /* GTK_MAJOR_VERSION >= 2 */