2 * Common routines for tracking & saving objects found in streams of data
3 * Copyright 2007, Stephen Fisher (see AUTHORS file)
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
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.
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.
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,
31 #include <epan/packet_info.h>
32 #include <epan/prefs.h>
35 #include <wsutil/file_util.h>
36 #include <wsutil/str_util.h>
38 #include <ui/export_object.h>
39 #include <ui/simple_dialog.h>
41 #include "dlg_utils.h"
43 #include "gui_utils.h"
46 #include "stock_icons.h"
47 #include "export_object_dlg.h"
49 /* When a protocol needs intermediate data structures to construct the
50 export objects, then it must specifiy a function that cleans up all
51 those data structures. This function is passed to export_object_window
52 and called when tap reset or windows closes occurs. If no function is needed
53 a NULL value should be passed instead */
54 typedef void (*eo_protocoldata_reset_cb
)(void);
59 EO_CONTENT_TYPE_COLUMN
,
62 EO_NUM_COLUMNS
/* must be last */
65 struct _export_object_list_t
{
67 GtkWidget
*tree
, *dlg
;
68 GtkTreeView
*tree_view
;
74 static eo_protocoldata_reset_cb eo_protocoldata_reset
= NULL
;
77 eo_remember_this_row(GtkTreeModel
*model _U_
, GtkTreePath
*path
,
78 GtkTreeIter
*iter _U_
, gpointer arg
)
80 export_object_list_t
*object_list
= (export_object_list_t
*)arg
;
81 export_object_entry_t
*entry
;
85 if((path_index
= gtk_tree_path_get_indices(path
)) == NULL
)
86 /* Row not found in tree - shouldn't happen */
89 object_list
->row_selected
= path_index
[0];
91 /* Select the corresponding packet in the packet list */
92 entry
= (export_object_entry_t
*)g_slist_nth_data(object_list
->entries
,
93 object_list
->row_selected
);
94 cf_goto_frame(&cfile
, entry
->pkt_num
);
98 eo_remember_row_num(GtkTreeSelection
*sel
, gpointer data
)
100 gtk_tree_selection_selected_foreach(sel
, eo_remember_this_row
, data
);
104 /* Called when the Export Object window is closed in any way */
106 eo_win_destroy_cb(GtkWindow
*win _U_
, gpointer data
)
108 export_object_list_t
*object_list
= (export_object_list_t
*)data
;
109 export_object_entry_t
*entry
;
110 GSList
*slist
= object_list
->entries
;
112 remove_tap_listener(object_list
);
114 /* Free the GSList attributes */
116 entry
= (export_object_entry_t
*)slist
->data
;
118 g_free(entry
->hostname
);
119 g_free(entry
->content_type
);
120 g_free(entry
->filename
);
121 g_free(entry
->payload_data
);
127 /* Free the GSList elements */
128 g_slist_free(object_list
->entries
);
131 /* Free the private export_object_xxx data */
132 if (eo_protocoldata_reset
!= NULL
) eo_protocoldata_reset();
135 static gchar
*eo_saveable_pathname(gchar
*filename
) {
136 gchar
**splitted_pathname
;
137 gchar
*auxstring
, *saveable_pathname
;
140 saveable_pathname
= NULL
;
141 splitted_pathname
= g_strsplit_set(filename
,"\\",-1);
142 nparts
= g_strv_length(splitted_pathname
);
144 saveable_pathname
=g_strdup(splitted_pathname
[0]);
146 for (i
=1;i
<nparts
;i
++) {
147 auxstring
= g_strconcat(saveable_pathname
,"__",splitted_pathname
[i
],NULL
);
148 g_free(saveable_pathname
);
149 saveable_pathname
= auxstring
;
152 return saveable_pathname
;
156 gtk_eo_save_object_as_file(export_object_list_t
*object_list
, char *auxfilename
)
158 GtkWidget
*save_as_w
;
161 save_as_w
= file_selection_new("Wireshark: Save Object As ...",
162 GTK_WINDOW(object_list
->dlg
),
163 FILE_SELECTION_SAVE
);
165 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w
),
167 pathname
= file_selection_run(save_as_w
);
168 if (pathname
== NULL
) {
169 /* User cancelled or closed the dialog. */
173 /* We've crosed the Rubicon; get rid of the dialog box. */
174 window_destroy(save_as_w
);
180 eo_save_clicked_cb(GtkWidget
*widget _U_
, gpointer arg
)
182 export_object_list_t
*object_list
= (export_object_list_t
*)arg
;
183 export_object_entry_t
*entry
;
184 gchar
*auxfilename
= NULL
;
187 entry
=(export_object_entry_t
*) g_slist_nth_data(object_list
->entries
,
188 object_list
->row_selected
);
191 simple_dialog(ESD_TYPE_WARN
, ESD_BTN_OK
, "No object was selected for saving. Please click on an object and click save again.");
195 auxfilename
= eo_saveable_pathname(entry
->filename
);
198 * Loop until the user either selects a file or gives up.
201 pathname
= gtk_eo_save_object_as_file(object_list
, auxfilename
);
202 if (pathname
== NULL
) {
206 if (eo_save_entry(pathname
, entry
, TRUE
)) {
211 /* Dump failed; let the user select another file
219 #define MAXFILELEN 255
221 eo_save_all_clicked_cb(GtkWidget
*widget _U_
, gpointer arg
)
223 gchar
*save_as_fullpath
= NULL
;
224 export_object_list_t
*object_list
= (export_object_list_t
*)arg
;
225 export_object_entry_t
*entry
;
226 GtkWidget
*save_in_w
;
227 GSList
*slist
= object_list
->entries
;
228 gboolean all_saved
= TRUE
;
230 GString
*safe_filename
;
231 gchar
*auxfilename
= NULL
;
234 save_in_w
= file_selection_new("Wireshark: Save All Objects In ...",
235 GTK_WINDOW(object_list
->dlg
),
236 FILE_SELECTION_CREATE_FOLDER
);
238 if (gtk_dialog_run(GTK_DIALOG(save_in_w
)) == GTK_RESPONSE_ACCEPT
) {
240 entry
= (export_object_entry_t
*)slist
->data
;
242 save_in_path
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w
));
243 if ((strlen(save_in_path
) < MAXFILELEN
)) {
245 g_free(save_as_fullpath
);
246 if (entry
->filename
) {
247 auxfilename
= eo_saveable_pathname(entry
->filename
);
248 safe_filename
= eo_massage_str(auxfilename
,
249 MAXFILELEN
- strlen(save_in_path
), count
);
252 char generic_name
[256];
254 ext
= ct2ext(entry
->content_type
);
255 g_snprintf(generic_name
, sizeof(generic_name
),
256 "object%u%s%s", entry
->pkt_num
, ext
? "." : "",
258 safe_filename
= eo_massage_str(generic_name
,
259 MAXFILELEN
- strlen(save_in_path
), count
);
261 save_as_fullpath
= g_build_filename(
262 save_in_path
, safe_filename
->str
, NULL
);
263 g_string_free(safe_filename
, TRUE
);
264 } while (g_file_test(save_as_fullpath
, G_FILE_TEST_EXISTS
) && ++count
< 1000);
266 if (!eo_save_entry(save_as_fullpath
, entry
, FALSE
))
268 g_free(save_as_fullpath
);
269 save_as_fullpath
= NULL
;
279 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
280 "Some files could not be saved.");
282 window_destroy(save_in_w
);
285 /* Runs at the beginning of tapping only */
287 eo_reset(void *tapdata
)
289 export_object_list_t
*object_list
= (export_object_list_t
*)tapdata
;
291 object_list
->entries
= NULL
;
292 object_list
->iter
= NULL
;
293 object_list
->row_selected
= -1;
295 if (eo_protocoldata_reset
!= NULL
) eo_protocoldata_reset();
299 eo_draw(void *tapdata
)
301 export_object_list_t
*object_list
= (export_object_list_t
*)tapdata
;
302 export_object_entry_t
*eo_entry
;
305 GSList
*slist
= object_list
->entries
;
306 GtkTreeIter new_iter
;
308 /* Free the tree first, since we may get called more than once for the same capture
309 Not doing so caused duplicate entries and clicking them caused crashes.
312 gtk_tree_store_clear(object_list
->store
);
315 eo_entry
= (export_object_entry_t
*)slist
->data
;
317 gtk_tree_store_append(object_list
->store
, &new_iter
,
320 size_str
= format_size(eo_entry
->payload_len
, (format_size_flags_e
)(format_size_unit_bytes
|format_size_prefix_si
));
321 gtk_tree_store_set(object_list
->store
, &new_iter
,
322 EO_PKT_NUM_COLUMN
, eo_entry
->pkt_num
,
323 EO_HOSTNAME_COLUMN
, eo_entry
->hostname
,
324 EO_CONTENT_TYPE_COLUMN
, eo_entry
->content_type
,
325 EO_BYTES_COLUMN
, size_str
,
326 EO_FILENAME_COLUMN
, eo_entry
->filename
,
334 void object_list_add_entry(export_object_list_t
*object_list
, export_object_entry_t
*entry
)
336 object_list
->entries
= g_slist_append(object_list
->entries
, entry
);
339 export_object_entry_t
*object_list_get_entry(export_object_list_t
*object_list
, int row
) {
340 return (export_object_entry_t
*)g_slist_nth_data(object_list
->entries
, row
);
344 export_object_window(const gchar
*tapname
, const gchar
*name
, tap_packet_cb tap_packet
, eo_protocoldata_reset_cb eo_protocoldata_resetfn
)
347 GtkCellRenderer
*renderer
;
348 GtkTreeViewColumn
*column
;
349 GtkTreeSelection
*selection
;
350 GtkWidget
*vbox
, *bbox
, *help_bt
, *cancel_bt
, *save_bt
, *save_all_bt
;
352 export_object_list_t
*object_list
;
355 /* Initialize the pointer to the private data clearing function */
356 eo_protocoldata_reset
= eo_protocoldata_resetfn
;
358 /* Initialize our object list structure */
359 object_list
= g_new0(export_object_list_t
,1);
361 /* Data will be gathered via a tap callback */
362 error_msg
= register_tap_listener(tapname
, object_list
, NULL
, 0,
368 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
369 "Can't register %s tap: %s\n", name
, error_msg
->str
);
370 g_string_free(error_msg
, TRUE
);
375 /* Setup our GUI window */
376 window_title
= g_strdup_printf("Wireshark: %s object list", name
);
377 object_list
->dlg
= dlg_window_new(window_title
);
378 g_free(window_title
);
380 gtk_window_set_default_size(GTK_WINDOW(object_list
->dlg
),
381 DEF_WIDTH
, DEF_HEIGHT
);
383 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 5, FALSE
);
385 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 5);
386 gtk_container_add(GTK_CONTAINER(object_list
->dlg
), vbox
);
388 sw
= scrolled_window_new(NULL
, NULL
);
389 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
392 gtk_box_pack_start(GTK_BOX (vbox
), sw
, TRUE
, TRUE
, 0);
394 object_list
->store
= gtk_tree_store_new(EO_NUM_COLUMNS
,
395 G_TYPE_INT
, G_TYPE_STRING
,
397 (was G_TYPE_STRING, G_TYPE_INT,) */
398 G_TYPE_STRING
, G_TYPE_STRING
,
401 object_list
->tree
= tree_view_new(GTK_TREE_MODEL(object_list
->store
));
402 g_object_unref(G_OBJECT(object_list
->store
));
404 object_list
->tree_view
= GTK_TREE_VIEW(object_list
->tree
);
406 renderer
= gtk_cell_renderer_text_new();
407 column
= gtk_tree_view_column_new_with_attributes("Packet num",
412 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
413 gtk_tree_view_append_column(object_list
->tree_view
, column
);
415 renderer
= gtk_cell_renderer_text_new();
416 column
= gtk_tree_view_column_new_with_attributes("Hostname",
421 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
422 gtk_tree_view_append_column(object_list
->tree_view
, column
);
424 renderer
= gtk_cell_renderer_text_new();
425 column
= gtk_tree_view_column_new_with_attributes("Content Type",
428 EO_CONTENT_TYPE_COLUMN
,
430 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
431 gtk_tree_view_append_column(object_list
->tree_view
, column
);
433 renderer
= gtk_cell_renderer_text_new();
434 column
= gtk_tree_view_column_new_with_attributes("Size",
439 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
440 gtk_tree_view_append_column(object_list
->tree_view
, column
);
442 renderer
= gtk_cell_renderer_text_new();
443 column
= gtk_tree_view_column_new_with_attributes("Filename",
448 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
449 gtk_tree_view_append_column(object_list
->tree_view
, column
);
451 gtk_container_add(GTK_CONTAINER(sw
), object_list
->tree
);
453 selection
= gtk_tree_view_get_selection(object_list
->tree_view
);
454 g_signal_connect(selection
, "changed", G_CALLBACK(eo_remember_row_num
), object_list
);
456 bbox
= dlg_button_row_new(GTK_STOCK_HELP
, WIRESHARK_STOCK_SAVE_ALL
, GTK_STOCK_SAVE_AS
, GTK_STOCK_CANCEL
, NULL
);
459 help_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_HELP
);
460 g_signal_connect(help_bt
, "clicked", G_CALLBACK(topic_cb
), (gpointer
)HELP_EXPORT_OBJECT_LIST
);
461 gtk_widget_set_tooltip_text(help_bt
, "Show help for this dialog.");
463 /* Save All button */
464 save_all_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), WIRESHARK_STOCK_SAVE_ALL
);
465 g_signal_connect(save_all_bt
, "clicked", G_CALLBACK(eo_save_all_clicked_cb
),
467 gtk_widget_set_tooltip_text(save_all_bt
, "Save all listed objects with their displayed filenames.");
470 save_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_SAVE_AS
);
471 g_signal_connect(save_bt
, "clicked", G_CALLBACK(eo_save_clicked_cb
), object_list
);
472 gtk_widget_set_tooltip_text(save_bt
, "Saves the currently selected content to a file.");
475 cancel_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_CANCEL
);
476 gtk_widget_set_tooltip_text(cancel_bt
, "Cancel this dialog.");
478 /* Pack the buttons into the "button box" */
479 gtk_box_pack_end(GTK_BOX(vbox
), bbox
, FALSE
, FALSE
, 0);
480 gtk_widget_show(bbox
);
482 /* Setup cancel/delete/destroy signal handlers */
483 g_signal_connect(object_list
->dlg
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
484 g_signal_connect(object_list
->dlg
, "destroy",
485 G_CALLBACK(eo_win_destroy_cb
), object_list
);
486 window_set_cancel_button(object_list
->dlg
, cancel_bt
,
487 window_cancel_button_cb
);
489 /* Show the window */
490 gtk_widget_show_all(object_list
->dlg
);
491 window_present(object_list
->dlg
);
493 cf_retap_packets(&cfile
);
497 eo_dicom_cb(GtkWidget
*widget _U_
, gpointer data _U_
)
499 export_object_window("dicom_eo", "DICOM", eo_dicom_packet
, NULL
);
503 eo_http_cb(GtkWidget
*widget _U_
, gpointer data _U_
)
505 export_object_window("http_eo", "HTTP", eo_http_packet
, NULL
);
509 eo_smb_cb(GtkWidget
*widget _U_
, gpointer data _U_
)
511 /* Call the export_object window */
512 export_object_window("smb_eo", "SMB", eo_smb_packet
, eo_smb_cleanup
);