3 * $Id: follow_dlg.c,v 1.58 2004/05/09 10:03:40 guy Exp $
5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@ethereal.com>
7 * Copyright 2000 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include <io.h> /* open/close on win32 */
44 #include "color_utils.h"
46 #include "follow_dlg.h"
48 #include "dlg_utils.h"
51 #include "gtkglobals.h"
53 #include "alert_box.h"
54 #include "simple_dialog.h"
55 #include "packet-ipv6.h"
57 #include <epan/resolv.h>
60 #include <epan/epan_dissect.h>
61 #include <epan/filesystem.h>
62 #include "compat_macros.h"
82 show_stream_t show_stream;
83 show_type_t show_type;
84 char data_out_filename[128 + 1];
88 GtkWidget *hexdump_bt;
90 GtkWidget *follow_save_as_w;
92 char *filter_out_filter;
94 GtkWidget *streamwindow;
97 static void follow_destroy_cb(GtkWidget * win, gpointer data);
98 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
99 static void follow_load_text(follow_info_t *follow_info);
100 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
101 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
102 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
103 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
104 static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
105 static void follow_stream_om_both(GtkWidget * w, gpointer data);
106 static void follow_stream_om_client(GtkWidget * w, gpointer data);
107 static void follow_stream_om_server(GtkWidget * w, gpointer data);
110 /* With MSVC and a libethereal.dll, we need a special declaration. */
111 ETH_VAR_IMPORT FILE *data_out_file;
113 #define E_FOLLOW_INFO_KEY "follow_info_key"
115 /* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
116 so we can redraw them all if the colors or font changes. */
117 static GList *follow_infos;
119 /* Add a "follow_info_t" structure to the list. */
121 remember_follow_info(follow_info_t *follow_info)
123 follow_infos = g_list_append(follow_infos, follow_info);
126 /* Remove a "follow_info_t" structure from the list. */
128 forget_follow_info(follow_info_t *follow_info)
130 follow_infos = g_list_remove(follow_infos, follow_info);
134 follow_redraw(gpointer data, gpointer user_data _U_)
136 follow_load_text((follow_info_t *)data);
139 /* Redraw the text in all "Follow TCP Stream" windows. */
141 follow_redraw_all(void)
143 g_list_foreach(follow_infos, follow_redraw, NULL);
146 /* Follow the TCP stream, if any, to which the last packet that we called
147 a dissection routine on belongs (this might be the most recently
148 selected packet, or it might be the last packet in the file). */
150 follow_stream_cb(GtkWidget * w, gpointer data _U_)
152 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
153 GtkWidget *hbox, *button_hbox, *button, *radio_bt;
154 GtkWidget *stream_fr, *stream_vb;
155 GtkWidget *stream_om, *stream_menu, *stream_mi;
156 GtkTooltips *tooltips;
158 gchar *follow_filter;
159 const gchar *previous_filter;
160 int filter_out_filter_len;
161 const char *hostname0, *hostname1;
164 follow_tcp_stats_t stats;
165 follow_info_t *follow_info;
167 /* we got tcp so we can follow */
168 if (cfile.edt->pi.ipproto != 6) {
169 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
170 "Error following stream. Please make\n"
171 "sure you have a TCP packet selected.");
175 follow_info = g_new0(follow_info_t, 1);
177 /* Create a temporary file into which to dump the reassembled data
178 from the TCP stream, and set "data_out_file" to refer to it, so
179 that the TCP code will write to it.
181 XXX - it might be nicer to just have the TCP code directly
182 append stuff to the text widget for the TCP stream window,
183 if we can arrange that said window not pop up until we're
185 tmp_fd = create_tempfile(follow_info->data_out_filename,
186 sizeof follow_info->data_out_filename, "follow");
189 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
190 "Could not create temporary file %s: %s",
191 follow_info->data_out_filename, strerror(errno));
196 data_out_file = fdopen(tmp_fd, "wb");
197 if (data_out_file == NULL) {
198 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
199 "Could not create temporary file %s: %s",
200 follow_info->data_out_filename, strerror(errno));
202 unlink(follow_info->data_out_filename);
207 /* Create a new filter that matches all packets in the TCP stream,
208 and set the display filter entry accordingly */
209 reset_tcp_reassembly();
210 follow_filter = build_follow_filter(&cfile.edt->pi);
212 /* Set the display filter entry accordingly */
213 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
215 /* needed in follow_filter_out_stream(), is there a better way? */
216 follow_info->filter_te = filter_te;
218 /* save previous filter, const since we're not supposed to alter */
220 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
222 /* allocate our new filter. API claims g_malloc terminates program on failure */
223 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
224 filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
225 follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
227 /* append the negation */
228 if(strlen(previous_filter)) {
229 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
230 "%s and !(%s)", previous_filter, follow_filter);
232 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
233 "!(%s)", follow_filter);
237 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
239 /* Run the display filter so it goes in effect - even if it's the
240 same as the previous display filter. */
241 main_filter_packets(&cfile, follow_filter, TRUE);
243 /* Free the filter string, as we're done with it. */
244 g_free(follow_filter);
246 /* The data_out_file should now be full of the streams information */
247 fclose(data_out_file);
249 /* The data_out_filename file now has all the text that was in the session */
250 streamwindow = window_new(GTK_WINDOW_TOPLEVEL, "Follow TCP stream");
252 /* needed in follow_filter_out_stream(), is there a better way? */
253 follow_info->streamwindow = streamwindow;
255 gtk_widget_set_name(streamwindow, "TCP stream window");
257 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
258 WIDGET_SET_SIZE(streamwindow, DEF_WIDTH, DEF_HEIGHT);
259 gtk_container_border_width(GTK_CONTAINER(streamwindow), 2);
261 /* setup the container */
262 tooltips = gtk_tooltips_new ();
264 vbox = gtk_vbox_new(FALSE, 0);
265 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
268 if (incomplete_tcp_stream) {
269 stream_fr = gtk_frame_new("Stream Content (incomplete)");
271 stream_fr = gtk_frame_new("Stream Content");
273 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
274 gtk_widget_show(stream_fr);
276 stream_vb = gtk_vbox_new(FALSE, 0);
277 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 5);
278 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
280 /* create a scrolled window for the text */
281 txt_scrollw = scrolled_window_new(NULL, NULL);
282 #if GTK_MAJOR_VERSION >= 2
283 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
286 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
288 /* create a text box */
289 #if GTK_MAJOR_VERSION < 2
290 text = gtk_text_new(NULL, NULL);
291 gtk_text_set_editable(GTK_TEXT(text), FALSE);
293 text = gtk_text_view_new();
294 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
296 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
297 follow_info->text = text;
301 hbox = gtk_hbox_new(FALSE, 1);
302 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
304 /* Create Save As Button */
305 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
306 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
307 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
308 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
310 /* Create Print Button */
311 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
312 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
313 gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
314 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
317 follow_tcp_stats(&stats);
320 struct e_in6_addr ipaddr;
321 memcpy(&ipaddr, stats.ip_address[0], 16);
322 hostname0 = get_hostname6(&ipaddr);
323 memcpy(&ipaddr, stats.ip_address[0], 16);
324 hostname1 = get_hostname6(&ipaddr);
327 memcpy(&ipaddr, stats.ip_address[0], 4);
328 hostname0 = get_hostname(ipaddr);
329 memcpy(&ipaddr, stats.ip_address[1], 4);
330 hostname1 = get_hostname(ipaddr);
333 port0 = get_tcp_port(stats.tcp_port[0]);
334 port1 = get_tcp_port(stats.tcp_port[1]);
336 follow_info->is_ipv6 = stats.is_ipv6;
338 stream_om = gtk_option_menu_new();
339 stream_menu = gtk_menu_new();
342 g_snprintf(string, sizeof(string),
343 "Entire conversation (%u bytes)",
344 stats.bytes_written[0] + stats.bytes_written[1]);
345 stream_mi = gtk_menu_item_new_with_label(string);
346 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
348 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
349 gtk_widget_show(stream_mi);
350 follow_info->show_stream = BOTH_HOSTS;
352 /* Host 0 --> Host 1 */
353 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
354 hostname0, port0, hostname1, port1,
355 stats.bytes_written[0]);
356 stream_mi = gtk_menu_item_new_with_label(string);
357 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
359 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
360 gtk_widget_show(stream_mi);
362 /* Host 1 --> Host 0 */
363 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
364 hostname1, port1, hostname0, port0,
365 stats.bytes_written[1]);
366 stream_mi = gtk_menu_item_new_with_label(string);
367 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
369 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
370 gtk_widget_show(stream_mi);
372 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
373 /* Set history to 0th item, i.e., the first item. */
374 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
375 gtk_tooltips_set_tip (tooltips, stream_om,
376 "Select the stream direction to display", NULL);
377 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
379 /* ASCII radio button */
380 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
381 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
382 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
383 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
385 follow_info->ascii_bt = radio_bt;
386 follow_info->show_type = SHOW_ASCII;
388 /* EBCDIC radio button */
389 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
390 (GTK_RADIO_BUTTON(radio_bt)),
392 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
393 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
394 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
396 follow_info->ebcdic_bt = radio_bt;
398 /* HEX DUMP radio button */
399 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
400 (GTK_RADIO_BUTTON(radio_bt)),
402 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
403 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
404 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
406 follow_info->hexdump_bt = radio_bt;
408 /* C Array radio button */
409 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
410 (GTK_RADIO_BUTTON(radio_bt)),
412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
413 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
414 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
416 follow_info->carray_bt = radio_bt;
419 button_hbox = gtk_hbutton_box_new();
420 gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
421 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
422 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
424 /* Create exclude stream button */
425 button = gtk_button_new_with_label("Filter out this stream");
426 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
427 gtk_tooltips_set_tip (tooltips, button,
428 "Build a display filter which cuts this stream from the capture", NULL);
429 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
431 /* Create Close Button */
432 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
433 SIGNAL_CONNECT_OBJECT(button, "clicked", gtk_widget_destroy,
435 gtk_tooltips_set_tip (tooltips, button,
436 "Close the dialog and keep the current display filter", NULL);
437 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
439 /* Catch the "key_press_event" signal in the window, so that we can catch
440 the ESC key being pressed and act as if the "Cancel" button had
442 dlg_set_cancel(streamwindow, button);
445 /* Tuck away the follow_info object into the window */
446 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
448 follow_load_text(follow_info);
449 remember_follow_info(follow_info);
451 data_out_file = NULL;
453 /* Make sure this widget gets destroyed if we quit the main loop,
454 so that if we exit, we clean up any temporary files we have
455 for "Follow TCP Stream" windows. */
456 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
457 gtk_widget_show_all(streamwindow);
460 /* The destroy call back has the responsibility of
461 * unlinking the temporary file
462 * and freeing the filter_out_filter */
464 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
466 follow_info_t *follow_info;
468 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
469 unlink(follow_info->data_out_filename);
470 g_free(follow_info->filter_out_filter);
471 gtk_widget_destroy(w);
472 forget_follow_info(follow_info);
476 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
477 * 3 different functions here? */
479 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
481 follow_info_t *follow_info = data;
482 follow_info->show_stream = BOTH_HOSTS;
483 follow_load_text(follow_info);
487 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
489 follow_info_t *follow_info = data;
490 follow_info->show_stream = FROM_CLIENT;
491 follow_load_text(follow_info);
495 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
497 follow_info_t *follow_info = data;
498 follow_info->show_stream = FROM_SERVER;
499 follow_load_text(follow_info);
503 /* Handles the ASCII/EBCDIC toggling */
505 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
507 follow_info_t *follow_info = data;
509 if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
510 follow_info->show_type = SHOW_EBCDIC;
511 else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
512 follow_info->show_type = SHOW_HEXDUMP;
513 else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
514 follow_info->show_type = SHOW_CARRAY;
515 else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
516 follow_info->show_type = SHOW_ASCII;
518 g_assert_not_reached();
520 follow_load_text(follow_info);
523 #define FLT_BUF_SIZE 1024
533 follow_read_stream(follow_info_t *follow_info,
534 gboolean (*print_line) (char *, int, gboolean, void *),
539 guint8 client_addr[MAX_IPADDR_LEN];
540 guint16 client_port = 0;
542 guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
546 guint32 server_packet_count = 0;
547 guint32 client_packet_count = 0;
548 char buffer[FLT_BUF_SIZE];
551 iplen = (follow_info->is_ipv6) ? 16 : 4;
553 data_out_file = fopen(follow_info->data_out_filename, "rb");
554 if (data_out_file == NULL) {
555 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
556 "Could not open temporary file %s: %s", follow_info->data_out_filename,
558 return FRS_OPEN_ERROR;
561 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
562 if (client_port == 0) {
563 memcpy(client_addr, sc.src_addr, iplen);
564 client_port = sc.src_port;
567 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
568 client_port == sc.src_port) {
570 global_pos = &global_client_pos;
571 if (follow_info->show_stream == FROM_SERVER) {
577 global_pos = &global_server_pos;
578 if (follow_info->show_stream == FROM_CLIENT) {
583 while (sc.dlen > 0) {
584 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
585 nchars = fread(buffer, 1, bcount, data_out_file);
590 switch (follow_info->show_type) {
593 /* If our native arch is ASCII, call: */
594 EBCDIC_to_ASCII(buffer, nchars);
595 if (!(*print_line) (buffer, nchars, is_server, arg))
600 /* If our native arch is EBCDIC, call:
601 * ASCII_TO_EBCDIC(buffer, nchars);
603 if (!(*print_line) (buffer, nchars, is_server, arg))
609 while (current_pos < nchars) {
611 gchar hexchars[] = "0123456789abcdef";
614 /* is_server indentation : put 63 spaces at the
615 * beginning of the string */
616 g_snprintf(hexbuf, sizeof(hexbuf),
617 (is_server && follow_info->show_stream == BOTH_HOSTS) ?
620 "%08X ", *global_pos);
621 cur = strlen(hexbuf);
622 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
624 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
626 hexchars[buffer[current_pos + i] & 0x0f];
633 /* Fill it up if column isn't complete */
637 for (j = i; j < 16; j++) {
647 /* Now dump bytes as text */
648 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
650 (isprint((guchar)buffer[current_pos + i]) ?
651 buffer[current_pos + i] : '.' );
658 hexbuf[cur++] = '\n';
660 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
667 g_snprintf(initbuf, 256, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
668 is_server ? server_packet_count++ : client_packet_count++);
669 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
671 while (current_pos < nchars) {
673 gchar hexchars[] = "0123456789abcdef";
677 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
678 /* Prepend entries with "0x" */
682 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
684 hexchars[buffer[current_pos + i] & 0x0f];
686 /* Delimit array entries with a comma */
687 if (current_pos + i + 1 < nchars)
693 /* Terminate the array if we are at the end */
694 if (current_pos + i == nchars) {
701 hexbuf[cur++] = '\n';
703 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
711 if (ferror(data_out_file)) {
712 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
713 "Error reading temporary file %s: %s", follow_info->data_out_filename,
715 fclose(data_out_file);
716 data_out_file = NULL;
717 return FRS_READ_ERROR;
723 fclose(data_out_file);
724 data_out_file = NULL;
725 return FRS_PRINT_ERROR;
729 * XXX - for text printing, we probably want to wrap lines at 80 characters;
730 * for PostScript printing, we probably want to wrap them at the appropriate
731 * width, and perhaps put some kind of dingbat (to use the technical term)
732 * to indicate a wrapped line, along the lines of what's done when displaying
733 * this in a window, as per Warren Young's suggestion.
735 * For now, we support only text printing.
738 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg)
742 if (fwrite(buffer, nchars, 1, fh) != 1)
748 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
750 follow_info_t *follow_info = data;
752 /* Lock out user from messing with us. (ie. don't free our data!) */
753 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
755 /* Set the display filter. */
756 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
758 /* Run the display filter so it goes in effect. */
759 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
761 /* we force a subsequent close */
762 gtk_widget_destroy(follow_info->streamwindow);
768 follow_print_stream(GtkWidget * w _U_, gpointer data)
773 follow_info_t *follow_info = data;
775 switch (prefs.pr_dest) {
777 print_dest = prefs.pr_cmd;
782 print_dest = prefs.pr_file;
785 default: /* "Can't happen" */
786 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
787 "Couldn't figure out where to send the print "
788 "job. Check your preferences.");
792 fh = open_print_dest(to_file, print_dest);
795 open_failure_alert_box(prefs.pr_file, errno, TRUE);
797 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
798 "Couldn't run print command %s.", prefs.pr_cmd);
803 print_preamble(fh, PR_FMT_TEXT, cfile.filename);
806 switch (follow_read_stream(follow_info, follow_print_text, fh)) {
813 /* XXX - cancel printing? */
814 close_print_dest(to_file, fh);
817 case FRS_PRINT_ERROR:
820 print_finale(fh, PR_FMT_TEXT);
823 if (!close_print_dest(to_file, fh)) {
825 write_failure_alert_box(prefs.pr_file, errno);
827 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
828 "Error closing print destination.");
835 write_failure_alert_box(prefs.pr_file, errno);
837 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
838 "Error writing to print command: %s", strerror(errno));
840 /* XXX - cancel printing? */
841 close_print_dest(to_file, fh);
845 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
848 GtkWidget *text = arg;
850 #if GTK_MAJOR_VERSION >= 2
851 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
858 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
859 /* While our isprint() hack is in place, we
860 * have to use convert some chars to '.' in order
861 * to be able to see the data we *should* see
862 * in the GtkText widget.
866 for (i = 0; i < nchars; i++) {
867 if (buffer[i] == '\n' || buffer[i] == '\r')
869 if (! isprint(buffer[i])) {
876 color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
877 color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
879 color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
880 color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
882 #if GTK_MAJOR_VERSION < 2
883 gtk_text_insert(GTK_TEXT(text), m_r_font, &fg, &bg, buffer, nchars);
885 gtk_text_buffer_get_end_iter(buf, &iter);
886 tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
887 "background-gdk", &bg, "font-desc",
889 convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
890 gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
898 follow_load_text(follow_info_t *follow_info)
900 #if GTK_MAJOR_VERSION < 2
905 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
908 /* Delete any info already in text box */
909 #if GTK_MAJOR_VERSION < 2
910 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
911 if (bytes_already > 0) {
912 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
913 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
916 /* stop the updates while we fill the text box */
917 gtk_text_freeze(GTK_TEXT(follow_info->text));
919 gtk_text_buffer_set_text(buf, "", -1);
921 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
922 #if GTK_MAJOR_VERSION < 2
923 gtk_text_thaw(GTK_TEXT(follow_info->text));
929 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
930 * any, so that if somebody tries to do "Save"
931 * while there's already a "Save TCP Follow Stream" window up, we just pop
932 * up the existing one, rather than creating a new one.
935 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
937 #if GTK_MAJOR_VERSION < 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4)
941 follow_info_t *follow_info = data;
943 if (follow_info->follow_save_as_w != NULL) {
944 /* There's already a dialog box; reactivate it. */
945 reactivate_window(follow_info->follow_save_as_w);
949 new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
950 FILE_SELECTION_SAVE);
951 follow_info->follow_save_as_w = new_win;
952 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
954 /* Tuck away the follow_info object into the window */
955 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
957 /* If we've opened a file, start out by showing the files in the directory
958 in which that file resided. */
960 file_selection_set_current_folder(new_win, last_open_dir);
962 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
963 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
965 follow_save_as_ok_cb(new_win, new_win);
967 else gtk_widget_destroy(new_win);
969 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
970 pointer to the file selection box widget */
971 ok_bt = GTK_FILE_SELECTION(new_win)->ok_button;
972 SIGNAL_CONNECT(ok_bt, "clicked", follow_save_as_ok_cb, new_win);
974 /* Connect the cancel_button to destroy the widget */
975 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(new_win)->cancel_button, "clicked",
976 gtk_widget_destroy, new_win);
978 /* Catch the "key_press_event" signal in the window, so that we can catch
979 the ESC key being pressed and act as if the "Cancel" button had
981 dlg_set_cancel(new_win,
982 GTK_FILE_SELECTION(new_win)->cancel_button);
984 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
985 gtk_widget_show_all(new_win);
991 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
994 follow_info_t *follow_info;
998 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
999 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1001 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1004 /* Perhaps the user specified a directory instead of a file.
1005 Check whether they did. */
1006 if (test_for_directory(to_name) == EISDIR) {
1007 /* It's a directory - set the file selection box to display that
1008 directory, and leave the selection box displayed. */
1009 set_last_open_dir(to_name);
1011 file_selection_set_current_folder(fs, last_open_dir);
1015 fh = fopen(to_name, "wb");
1017 open_failure_alert_box(to_name, errno, TRUE);
1022 gtk_widget_hide(GTK_WIDGET(fs));
1023 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1024 gtk_widget_destroy(GTK_WIDGET(fs));
1026 switch (follow_read_stream(follow_info, follow_print_text, fh)) {
1029 if (fclose(fh) == EOF)
1030 write_failure_alert_box(to_name, errno);
1033 case FRS_OPEN_ERROR:
1034 case FRS_READ_ERROR:
1038 case FRS_PRINT_ERROR:
1039 write_failure_alert_box(to_name, errno);
1044 /* Save the directory name for future file dialogs. */
1045 dirname = get_dirname(to_name); /* Overwrites to_name */
1046 set_last_open_dir(dirname);
1051 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1053 follow_info_t *follow_info = data;
1055 /* Note that we no longer have a dialog box. */
1056 follow_info->follow_save_as_w = NULL;