3 * $Id: follow_dlg.c,v 1.60 2004/05/26 03:49:23 ulfl 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 = dlg_window_new("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");
256 gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
257 gtk_container_border_width(GTK_CONTAINER(streamwindow), 2);
259 /* setup the container */
260 tooltips = gtk_tooltips_new ();
262 vbox = gtk_vbox_new(FALSE, 0);
263 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
266 if (incomplete_tcp_stream) {
267 stream_fr = gtk_frame_new("Stream Content (incomplete)");
269 stream_fr = gtk_frame_new("Stream Content");
271 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
272 gtk_widget_show(stream_fr);
274 stream_vb = gtk_vbox_new(FALSE, 0);
275 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 5);
276 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
278 /* create a scrolled window for the text */
279 txt_scrollw = scrolled_window_new(NULL, NULL);
280 #if GTK_MAJOR_VERSION >= 2
281 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
284 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
286 /* create a text box */
287 #if GTK_MAJOR_VERSION < 2
288 text = gtk_text_new(NULL, NULL);
289 gtk_text_set_editable(GTK_TEXT(text), FALSE);
291 text = gtk_text_view_new();
292 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
294 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
295 follow_info->text = text;
299 hbox = gtk_hbox_new(FALSE, 1);
300 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
302 /* Create Save As Button */
303 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
304 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
305 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
306 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
308 /* Create Print Button */
309 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
310 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
311 gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
312 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
315 follow_tcp_stats(&stats);
318 struct e_in6_addr ipaddr;
319 memcpy(&ipaddr, stats.ip_address[0], 16);
320 hostname0 = get_hostname6(&ipaddr);
321 memcpy(&ipaddr, stats.ip_address[0], 16);
322 hostname1 = get_hostname6(&ipaddr);
325 memcpy(&ipaddr, stats.ip_address[0], 4);
326 hostname0 = get_hostname(ipaddr);
327 memcpy(&ipaddr, stats.ip_address[1], 4);
328 hostname1 = get_hostname(ipaddr);
331 port0 = get_tcp_port(stats.tcp_port[0]);
332 port1 = get_tcp_port(stats.tcp_port[1]);
334 follow_info->is_ipv6 = stats.is_ipv6;
336 stream_om = gtk_option_menu_new();
337 stream_menu = gtk_menu_new();
340 g_snprintf(string, sizeof(string),
341 "Entire conversation (%u bytes)",
342 stats.bytes_written[0] + stats.bytes_written[1]);
343 stream_mi = gtk_menu_item_new_with_label(string);
344 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
346 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
347 gtk_widget_show(stream_mi);
348 follow_info->show_stream = BOTH_HOSTS;
350 /* Host 0 --> Host 1 */
351 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
352 hostname0, port0, hostname1, port1,
353 stats.bytes_written[0]);
354 stream_mi = gtk_menu_item_new_with_label(string);
355 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
357 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
358 gtk_widget_show(stream_mi);
360 /* Host 1 --> Host 0 */
361 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
362 hostname1, port1, hostname0, port0,
363 stats.bytes_written[1]);
364 stream_mi = gtk_menu_item_new_with_label(string);
365 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
367 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
368 gtk_widget_show(stream_mi);
370 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
371 /* Set history to 0th item, i.e., the first item. */
372 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
373 gtk_tooltips_set_tip (tooltips, stream_om,
374 "Select the stream direction to display", NULL);
375 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
377 /* ASCII radio button */
378 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
379 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
380 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
381 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
383 follow_info->ascii_bt = radio_bt;
384 follow_info->show_type = SHOW_ASCII;
386 /* EBCDIC radio button */
387 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
388 (GTK_RADIO_BUTTON(radio_bt)),
390 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
391 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
392 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
394 follow_info->ebcdic_bt = radio_bt;
396 /* HEX DUMP radio button */
397 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
398 (GTK_RADIO_BUTTON(radio_bt)),
400 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
401 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
402 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
404 follow_info->hexdump_bt = radio_bt;
406 /* C Array radio button */
407 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
408 (GTK_RADIO_BUTTON(radio_bt)),
410 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
411 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
412 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
414 follow_info->carray_bt = radio_bt;
417 button_hbox = gtk_hbutton_box_new();
418 gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
419 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
420 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
422 /* Create exclude stream button */
423 button = gtk_button_new_with_label("Filter out this stream");
424 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
425 gtk_tooltips_set_tip (tooltips, button,
426 "Build a display filter which cuts this stream from the capture", NULL);
427 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
429 /* Create Close Button */
430 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
431 gtk_tooltips_set_tip (tooltips, button,
432 "Close the dialog and keep the current display filter", NULL);
433 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
434 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
436 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
438 /* Tuck away the follow_info object into the window */
439 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
441 follow_load_text(follow_info);
442 remember_follow_info(follow_info);
444 data_out_file = NULL;
446 SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
447 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
449 /* Make sure this widget gets destroyed if we quit the main loop,
450 so that if we exit, we clean up any temporary files we have
451 for "Follow TCP Stream" windows. */
452 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
454 gtk_widget_show_all(streamwindow);
455 window_present(streamwindow);
458 /* The destroy call back has the responsibility of
459 * unlinking the temporary file
460 * and freeing the filter_out_filter */
462 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
464 follow_info_t *follow_info;
466 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
467 unlink(follow_info->data_out_filename);
468 g_free(follow_info->filter_out_filter);
470 forget_follow_info(follow_info);
474 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
475 * 3 different functions here? */
477 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
479 follow_info_t *follow_info = data;
480 follow_info->show_stream = BOTH_HOSTS;
481 follow_load_text(follow_info);
485 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
487 follow_info_t *follow_info = data;
488 follow_info->show_stream = FROM_CLIENT;
489 follow_load_text(follow_info);
493 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
495 follow_info_t *follow_info = data;
496 follow_info->show_stream = FROM_SERVER;
497 follow_load_text(follow_info);
501 /* Handles the ASCII/EBCDIC toggling */
503 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
505 follow_info_t *follow_info = data;
507 if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
508 follow_info->show_type = SHOW_EBCDIC;
509 else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
510 follow_info->show_type = SHOW_HEXDUMP;
511 else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
512 follow_info->show_type = SHOW_CARRAY;
513 else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
514 follow_info->show_type = SHOW_ASCII;
516 g_assert_not_reached();
518 follow_load_text(follow_info);
521 #define FLT_BUF_SIZE 1024
531 follow_read_stream(follow_info_t *follow_info,
532 gboolean (*print_line) (char *, int, gboolean, void *),
537 guint8 client_addr[MAX_IPADDR_LEN];
538 guint16 client_port = 0;
540 guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
544 guint32 server_packet_count = 0;
545 guint32 client_packet_count = 0;
546 char buffer[FLT_BUF_SIZE];
549 iplen = (follow_info->is_ipv6) ? 16 : 4;
551 data_out_file = fopen(follow_info->data_out_filename, "rb");
552 if (data_out_file == NULL) {
553 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
554 "Could not open temporary file %s: %s", follow_info->data_out_filename,
556 return FRS_OPEN_ERROR;
559 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
560 if (client_port == 0) {
561 memcpy(client_addr, sc.src_addr, iplen);
562 client_port = sc.src_port;
565 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
566 client_port == sc.src_port) {
568 global_pos = &global_client_pos;
569 if (follow_info->show_stream == FROM_SERVER) {
575 global_pos = &global_server_pos;
576 if (follow_info->show_stream == FROM_CLIENT) {
581 while (sc.dlen > 0) {
582 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
583 nchars = fread(buffer, 1, bcount, data_out_file);
588 switch (follow_info->show_type) {
591 /* If our native arch is ASCII, call: */
592 EBCDIC_to_ASCII(buffer, nchars);
593 if (!(*print_line) (buffer, nchars, is_server, arg))
598 /* If our native arch is EBCDIC, call:
599 * ASCII_TO_EBCDIC(buffer, nchars);
601 if (!(*print_line) (buffer, nchars, is_server, arg))
607 while (current_pos < nchars) {
609 gchar hexchars[] = "0123456789abcdef";
612 /* is_server indentation : put 63 spaces at the
613 * beginning of the string */
614 g_snprintf(hexbuf, sizeof(hexbuf),
615 (is_server && follow_info->show_stream == BOTH_HOSTS) ?
618 "%08X ", *global_pos);
619 cur = strlen(hexbuf);
620 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
622 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
624 hexchars[buffer[current_pos + i] & 0x0f];
631 /* Fill it up if column isn't complete */
635 for (j = i; j < 16; j++) {
645 /* Now dump bytes as text */
646 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
648 (isprint((guchar)buffer[current_pos + i]) ?
649 buffer[current_pos + i] : '.' );
656 hexbuf[cur++] = '\n';
658 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
665 g_snprintf(initbuf, 256, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
666 is_server ? server_packet_count++ : client_packet_count++);
667 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
669 while (current_pos < nchars) {
671 gchar hexchars[] = "0123456789abcdef";
675 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
676 /* Prepend entries with "0x" */
680 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
682 hexchars[buffer[current_pos + i] & 0x0f];
684 /* Delimit array entries with a comma */
685 if (current_pos + i + 1 < nchars)
691 /* Terminate the array if we are at the end */
692 if (current_pos + i == nchars) {
699 hexbuf[cur++] = '\n';
701 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
709 if (ferror(data_out_file)) {
710 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
711 "Error reading temporary file %s: %s", follow_info->data_out_filename,
713 fclose(data_out_file);
714 data_out_file = NULL;
715 return FRS_READ_ERROR;
721 fclose(data_out_file);
722 data_out_file = NULL;
723 return FRS_PRINT_ERROR;
727 * XXX - for text printing, we probably want to wrap lines at 80 characters;
728 * for PostScript printing, we probably want to wrap them at the appropriate
729 * width, and perhaps put some kind of dingbat (to use the technical term)
730 * to indicate a wrapped line, along the lines of what's done when displaying
731 * this in a window, as per Warren Young's suggestion.
733 * For now, we support only text printing.
736 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg)
740 if (fwrite(buffer, nchars, 1, fh) != 1)
746 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
748 follow_info_t *follow_info = data;
750 /* Lock out user from messing with us. (ie. don't free our data!) */
751 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
753 /* Set the display filter. */
754 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
756 /* Run the display filter so it goes in effect. */
757 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
759 /* we force a subsequent close */
760 window_destroy(follow_info->streamwindow);
766 follow_print_stream(GtkWidget * w _U_, gpointer data)
771 follow_info_t *follow_info = data;
773 switch (prefs.pr_dest) {
775 print_dest = prefs.pr_cmd;
780 print_dest = prefs.pr_file;
783 default: /* "Can't happen" */
784 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
785 "Couldn't figure out where to send the print "
786 "job. Check your preferences.");
790 fh = open_print_dest(to_file, print_dest);
793 open_failure_alert_box(prefs.pr_file, errno, TRUE);
795 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
796 "Couldn't run print command %s.", prefs.pr_cmd);
801 print_preamble(fh, PR_FMT_TEXT, cfile.filename);
804 switch (follow_read_stream(follow_info, follow_print_text, fh)) {
811 /* XXX - cancel printing? */
812 close_print_dest(to_file, fh);
815 case FRS_PRINT_ERROR:
818 print_finale(fh, PR_FMT_TEXT);
821 if (!close_print_dest(to_file, fh)) {
823 write_failure_alert_box(prefs.pr_file, errno);
825 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
826 "Error closing print destination.");
833 write_failure_alert_box(prefs.pr_file, errno);
835 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
836 "Error writing to print command: %s", strerror(errno));
838 /* XXX - cancel printing? */
839 close_print_dest(to_file, fh);
843 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
846 GtkWidget *text = arg;
848 #if GTK_MAJOR_VERSION >= 2
849 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
856 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
857 /* While our isprint() hack is in place, we
858 * have to use convert some chars to '.' in order
859 * to be able to see the data we *should* see
860 * in the GtkText widget.
864 for (i = 0; i < nchars; i++) {
865 if (buffer[i] == '\n' || buffer[i] == '\r')
867 if (! isprint(buffer[i])) {
874 color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
875 color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
877 color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
878 color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
880 #if GTK_MAJOR_VERSION < 2
881 gtk_text_insert(GTK_TEXT(text), m_r_font, &fg, &bg, buffer, nchars);
883 gtk_text_buffer_get_end_iter(buf, &iter);
884 tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
885 "background-gdk", &bg, "font-desc",
887 convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
888 gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
896 follow_load_text(follow_info_t *follow_info)
898 #if GTK_MAJOR_VERSION < 2
903 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
906 /* Delete any info already in text box */
907 #if GTK_MAJOR_VERSION < 2
908 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
909 if (bytes_already > 0) {
910 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
911 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
914 /* stop the updates while we fill the text box */
915 gtk_text_freeze(GTK_TEXT(follow_info->text));
917 gtk_text_buffer_set_text(buf, "", -1);
919 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
920 #if GTK_MAJOR_VERSION < 2
921 gtk_text_thaw(GTK_TEXT(follow_info->text));
927 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
928 * any, so that if somebody tries to do "Save"
929 * while there's already a "Save TCP Follow Stream" window up, we just pop
930 * up the existing one, rather than creating a new one.
933 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
936 follow_info_t *follow_info = data;
938 if (follow_info->follow_save_as_w != NULL) {
939 /* There's already a dialog box; reactivate it. */
940 reactivate_window(follow_info->follow_save_as_w);
944 new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
945 FILE_SELECTION_SAVE);
946 follow_info->follow_save_as_w = new_win;
948 /* Tuck away the follow_info object into the window */
949 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
951 /* If we've opened a file, start out by showing the files in the directory
952 in which that file resided. */
954 file_selection_set_current_folder(new_win, last_open_dir);
956 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
957 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
959 follow_save_as_ok_cb(new_win, new_win);
961 else window_destroy(new_win);
963 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
964 pointer to the file selection box widget */
965 SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
966 "clicked", follow_save_as_ok_cb, new_win);
968 window_set_cancel_button(new_win,
969 GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
971 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
973 SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
974 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
976 gtk_widget_show_all(new_win);
977 window_present(new_win);
983 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
986 follow_info_t *follow_info;
990 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
991 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
993 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
996 /* Perhaps the user specified a directory instead of a file.
997 Check whether they did. */
998 if (test_for_directory(to_name) == EISDIR) {
999 /* It's a directory - set the file selection box to display that
1000 directory, and leave the selection box displayed. */
1001 set_last_open_dir(to_name);
1003 file_selection_set_current_folder(fs, last_open_dir);
1007 fh = fopen(to_name, "wb");
1009 open_failure_alert_box(to_name, errno, TRUE);
1014 gtk_widget_hide(GTK_WIDGET(fs));
1015 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1016 window_destroy(GTK_WIDGET(fs));
1018 switch (follow_read_stream(follow_info, follow_print_text, fh)) {
1021 if (fclose(fh) == EOF)
1022 write_failure_alert_box(to_name, errno);
1025 case FRS_OPEN_ERROR:
1026 case FRS_READ_ERROR:
1030 case FRS_PRINT_ERROR:
1031 write_failure_alert_box(to_name, errno);
1036 /* Save the directory name for future file dialogs. */
1037 dirname = get_dirname(to_name); /* Overwrites to_name */
1038 set_last_open_dir(dirname);
1043 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1045 follow_info_t *follow_info = data;
1047 /* Note that we no longer have a dialog box. */
1048 follow_info->follow_save_as_w = NULL;