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,
41 #include <epan/packet_info.h>
42 #include <epan/prefs.h>
45 #include <../alert_box.h>
46 #include <../simple_dialog.h>
47 #include <wsutil/file_util.h>
49 #include <gtk/dlg_utils.h>
50 #include <gtk/file_dlg.h>
51 #include <gtk/gui_utils.h>
52 #include <gtk/help_dlg.h>
54 #include <gtk/stock_icons.h>
55 #include "gtk/export_object.h"
61 EO_CONTENT_TYPE_COLUMN,
64 EO_NUM_COLUMNS /* must be last */
67 static eo_protocoldata_reset_cb eo_protocoldata_reset = NULL;
71 eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path,
72 GtkTreeIter *iter _U_, gpointer arg)
74 export_object_list_t *object_list = arg;
75 export_object_entry_t *entry;
79 if((path_index = gtk_tree_path_get_indices(path)) == NULL)
80 /* Row not found in tree - shouldn't happen */
83 object_list->row_selected = path_index[0];
85 /* Select the corresponding packet in the packet list */
86 entry = g_slist_nth_data(object_list->entries,
87 object_list->row_selected);
88 cf_goto_frame(&cfile, entry->pkt_num);
92 eo_remember_row_num(GtkTreeSelection *sel, gpointer data)
94 gtk_tree_selection_selected_foreach(sel, eo_remember_this_row, data);
98 /* Called when the Export Object window is closed in any way */
100 eo_win_destroy_cb(GtkWindow *win _U_, gpointer data)
102 export_object_list_t *object_list = data;
103 export_object_entry_t *entry;
104 GSList *slist = object_list->entries;
106 protect_thread_critical_region();
107 remove_tap_listener(object_list);
108 unprotect_thread_critical_region();
110 /* Free the GSList attributes */
114 g_free(entry->hostname);
115 g_free(entry->content_type);
116 g_free(entry->filename);
117 g_free(entry->payload_data);
123 /* Free the GSList elements */
124 g_slist_free(object_list->entries);
127 /* Free the private export_object_xxx data */
128 if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
132 eo_save_entry(gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err)
137 ssize_t bytes_written;
141 to_fd = ws_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL |
143 if(to_fd == -1) { /* An error occurred */
145 open_failure_alert_box(save_as_filename, errno, TRUE);
146 g_free(save_as_filename);
151 * The third argument to _write() on Windows is an unsigned int,
152 * so, on Windows, that's the size of the third argument to
155 * The third argument to write() on UN*X is a size_t, although
156 * the return value is an ssize_t, so one probably shouldn't
157 * write more than the max value of an ssize_t.
159 * In either case, there's no guarantee that a gint64 such as
160 * payload_len can be passed to ws_write(), so we write in
161 * chunks of, at most 2^31 bytes.
163 ptr = entry->payload_data;
164 bytes_left = entry->payload_len;
165 while (bytes_left != 0) {
166 if (bytes_left > 0x40000000)
167 bytes_to_write = 0x40000000;
169 bytes_to_write = (int)bytes_left;
170 bytes_written = ws_write(to_fd, ptr, bytes_to_write);
171 if(bytes_written <= 0) {
172 if (bytes_written < 0)
175 err = WTAP_ERR_SHORT_WRITE;
177 write_failure_alert_box(save_as_filename, err);
179 g_free(save_as_filename);
182 bytes_left -= bytes_written;
183 ptr += bytes_written;
185 if (ws_close(to_fd) < 0) {
187 write_failure_alert_box(save_as_filename, errno);
188 g_free(save_as_filename);
192 g_free(save_as_filename);
198 eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg)
200 GtkWidget *save_as_w;
201 export_object_list_t *object_list = arg;
202 export_object_entry_t *entry = NULL;
204 entry = g_slist_nth_data(object_list->entries,
205 object_list->row_selected);
208 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "No object was selected for saving. Please click on an object and click save again.");
212 save_as_w = file_selection_new("Wireshark: Save Object As ...",
213 FILE_SELECTION_SAVE);
215 gtk_window_set_transient_for(GTK_WINDOW(save_as_w),
216 GTK_WINDOW(object_list->dlg));
218 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w),
221 if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
222 eo_save_entry(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w)), entry, TRUE);
224 window_destroy(save_as_w);
227 #define HINIBBLE(x) (((x) >> 4) & 0xf)
228 #define LONIBBLE(x) ((x) & 0xf)
229 #define HEXTOASCII(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'a'))
230 #define MAXFILELEN 255
232 static GString *eo_rename(GString *gstr, gchar dup)
234 gchar tmp[4] = "( )";
239 if ( (tmp_ptr = strrchr(gstr->str, '.')) != NULL ) {
240 /* Retain the extension */
241 ext_str = g_string_new(tmp_ptr);
242 gstr = g_string_truncate(gstr, gstr->len - ext_str->len);
243 if ( gstr->len >= (MAXFILELEN - (strlen(tmp) + ext_str->len)) )
244 gstr = g_string_truncate(gstr, MAXFILELEN - (strlen(tmp) + ext_str->len));
245 gstr = g_string_append(gstr, tmp);
246 gstr = g_string_append(gstr, ext_str->str);
247 g_string_free(ext_str, TRUE);
250 if ( gstr->len >= (MAXFILELEN - strlen(tmp)) )
251 gstr = g_string_truncate(gstr, MAXFILELEN - strlen(tmp));
252 gstr = g_string_append(gstr, tmp);
258 eo_massage_str(const gchar *in_str, gsize maxlen, gchar dup)
261 /* The characters in "reject" come from:
262 * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx.
263 * Add to the list as necessary for other OS's.
265 const gchar *reject = "<>:\"/\\|?*"
266 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
267 "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
268 "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
272 out_str = g_string_new("");
274 /* Find all disallowed characters/bytes and replace them with %xx */
275 while ( (tmp_ptr = strpbrk(in_str, reject)) != NULL ) {
276 out_str = g_string_append_len(out_str, in_str, tmp_ptr - in_str);
277 out_str = g_string_append_c(out_str, '%');
278 out_str = g_string_append_c(out_str, HEXTOASCII(HINIBBLE(*tmp_ptr)));
279 out_str = g_string_append_c(out_str, HEXTOASCII(LONIBBLE(*tmp_ptr)));
280 in_str = tmp_ptr + 1;
282 out_str = g_string_append(out_str, in_str);
283 if ( out_str->len > maxlen ) {
284 if ( (tmp_ptr = strrchr(out_str->str, '.')) != NULL ) {
285 /* Retain the extension */
286 ext_str = g_string_new(tmp_ptr);
287 out_str = g_string_truncate(out_str, maxlen - ext_str->len);
288 out_str = g_string_append(out_str, ext_str->str);
289 g_string_free(ext_str, TRUE);
292 out_str = g_string_truncate(out_str, maxlen);
295 out_str = eo_rename(out_str, dup);
300 eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg)
302 gchar *save_as_fullpath;
303 export_object_list_t *object_list = arg;
304 export_object_entry_t *entry;
305 GtkWidget *save_in_w;
306 GSList *slist = object_list->entries;
307 gboolean all_saved = TRUE;
309 GString *safe_filename;
312 save_in_w = file_selection_new("Wireshark: Save All Objects In ...",
313 FILE_SELECTION_CREATE_FOLDER);
315 gtk_window_set_transient_for(GTK_WINDOW(save_in_w),
316 GTK_WINDOW(object_list->dlg));
318 if(gtk_dialog_run(GTK_DIALOG(save_in_w)) == GTK_RESPONSE_ACCEPT) {
322 save_in_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w));
323 if ( strlen(save_in_path) < MAXFILELEN ) {
325 safe_filename = eo_massage_str(entry->filename,
326 MAXFILELEN - strlen(save_in_path), count | 0x30);
327 save_as_fullpath = g_build_filename(
328 save_in_path, safe_filename->str, NULL);
329 g_string_free(safe_filename, TRUE);
330 } while ( g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && (++count < 10) );
332 if (!eo_save_entry(save_as_fullpath, entry, TRUE))
343 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
344 "Some files could not be saved.");
346 window_destroy(save_in_w);
349 /* Runs at the beginning of tapping only */
351 eo_reset(void *tapdata)
353 export_object_list_t *object_list = tapdata;
355 object_list->entries = NULL;
356 object_list->iter = NULL;
357 object_list->row_selected = -1;
359 if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
363 eo_draw(void *tapdata)
365 export_object_list_t *object_list = tapdata;
366 export_object_entry_t *eo_entry;
368 GSList *slist = object_list->entries;
369 GtkTreeIter new_iter;
371 /* Free the tree first, since we may get called more than once for the same capture
372 Not doing so caused duplicate entries and clicking them caused crashes.
375 gtk_tree_store_clear(object_list->store);
378 eo_entry = slist->data;
380 gtk_tree_store_append(object_list->store, &new_iter,
383 gtk_tree_store_set(object_list->store, &new_iter,
384 EO_PKT_NUM_COLUMN, eo_entry->pkt_num,
385 EO_HOSTNAME_COLUMN, eo_entry->hostname,
386 EO_CONTENT_TYPE_COLUMN, eo_entry->content_type,
387 EO_BYTES_COLUMN, eo_entry->payload_len,
388 EO_FILENAME_COLUMN, eo_entry->filename,
396 export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet, eo_protocoldata_reset_cb eo_protocoldata_resetfn)
399 GtkCellRenderer *renderer;
400 GtkTreeViewColumn *column;
401 GtkTreeSelection *selection;
402 GtkWidget *vbox, *bbox, *help_bt, *cancel_bt, *save_bt, *save_all_bt;
404 export_object_list_t *object_list;
407 /* Initialize the pointer to the private data clearing function */
408 eo_protocoldata_reset = eo_protocoldata_resetfn;
410 /* Initialize our object list structure */
411 object_list = g_malloc0(sizeof(export_object_list_t));
413 /* Data will be gathered via a tap callback */
414 error_msg = register_tap_listener(tapname, object_list, NULL, 0,
420 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
421 "Can't register %s tap: %s\n", name, error_msg->str);
422 g_string_free(error_msg, TRUE);
427 /* Setup our GUI window */
428 window_title = g_strdup_printf("Wireshark: %s object list", name);
429 object_list->dlg = dlg_window_new(window_title);
430 g_free(window_title);
432 gtk_window_set_default_size(GTK_WINDOW(object_list->dlg),
433 DEF_WIDTH, DEF_HEIGHT);
435 vbox = gtk_vbox_new(FALSE, 5);
437 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
438 gtk_container_add(GTK_CONTAINER(object_list->dlg), vbox);
440 sw = scrolled_window_new(NULL, NULL);
441 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
444 gtk_container_add(GTK_CONTAINER(vbox), sw);
446 object_list->store = gtk_tree_store_new(EO_NUM_COLUMNS,
447 G_TYPE_INT, G_TYPE_STRING,
449 (was G_TYPE_STRING, G_TYPE_INT,) */
450 G_TYPE_STRING, G_TYPE_INT64,
453 object_list->tree = tree_view_new(GTK_TREE_MODEL(object_list->store));
454 g_object_unref(G_OBJECT(object_list->store));
456 object_list->tree_view = GTK_TREE_VIEW(object_list->tree);
458 renderer = gtk_cell_renderer_text_new();
459 column = gtk_tree_view_column_new_with_attributes("Packet num",
464 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
465 gtk_tree_view_append_column(object_list->tree_view, column);
467 renderer = gtk_cell_renderer_text_new();
468 column = gtk_tree_view_column_new_with_attributes("Hostname",
473 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
474 gtk_tree_view_append_column(object_list->tree_view, column);
476 renderer = gtk_cell_renderer_text_new();
477 column = gtk_tree_view_column_new_with_attributes("Content Type",
480 EO_CONTENT_TYPE_COLUMN,
482 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
483 gtk_tree_view_append_column(object_list->tree_view, column);
485 renderer = gtk_cell_renderer_text_new();
486 column = gtk_tree_view_column_new_with_attributes("Bytes",
491 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
492 gtk_tree_view_append_column(object_list->tree_view, column);
494 renderer = gtk_cell_renderer_text_new();
495 column = gtk_tree_view_column_new_with_attributes("Filename",
500 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
501 gtk_tree_view_append_column(object_list->tree_view, column);
503 gtk_container_add(GTK_CONTAINER(sw), object_list->tree);
505 selection = gtk_tree_view_get_selection(object_list->tree_view);
506 g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), object_list);
508 bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_SAVE_ALL, GTK_STOCK_SAVE_AS, GTK_STOCK_CANCEL, NULL);
511 help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
512 g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_OBJECT_LIST);
513 gtk_widget_set_tooltip_text(help_bt, "Show help for this dialog.");
515 /* Save All button */
516 save_all_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_SAVE_ALL);
517 g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb),
519 gtk_widget_set_tooltip_text(save_all_bt, "Save all listed objects with their displayed filenames.");
522 save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS);
523 g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), object_list);
524 gtk_widget_set_tooltip_text(save_bt, "Saves the currently selected content to a file.");
527 cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
528 gtk_widget_set_tooltip_text(cancel_bt, "Cancel this dialog.");
530 /* Pack the buttons into the "button box" */
531 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
532 gtk_widget_show(bbox);
534 /* Setup cancel/delete/destroy signal handlers */
535 g_signal_connect(object_list->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
536 g_signal_connect(object_list->dlg, "destroy",
537 G_CALLBACK(eo_win_destroy_cb), object_list);
538 window_set_cancel_button(object_list->dlg, cancel_bt,
539 window_cancel_button_cb);
541 /* Show the window */
542 gtk_widget_show_all(object_list->dlg);
543 window_present(object_list->dlg);
545 cf_retap_packets(&cfile);