Make the "Export Selected Packets As..." code path more like the "Save

As..." code path.

Extract the code for the "do you want to overwrite this file" and "OK,
you do - are you aware it's {user-immutable, read-only}?" code paths
into a common routine for use by both of those and, potentially, other
save/export/etc. code paths in the future.

For "Save As", allow us to save atop the current capture file, as that's
just what "Save" does if there are unsaved changes, and "safe save"
makes that work.  *Don't* allow that for "Export Selected Packets
As...", however.

The file chooser is run as a modal dialog, so we don't need to worry
about creating more than one of them or about the number of marked
packets etc. being changed out from under us.  Get rid of a bunch of
static variables.

svn path=/trunk/; revision=43060
This commit is contained in:
Guy Harris 2012-06-04 10:27:59 +00:00
parent f275aeadd9
commit afa17a95b0
8 changed files with 300 additions and 406 deletions

View File

@ -84,12 +84,8 @@ static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
static void do_file_save(capture_file *cf, gboolean dont_reopen);
static void do_file_save_as(capture_file *cf);
static void file_save_as_cb(GtkWidget *fs);
static void file_save_as_select_file_type_cb(GtkWidget *w, gpointer data);
static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_export_specified_packets_cb(GtkWidget *w, gpointer fs);
static void file_export_specified_packets_select_file_type_cb(GtkWidget *w, gpointer data);
static void file_export_specified_packets_ok_cb(GtkWidget *w, gpointer fs);
static void file_export_specified_packets_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_select_file_type_cb(GtkWidget *w, gpointer data);
static void file_export_specified_packets_cb(GtkWidget *fs, packet_range_t *range);
static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
@ -116,27 +112,8 @@ static void set_file_type_list(GtkWidget *combo_box, capture_file *cf);
#define PREVIEW_PACKETS_KEY "preview_packets_key"
#define PREVIEW_FIRST_KEY "preview_first_key"
/*
* Keep a static pointer to the current "Save Capture File As" window, if
* any, so that if somebody tries to do "File:Save" or "File:Save As"
* while there's already a "Save Capture File As" window up, we just pop
* up the existing one, rather than creating a new one.
*/
static GtkWidget *file_save_as_w;
/*
* Keep a static pointer to the current "Export Specified Packets" window, if
* any, so that if somebody tries to do "File:Export Specified Packets"
* while there's already a "Export Specified Packets" window up, we just pop
* up the existing one, rather than creating a new one.
*/
static GtkWidget *file_export_specified_packets_w;
/* XXX - can we make these not be static? */
static packet_range_t range;
static gboolean color_selected;
static GtkWidget *range_tb;
#define PREVIEW_STR_MAX 200
@ -789,7 +766,6 @@ file_merge_cmd(GtkWidget *w)
gtk_widget_show(main_vb);
/* File type row */
range_tb = NULL;
ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
gtk_widget_show(ft_hb);
@ -1318,8 +1294,9 @@ set_file_type_list(GtkWidget *combo_box, capture_file *cf)
}
static void
file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
file_select_file_type_cb(GtkWidget *w, gpointer parent_arg)
{
GtkWidget *parent = parent_arg;
int new_file_type;
gpointer ptr;
GtkWidget *compressed_cb;
@ -1329,7 +1306,7 @@ file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
}
new_file_type = GPOINTER_TO_INT(ptr);
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY);
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(parent), E_COMPRESSED_CB_KEY);
if (!wtap_dump_can_compress(new_file_type)) {
/* Can't compress this file type; turn off compression and make
the compression checkbox insensitive. */
@ -1346,18 +1323,9 @@ do_file_save_as(capture_file *cf)
#if _WIN32
win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
#else /* _WIN32 */
GtkWidget *file_save_as_w;
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *compressed_cb;
GtkWidget *msg_dialog;
gchar *display_basename;
gint response;
char *cf_name;
ws_statb64 statbuf;
if (file_save_as_w != NULL) {
/* There's already an "Save Capture File As" dialog box; reactivate it. */
reactivate_window(file_save_as_w);
return;
}
/* Default to saving in the file's current format. */
@ -1373,7 +1341,6 @@ do_file_save_as(capture_file *cf)
gtk_widget_show(main_vb);
/* File type row */
range_tb = NULL;
ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
gtk_widget_show(ft_hb);
@ -1395,13 +1362,10 @@ do_file_save_as(capture_file *cf)
gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
gtk_widget_show(compressed_cb);
g_object_set_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY, compressed_cb);
/* Ok: now "select" the default filetype which invokes file_save_as_select_file_type_cb */
g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_save_as_select_file_type_cb), NULL);
/* Ok: now "select" the default filetype which invokes file_select_file_type_cb */
g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_select_file_type_cb), file_save_as_w);
ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
g_signal_connect(file_save_as_w, "destroy",
G_CALLBACK(file_save_as_destroy_cb), NULL);
/*
* Loop until the user either selects a file or gives up.
*/
@ -1425,166 +1389,24 @@ do_file_save_as(capture_file *cf)
continue;
}
/*
* Check that the from file is not the same as to file
* We do it here so we catch all cases ...
* Unfortunately, the file requester gives us an absolute file
* name and the read file name may be relative (if supplied on
* the command line). From Joerg Mayer.
*/
if (files_identical(cf->filename, cf_name)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%sCapture file: \"%s\" identical to loaded file!%s\n\n"
"Please choose a different filename.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_save_as_w)
window_destroy(GTK_WIDGET(file_save_as_w));
return;
}
/* it the file doesn't exist, simply try to save it */
if (!file_exists(cf_name)) {
gtk_widget_hide(GTK_WIDGET(file_save_as_w));
g_free(cf_name);
file_save_as_cb(file_save_as_w);
return;
}
/*
* The file exists. Ask the user if they want to overwrite it.
*
* XXX - what if it's a symlink? TextEdit just blindly replaces
* symlinks with files if you save over them, but gedit appears
* to replace the *target* of the symlink. (To find the target,
* use realpath() - it dates back to the SUSv2 and may be even
* older.)
*/
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"A file named \"%s\" already exists. Do you want to replace it?",
display_basename);
g_free(display_basename);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
"A file or folder with the same name already exists in that folder. "
"Replacing it will overwrite its current contents.");
gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
#ifndef _WIN32
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
"Replace", GTK_RESPONSE_ACCEPT,
#else
"Replace", GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
#endif
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_CANCEL);
response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
if (response != GTK_RESPONSE_ACCEPT) {
/* The user doesn't want to overwrite this file; let them choose
another one */
/* Ask the user whether they want to overwrite it and, if so and
it's user-immutable or not writable, whether they want to
override that. */
if (!file_target_exist_ui(file_save_as_w, cf_name)) {
/* They don't. Let them try another file name or cancel. */
g_free(cf_name);
continue;
}
/* Check whether the file has all the write permission bits clear
and, on systems that have the 4.4-Lite file flags, whether it
has the "user immutable" flag set. Treat both of those as an
indication that the user wants to protect the file from
casual overwriting, and ask the user if they want to override
that.
(Linux's "immutable" flag, as fetched and set by the appropriate
ioctls (FS_IOC_GETFLAGS/FS_IOC_SETFLAGS in newer kernels,
EXT2_IOC_GETFLAGS/EXT2_IOC_SETFLAGS in older kernels - non-ext2
file systems that support those ioctls use the same values as ext2
does), appears to be more like the *BSD/OS X "system immutable"
flag, as it can be set only by the superuser or by processes with
CAP_LINUX_IMMUTABLE, so it sounds as if it's not intended for
arbitrary users to set or clear. */
if (ws_stat64(cf_name, &statbuf) != -1) {
/* OK, we have the permission bits and, if HAVE_ST_FLAGS is defined,
the flags. (If we don't, we don't worry about it.) */
#ifdef HAVE_ST_FLAGS
if (statbuf.st_flags & UF_IMMUTABLE) {
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
#ifdef __APPLE__
/* Stuff in the OS X UI calls files with the "user immutable" bit
"locked"; pre-OS X Mac software might have had that notion and
called it "locked". */
"The file \"%s\" is locked.",
#else /* __APPLE__ */
/* Just call it "immutable" in *BSD. */
"The file \"%s\" is immutable.",
#endif /* __APPLE__ */
display_basename);
g_free(display_basename);
} else
#endif /* HAVE_ST_FLAGS */
if ((statbuf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) {
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_save_as_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"The file \"%s\" is read-only.",
display_basename);
g_free(display_basename);
} else {
/* No problem, just drive on. */
msg_dialog = NULL;
}
if (msg_dialog != NULL) {
/* OK, ask the user if they want to overwrite the file. */
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
"Do you want to overwrite it anyway?");
gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
"Overwrite", GTK_RESPONSE_ACCEPT,
"Don't overwrite", GTK_RESPONSE_REJECT,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_REJECT);
response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
if (response != GTK_RESPONSE_ACCEPT) {
/* The user doesn't want to overwrite this file; let them choose
another one */
g_free(cf_name);
continue;
}
#ifdef HAVE_ST_FLAGS
/* OK, they want to overwrite the file. If it has the "user
immutable" flag, we have to turn that off first, so we
can move the file. */
if (statbuf.st_flags & UF_IMMUTABLE) {
/* If this fails, the attempt to save will fail, so just
let that happen and pop up a "you lose" dialog. */
chflags(cf_name, statbuf.st_flags & ~UF_IMMUTABLE);
}
#endif
}
}
/* save file */
gtk_widget_hide(GTK_WIDGET(file_save_as_w));
g_free(cf_name);
file_save_as_cb(file_save_as_w);
return;
}
@ -1609,6 +1431,9 @@ file_save_as_cb(GtkWidget *fs) {
int file_type;
gboolean compressed;
/* Hide the file chooser while doing the save. */
gtk_widget_hide(fs);
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
@ -1629,15 +1454,13 @@ file_save_as_cb(GtkWidget *fs) {
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_save_as_w)
window_destroy(GTK_WIDGET (fs));
window_destroy(fs);
return;
}
/* The write succeeded; get rid of the file selection box. */
/* cf_save_packets() might already closed our dialog! */
if (file_save_as_w)
window_destroy(GTK_WIDGET (fs));
window_destroy(fs);
/* Save the directory name for future file dialogs. */
dirname = get_dirname(cf_name); /* Overwrites cf_name */
@ -1645,49 +1468,19 @@ file_save_as_cb(GtkWidget *fs) {
g_free(cf_name);
}
void
file_save_as_destroy(void)
{
if (file_save_as_w)
window_destroy(file_save_as_w);
}
static void
file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
{
/* Note that we no longer have a "Save Capture File As" dialog box. */
file_save_as_w = NULL;
}
/*
* Update various dynamic parts of the range controls; called from outside
* the file dialog code whenever the packet counts change.
*/
void
file_export_specified_packets_update_dynamics(void)
{
if (file_export_specified_packets_w == NULL) {
/* We don't currently have a "Export Specified Packets..." dialog box up. */
return;
}
range_update_dynamics(range_tb);
}
void
file_export_specified_packets_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
{
#if _WIN32
win32_export_specified_packets_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
#else /* _WIN32 */
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
if (file_export_specified_packets_w != NULL) {
/* There's already an "Export Specified Packets" dialog box; reactivate it. */
reactivate_window(file_export_specified_packets_w);
return;
}
GtkWidget *file_export_specified_packets_w;
GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *range_tb,
*compressed_cb;
packet_range_t range;
char *cf_name;
gchar *display_basename;
GtkWidget *msg_dialog;
/* Default to writing out all displayed packets, in the file's current format. */
@ -1743,151 +1536,95 @@ file_export_specified_packets_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
gtk_widget_show(compressed_cb);
g_object_set_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY, compressed_cb);
/* Ok: now "select" the default filetype which invokes file_export_specified_packets_select_file_type_cb */
g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_export_specified_packets_select_file_type_cb), NULL);
/* Ok: now "select" the default filetype which invokes file_select_file_type_cb */
g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_select_file_type_cb), file_export_specified_packets_w);
ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
g_signal_connect(file_export_specified_packets_w, "destroy",
G_CALLBACK(file_export_specified_packets_destroy_cb), NULL);
/*
* Loop until the user either selects a file or gives up.
*/
for (;;) {
if (gtk_dialog_run(GTK_DIALOG(file_export_specified_packets_w)) != GTK_RESPONSE_ACCEPT) {
/* They clicked "Cancel" or closed the dialog or.... */
window_destroy(file_export_specified_packets_w);
return;
}
if (gtk_dialog_run(GTK_DIALOG(file_export_specified_packets_w)) == GTK_RESPONSE_ACCEPT) {
file_export_specified_packets_ok_cb(file_export_specified_packets_w, file_export_specified_packets_w);
} else {
window_destroy(file_export_specified_packets_w);
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_export_specified_packets_w));
/* Perhaps the user specified a directory instead of a file.
Check whether they did. */
if (test_for_directory(cf_name) == EISDIR) {
/* It's a directory - set the file selection box to display that
directory, and leave the selection box displayed. */
set_last_open_dir(cf_name);
g_free(cf_name);
file_selection_set_current_folder(file_export_specified_packets_w, get_last_open_dir());
continue;
}
/* Check whether the range is valid. */
if (!range_check_validity_modal(file_export_specified_packets_w, &range)) {
/* The range isn't valid; the user was told that, and dismissed
the dialog telling them that, so let them fix the range
and try again, or cancel. */
g_free(cf_name);
continue;
}
/*
* Check that we're not going to save on top of the current
* capture file.
* We do it here so we catch all cases ...
* Unfortunately, the file requester gives us an absolute file
* name and the read file name may be relative (if supplied on
* the command line). From Joerg Mayer.
*/
if (files_identical(cfile.filename, cf_name)) {
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(file_export_specified_packets_w),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"The file \"%s\" is the capture file from which you're exporting the packets.",
display_basename);
g_free(display_basename);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
"You cannot export packets on top of the current capture file.");
gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
g_free(cf_name);
continue;
}
/* it the file doesn't exist, simply try to write the packets to it */
if (!file_exists(cf_name)) {
g_free(cf_name);
file_export_specified_packets_cb(file_export_specified_packets_w, &range);
return;
}
/* Ask the user whether they want to overwrite it and, if so and
it's user-immutable or not writable, whether they want to
override that. */
if (!file_target_exist_ui(file_export_specified_packets_w, cf_name)) {
/* They don't. Let them try another file name or cancel. */
g_free(cf_name);
continue;
}
/* export packets */
g_free(cf_name);
file_export_specified_packets_cb(file_export_specified_packets_w, &range);
return;
}
#endif /* _WIN32 */
}
static void
file_export_specified_packets_select_file_type_cb(GtkWidget *w, gpointer data _U_)
{
int new_file_type;
gpointer ptr;
GtkWidget *compressed_cb;
if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
g_assert_not_reached(); /* Programming error: somehow nothing is active */
}
new_file_type = GPOINTER_TO_INT(ptr);
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(file_export_specified_packets_w), E_COMPRESSED_CB_KEY);
if (!wtap_dump_can_compress(new_file_type)) {
/* Can't compress this file type; turn off compression and make
the compression checkbox insensitive. */
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compressed_cb), FALSE);
gtk_widget_set_sensitive(compressed_cb, FALSE);
} else
gtk_widget_set_sensitive(compressed_cb, TRUE);
}
static void
file_export_specified_packets_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
{
gchar *cf_name;
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
switch(btn) {
case(ESD_BTN_OK):
/* save file */
file_export_specified_packets_cb(NULL, data);
break;
case(ESD_BTN_CANCEL):
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(file_export_specified_packets_w);
break;
default:
g_assert_not_reached();
}
g_free(cf_name);
}
/* user pressed "Export Specified Packets" dialog "Ok" button */
static void
file_export_specified_packets_ok_cb(GtkWidget *w _U_, gpointer fs) {
gchar *cf_name;
gpointer dialog;
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
/* Perhaps the user specified a directory instead of a file.
Check whether they did. */
if (test_for_directory(cf_name) == EISDIR) {
/* It's a directory - set the file selection box to display that
directory, and leave the selection box displayed. */
set_last_open_dir(cf_name);
g_free(cf_name);
file_selection_set_current_folder(fs, get_last_open_dir());
return;
}
/* Check whether the range is valid. */
if (!range_check_validity(&range)) {
/* The range isn't valid; don't dismiss the open dialog box,
just leave it around so that the user can, after they
dismiss the alert box popped up for the error, try again. */
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_save_as_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/*
* Check that the from file is not the same as to file
* We do it here so we catch all cases ...
* Unfortunately, the file requester gives us an absolute file
* name and the read file name may be relative (if supplied on
* the command line). From Joerg Mayer.
*/
if (files_identical(cfile.filename, cf_name)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%sCapture file: \"%s\" identical to loaded file!%s\n\n"
"Please choose a different filename.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
g_free(cf_name);
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
return;
}
/* don't show the dialog while saving (or asking) */
gtk_widget_hide(GTK_WIDGET (fs));
/* it the file doesn't exist, simply try to write the packets to it */
if (!file_exists(cf_name)) {
file_export_specified_packets_cb(NULL, fs);
g_free(cf_name);
return;
}
/* The file exists. Ask the user if they want to overwrite it. */
dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
"%s"
"A file named \"%s\" already exists. Do you want to replace it?"
"%s\n\n"
"A file or folder with the same name already exists in that folder. "
"Replacing it will overwrite its current contents.",
simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
simple_dialog_set_cb(dialog, file_export_specified_packets_exists_answered_cb, fs);
g_free(cf_name);
}
/* all tests ok, we only have to write out the packets */
/* (and probably continue with a pending operation) */
static void
file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
file_export_specified_packets_cb(GtkWidget *fs, packet_range_t *range) {
GtkWidget *ft_combo_box;
GtkWidget *compressed_cb;
gchar *cf_name;
@ -1896,6 +1633,9 @@ file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
int file_type;
gboolean compressed;
/* Hide the file chooser while we're doing the export. */
gtk_widget_hide(fs);
cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
@ -1908,7 +1648,7 @@ file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
compressed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb));
/* Write out the specified packets to the file with the specified name. */
if (cf_export_specified_packets(&cfile, cf_name, &range, file_type,
if (cf_export_specified_packets(&cfile, cf_name, range, file_type,
compressed) != CF_OK) {
/* The write failed; don't dismiss the open dialog box,
just leave it around so that the user can, after they
@ -1917,15 +1657,13 @@ file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
/* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
* as this will prevent the user from closing the now existing error
* message, simply close the dialog (this is the best we can do here). */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
window_destroy(GTK_WIDGET(fs));
return;
}
/* The write succeeded; get rid of the file selection box. */
/* cf_export_specified_packets() might already closed our dialog! */
if (file_export_specified_packets_w)
window_destroy(GTK_WIDGET (fs));
window_destroy(GTK_WIDGET(fs));
/* Save the directory name for future file dialogs.
XXX - should there be separate ones for "Save As" and
@ -1935,20 +1673,6 @@ file_export_specified_packets_cb(GtkWidget *w _U_, gpointer fs) {
g_free(cf_name);
}
void
file_export_specified_packets_destroy(void)
{
if (file_export_specified_packets_w)
window_destroy(file_export_specified_packets_w);
}
static void
file_export_specified_packets_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
{
/* Note that we no longer have a "Export Specified Packets" dialog box. */
file_export_specified_packets_w = NULL;
}
/* Reload a file using the current read and display filters */
void
file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {

View File

@ -43,14 +43,6 @@
*/
gboolean do_file_close(capture_file *cf, gboolean from_quit, const char *before_what);
/** Destroy the "Save As" dialog.
*/
void file_save_as_destroy(void);
/** Destroy the "Export Specified Packets" dialog.
*/
void file_export_specified_packets_destroy(void);
/** User requested the "Open" dialog box.
*
* @param widget parent widget
@ -93,14 +85,6 @@ void file_close_cmd_cb(GtkWidget *widget, gpointer data);
*/
void file_export_specified_packets_cmd_cb(GtkWidget *widget, gpointer data);
/*
* Set the "Save only marked packets" toggle button as appropriate for
* the current output file type and count of marked packets.
* Called when the "Export Specified Packets..." dialog box is created
* and when either the file type or the marked count changes.
*/
void file_export_specified_packets_update_dynamics(void);
/** User requested "Reload".
*
* @param widget parent widget

View File

@ -153,6 +153,141 @@ file_selection_set_extra_widget(GtkWidget *fs, GtkWidget *extra)
gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fs), extra);
}
/* Pop up and run the UI asking the user whether they want to
overwrite a file and, if it's user-immutable or not writable,
whether they want to overwrite it anyway. */
gboolean
file_target_exist_ui(GtkWidget *chooser_w, char *cf_name)
{
GtkWidget *msg_dialog;
gchar *display_basename;
gint response;
ws_statb64 statbuf;
/*
* The file exists. Ask the user if they want to overwrite it.
*
* XXX - what if it's a symlink? TextEdit just blindly replaces
* symlinks with files if you save over them, but gedit appears
* to replace the *target* of the symlink. (To find the target,
* use realpath() - it dates back to the SUSv2 and may be even
* older.)
*/
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(chooser_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"A file named \"%s\" already exists. Do you want to replace it?",
display_basename);
g_free(display_basename);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
"A file or folder with the same name already exists in that folder. "
"Replacing it will overwrite its current contents.");
gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
#ifndef _WIN32
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
"Replace", GTK_RESPONSE_ACCEPT,
#else
"Replace", GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
#endif
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_CANCEL);
response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
if (response != GTK_RESPONSE_ACCEPT) {
/* The user doesn't want to overwrite this file. */
return FALSE;
}
/* Check whether the file has all the write permission bits clear
and, on systems that have the 4.4-Lite file flags, whether it
has the "user immutable" flag set. Treat both of those as an
indication that the user wants to protect the file from
casual overwriting, and ask the user if they want to override
that.
(Linux's "immutable" flag, as fetched and set by the appropriate
ioctls (FS_IOC_GETFLAGS/FS_IOC_SETFLAGS in newer kernels,
EXT2_IOC_GETFLAGS/EXT2_IOC_SETFLAGS in older kernels - non-ext2
file systems that support those ioctls use the same values as ext2
does), appears to be more like the *BSD/OS X "system immutable"
flag, as it can be set only by the superuser or by processes with
CAP_LINUX_IMMUTABLE, so it sounds as if it's not intended for
arbitrary users to set or clear. */
if (ws_stat64(cf_name, &statbuf) != -1) {
/* OK, we have the permission bits and, if HAVE_ST_FLAGS is defined,
the flags. (If we don't, we don't worry about it.) */
#ifdef HAVE_ST_FLAGS
if (statbuf.st_flags & UF_IMMUTABLE) {
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(chooser_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
#ifdef __APPLE__
/* Stuff in the OS X UI calls files with the "user immutable" bit
"locked"; pre-OS X Mac software might have had that notion and
called it "locked". */
"The file \"%s\" is locked.",
#else /* __APPLE__ */
/* Just call it "immutable" in *BSD. */
"The file \"%s\" is immutable.",
#endif /* __APPLE__ */
display_basename);
g_free(display_basename);
} else
#endif /* HAVE_ST_FLAGS */
if ((statbuf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) {
display_basename = g_filename_display_basename(cf_name);
msg_dialog = gtk_message_dialog_new(GTK_WINDOW(chooser_w),
GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"The file \"%s\" is read-only.",
display_basename);
g_free(display_basename);
} else {
/* No problem, just drive on. */
msg_dialog = NULL;
}
if (msg_dialog != NULL) {
/* OK, ask the user if they want to overwrite the file. */
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
"Do you want to overwrite it anyway?");
gtk_dialog_add_buttons(GTK_DIALOG(msg_dialog),
"Overwrite", GTK_RESPONSE_ACCEPT,
"Don't overwrite", GTK_RESPONSE_REJECT,
NULL);
gtk_dialog_set_default_response(GTK_DIALOG(msg_dialog), GTK_RESPONSE_REJECT);
response = gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
if (response != GTK_RESPONSE_ACCEPT) {
/* The user doesn't want to overwrite this file. */
return FALSE;
}
#ifdef HAVE_ST_FLAGS
/* OK, they want to overwrite the file. If it has the "user
immutable" flag, we have to turn that off first, so we
can move on top of, or overwrite, the file. */
if (statbuf.st_flags & UF_IMMUTABLE) {
/* If this fails, the attempt to save will fail, so just
let that happen and pop up a "you lose" dialog. */
chflags(cf_name, statbuf.st_flags & ~UF_IMMUTABLE);
}
#endif
}
}
return TRUE;
}
/*
* A generic select_file routine that is intended to be connected to

View File

@ -85,6 +85,15 @@ extern gboolean file_selection_set_current_folder(GtkWidget *fs, const gchar *fi
*/
extern void file_selection_set_extra_widget(GtkWidget *fs, GtkWidget *extra);
/* Pop up and run the UI asking the user whether they want to
* overwrite a file and, if it's user-immutable or not writable,
* whether they want to overwrite it anyway.
*
* @param chooser_w the GtkFileChooser used to select the file in question
* @param cf_name the current name chosen
*/
extern gboolean file_target_exist_ui(GtkWidget *chooser_w, char *cf_name);
/** The function file_selection_browse() will g_object_set_data() itself on it's parent window.
* When destroying the parent window, it can close the corresponding file selection. */
#define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"

View File

@ -1425,8 +1425,6 @@ main_cf_cb_file_closing(capture_file *cf)
/* Destroy all windows that refer to the
capture file we're closing. */
destroy_packet_wins();
file_save_as_destroy();
file_export_specified_packets_destroy();
/* Restore the standard title bar message. */
main_set_window_name("The Wireshark Network Analyzer");

View File

@ -1450,7 +1450,6 @@ new_packet_list_set_font(PangoFontDescription *font)
static void
mark_frames_ready(void)
{
file_export_specified_packets_update_dynamics();
packets_bar_update();
new_packet_list_queue_draw();
}

View File

@ -88,6 +88,42 @@ range_check_validity(packet_range_t *range)
}
}
gboolean
range_check_validity_modal(GtkWidget *parent, packet_range_t *range)
{
GtkWidget *dialog;
switch (packet_range_check(range)) {
case CVT_NO_ERROR:
return TRUE;
case CVT_SYNTAX_ERROR:
dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"The specified range of packets isn't a valid range.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return FALSE;
case CVT_NUMBER_TOO_BIG:
dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"The specified range of packets has a packet number that's too large.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return FALSE;
default:
g_assert_not_reached();
return FALSE;
}
}
/* update all "dynamic" things */
void
range_update_dynamics(gpointer data)

View File

@ -39,6 +39,15 @@
*/
extern gboolean range_check_validity(packet_range_t *range);
/** Check the validity of a packet_range_t, and put up a modal alert box if
** it's not valid.
*
* @param parent the widget for the window that contained the range specification
* @param range the range to check
* @return a Boolean that's TRUE if it's valid and FALSE if it isn't
*/
extern gboolean range_check_validity_modal(GtkWidget *parent, packet_range_t *range);
/** Create a new range widget.
*
* @param range the range to set