3 * $Id: follow_dlg.c,v 1.64 2004/05/27 23:09:09 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"
65 #include "print_mswin.h"
83 show_stream_t show_stream;
84 show_type_t show_type;
85 char data_out_filename[128 + 1];
89 GtkWidget *hexdump_bt;
91 GtkWidget *follow_save_as_w;
93 char *filter_out_filter;
95 GtkWidget *streamwindow;
98 static void follow_destroy_cb(GtkWidget * win, gpointer data);
99 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
100 static void follow_load_text(follow_info_t *follow_info);
101 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
102 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
103 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
104 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
105 static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
106 static void follow_stream_om_both(GtkWidget * w, gpointer data);
107 static void follow_stream_om_client(GtkWidget * w, gpointer data);
108 static void follow_stream_om_server(GtkWidget * w, gpointer data);
111 /* With MSVC and a libethereal.dll, we need a special declaration. */
112 ETH_VAR_IMPORT FILE *data_out_file;
114 #define E_FOLLOW_INFO_KEY "follow_info_key"
116 /* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
117 so we can redraw them all if the colors or font changes. */
118 static GList *follow_infos;
120 /* Add a "follow_info_t" structure to the list. */
122 remember_follow_info(follow_info_t *follow_info)
124 follow_infos = g_list_append(follow_infos, follow_info);
127 /* Remove a "follow_info_t" structure from the list. */
129 forget_follow_info(follow_info_t *follow_info)
131 follow_infos = g_list_remove(follow_infos, follow_info);
135 follow_redraw(gpointer data, gpointer user_data _U_)
137 follow_load_text((follow_info_t *)data);
140 /* Redraw the text in all "Follow TCP Stream" windows. */
142 follow_redraw_all(void)
144 g_list_foreach(follow_infos, follow_redraw, NULL);
147 /* Follow the TCP stream, if any, to which the last packet that we called
148 a dissection routine on belongs (this might be the most recently
149 selected packet, or it might be the last packet in the file). */
151 follow_stream_cb(GtkWidget * w, gpointer data _U_)
153 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
154 GtkWidget *hbox, *button_hbox, *button, *radio_bt;
155 GtkWidget *stream_fr, *stream_vb;
156 GtkWidget *stream_om, *stream_menu, *stream_mi;
157 GtkTooltips *tooltips;
159 gchar *follow_filter;
160 const gchar *previous_filter;
161 int filter_out_filter_len;
162 const char *hostname0, *hostname1;
165 follow_tcp_stats_t stats;
166 follow_info_t *follow_info;
168 /* we got tcp so we can follow */
169 if (cfile.edt->pi.ipproto != 6) {
170 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
171 "Error following stream. Please make\n"
172 "sure you have a TCP packet selected.");
176 follow_info = g_new0(follow_info_t, 1);
178 /* Create a temporary file into which to dump the reassembled data
179 from the TCP stream, and set "data_out_file" to refer to it, so
180 that the TCP code will write to it.
182 XXX - it might be nicer to just have the TCP code directly
183 append stuff to the text widget for the TCP stream window,
184 if we can arrange that said window not pop up until we're
186 tmp_fd = create_tempfile(follow_info->data_out_filename,
187 sizeof follow_info->data_out_filename, "follow");
190 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
191 "Could not create temporary file %s: %s",
192 follow_info->data_out_filename, strerror(errno));
197 data_out_file = fdopen(tmp_fd, "wb");
198 if (data_out_file == NULL) {
199 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
200 "Could not create temporary file %s: %s",
201 follow_info->data_out_filename, strerror(errno));
203 unlink(follow_info->data_out_filename);
208 /* Create a new filter that matches all packets in the TCP stream,
209 and set the display filter entry accordingly */
210 reset_tcp_reassembly();
211 follow_filter = build_follow_filter(&cfile.edt->pi);
213 /* Set the display filter entry accordingly */
214 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
216 /* needed in follow_filter_out_stream(), is there a better way? */
217 follow_info->filter_te = filter_te;
219 /* save previous filter, const since we're not supposed to alter */
221 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
223 /* allocate our new filter. API claims g_malloc terminates program on failure */
224 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
225 filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
226 follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
228 /* append the negation */
229 if(strlen(previous_filter)) {
230 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
231 "%s and !(%s)", previous_filter, follow_filter);
233 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
234 "!(%s)", follow_filter);
238 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
240 /* Run the display filter so it goes in effect - even if it's the
241 same as the previous display filter. */
242 main_filter_packets(&cfile, follow_filter, TRUE);
244 /* Free the filter string, as we're done with it. */
245 g_free(follow_filter);
247 /* The data_out_file should now be full of the streams information */
248 fclose(data_out_file);
250 /* The data_out_filename file now has all the text that was in the session */
251 streamwindow = dlg_window_new("Follow TCP stream");
253 /* needed in follow_filter_out_stream(), is there a better way? */
254 follow_info->streamwindow = streamwindow;
256 gtk_widget_set_name(streamwindow, "TCP stream window");
257 gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
258 gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
260 /* setup the container */
261 tooltips = gtk_tooltips_new ();
263 vbox = gtk_vbox_new(FALSE, 6);
264 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
267 if (incomplete_tcp_stream) {
268 stream_fr = gtk_frame_new("Stream Content (incomplete)");
270 stream_fr = gtk_frame_new("Stream Content");
272 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
273 gtk_widget_show(stream_fr);
275 stream_vb = gtk_vbox_new(FALSE, 6);
276 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
277 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
279 /* create a scrolled window for the text */
280 txt_scrollw = scrolled_window_new(NULL, NULL);
281 #if GTK_MAJOR_VERSION >= 2
282 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
285 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
287 /* create a text box */
288 #if GTK_MAJOR_VERSION < 2
289 text = gtk_text_new(NULL, NULL);
290 gtk_text_set_editable(GTK_TEXT(text), FALSE);
292 text = gtk_text_view_new();
293 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
295 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
296 follow_info->text = text;
300 hbox = gtk_hbox_new(FALSE, 1);
301 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
303 /* Create Save As Button */
304 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
305 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
306 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
307 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
309 /* Create Print Button */
310 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
311 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
312 gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
313 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
316 follow_tcp_stats(&stats);
319 struct e_in6_addr ipaddr;
320 memcpy(&ipaddr, stats.ip_address[0], 16);
321 hostname0 = get_hostname6(&ipaddr);
322 memcpy(&ipaddr, stats.ip_address[0], 16);
323 hostname1 = get_hostname6(&ipaddr);
326 memcpy(&ipaddr, stats.ip_address[0], 4);
327 hostname0 = get_hostname(ipaddr);
328 memcpy(&ipaddr, stats.ip_address[1], 4);
329 hostname1 = get_hostname(ipaddr);
332 port0 = get_tcp_port(stats.tcp_port[0]);
333 port1 = get_tcp_port(stats.tcp_port[1]);
335 follow_info->is_ipv6 = stats.is_ipv6;
337 stream_om = gtk_option_menu_new();
338 stream_menu = gtk_menu_new();
341 g_snprintf(string, sizeof(string),
342 "Entire conversation (%u bytes)",
343 stats.bytes_written[0] + stats.bytes_written[1]);
344 stream_mi = gtk_menu_item_new_with_label(string);
345 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
347 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
348 gtk_widget_show(stream_mi);
349 follow_info->show_stream = BOTH_HOSTS;
351 /* Host 0 --> Host 1 */
352 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
353 hostname0, port0, hostname1, port1,
354 stats.bytes_written[0]);
355 stream_mi = gtk_menu_item_new_with_label(string);
356 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
358 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
359 gtk_widget_show(stream_mi);
361 /* Host 1 --> Host 0 */
362 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
363 hostname1, port1, hostname0, port0,
364 stats.bytes_written[1]);
365 stream_mi = gtk_menu_item_new_with_label(string);
366 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
368 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
369 gtk_widget_show(stream_mi);
371 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
372 /* Set history to 0th item, i.e., the first item. */
373 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
374 gtk_tooltips_set_tip (tooltips, stream_om,
375 "Select the stream direction to display", NULL);
376 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
378 /* ASCII radio button */
379 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
380 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
381 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
382 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
384 follow_info->ascii_bt = radio_bt;
385 follow_info->show_type = SHOW_ASCII;
387 /* EBCDIC radio button */
388 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
389 (GTK_RADIO_BUTTON(radio_bt)),
391 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
392 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
393 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
395 follow_info->ebcdic_bt = radio_bt;
397 /* HEX DUMP radio button */
398 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
399 (GTK_RADIO_BUTTON(radio_bt)),
401 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
402 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
403 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
405 follow_info->hexdump_bt = radio_bt;
407 /* C Array radio button */
408 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
409 (GTK_RADIO_BUTTON(radio_bt)),
411 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
412 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
413 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
415 follow_info->carray_bt = radio_bt;
418 button_hbox = gtk_hbutton_box_new();
419 gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
420 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
421 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
423 /* Create exclude stream button */
424 button = gtk_button_new_with_label("Filter out this stream");
425 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
426 gtk_tooltips_set_tip (tooltips, button,
427 "Build a display filter which cuts this stream from the capture", NULL);
428 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
430 /* Create Close Button */
431 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
432 gtk_tooltips_set_tip (tooltips, button,
433 "Close the dialog and keep the current display filter", NULL);
434 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
435 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
437 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
439 /* Tuck away the follow_info object into the window */
440 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
442 follow_load_text(follow_info);
443 remember_follow_info(follow_info);
445 data_out_file = NULL;
447 SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
448 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
450 /* Make sure this widget gets destroyed if we quit the main loop,
451 so that if we exit, we clean up any temporary files we have
452 for "Follow TCP Stream" windows. */
453 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
455 gtk_widget_show_all(streamwindow);
456 window_present(streamwindow);
459 /* The destroy call back has the responsibility of
460 * unlinking the temporary file
461 * and freeing the filter_out_filter */
463 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
465 follow_info_t *follow_info;
468 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
469 i = unlink(follow_info->data_out_filename);
471 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
472 follow_info->data_out_filename, strerror(errno), errno);
474 g_free(follow_info->filter_out_filter);
475 forget_follow_info(follow_info);
479 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
480 * 3 different functions here? */
482 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
484 follow_info_t *follow_info = data;
485 follow_info->show_stream = BOTH_HOSTS;
486 follow_load_text(follow_info);
490 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
492 follow_info_t *follow_info = data;
493 follow_info->show_stream = FROM_CLIENT;
494 follow_load_text(follow_info);
498 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
500 follow_info_t *follow_info = data;
501 follow_info->show_stream = FROM_SERVER;
502 follow_load_text(follow_info);
506 /* Handles the ASCII/EBCDIC toggling */
508 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
510 follow_info_t *follow_info = data;
512 if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
513 follow_info->show_type = SHOW_EBCDIC;
514 else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
515 follow_info->show_type = SHOW_HEXDUMP;
516 else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
517 follow_info->show_type = SHOW_CARRAY;
518 else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
519 follow_info->show_type = SHOW_ASCII;
521 g_assert_not_reached();
523 follow_load_text(follow_info);
526 #define FLT_BUF_SIZE 1024
536 follow_read_stream(follow_info_t *follow_info,
537 gboolean (*print_line) (char *, int, gboolean, void *, print_format_e),
538 void *arg, print_format_e format)
542 guint8 client_addr[MAX_IPADDR_LEN];
543 guint16 client_port = 0;
545 guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
549 guint32 server_packet_count = 0;
550 guint32 client_packet_count = 0;
551 char buffer[FLT_BUF_SIZE];
554 iplen = (follow_info->is_ipv6) ? 16 : 4;
556 data_out_file = fopen(follow_info->data_out_filename, "rb");
557 if (data_out_file == NULL) {
558 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
559 "Could not open temporary file %s: %s", follow_info->data_out_filename,
561 return FRS_OPEN_ERROR;
564 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
565 if (client_port == 0) {
566 memcpy(client_addr, sc.src_addr, iplen);
567 client_port = sc.src_port;
570 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
571 client_port == sc.src_port) {
573 global_pos = &global_client_pos;
574 if (follow_info->show_stream == FROM_SERVER) {
580 global_pos = &global_server_pos;
581 if (follow_info->show_stream == FROM_CLIENT) {
586 while (sc.dlen > 0) {
587 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
588 nchars = fread(buffer, 1, bcount, data_out_file);
593 switch (follow_info->show_type) {
596 /* If our native arch is ASCII, call: */
597 EBCDIC_to_ASCII(buffer, nchars);
598 if (!(*print_line) (buffer, nchars, is_server, arg, format))
603 /* If our native arch is EBCDIC, call:
604 * ASCII_TO_EBCDIC(buffer, nchars);
606 if (!(*print_line) (buffer, nchars, is_server, arg, format))
612 while (current_pos < nchars) {
614 gchar hexchars[] = "0123456789abcdef";
617 /* is_server indentation : put 63 spaces at the
618 * beginning of the string */
619 g_snprintf(hexbuf, sizeof(hexbuf),
620 (is_server && follow_info->show_stream == BOTH_HOSTS) ?
623 "%08X ", *global_pos);
624 cur = strlen(hexbuf);
625 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
627 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
629 hexchars[buffer[current_pos + i] & 0x0f];
636 /* Fill it up if column isn't complete */
640 for (j = i; j < 16; j++) {
650 /* Now dump bytes as text */
651 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
653 (isprint((guchar)buffer[current_pos + i]) ?
654 buffer[current_pos + i] : '.' );
661 hexbuf[cur++] = '\n';
663 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg, format))
670 g_snprintf(initbuf, 256, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
671 is_server ? server_packet_count++ : client_packet_count++);
672 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg, format))
674 while (current_pos < nchars) {
676 gchar hexchars[] = "0123456789abcdef";
680 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
681 /* Prepend entries with "0x" */
685 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
687 hexchars[buffer[current_pos + i] & 0x0f];
689 /* Delimit array entries with a comma */
690 if (current_pos + i + 1 < nchars)
696 /* Terminate the array if we are at the end */
697 if (current_pos + i == nchars) {
704 hexbuf[cur++] = '\n';
706 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg, format))
714 if (ferror(data_out_file)) {
715 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
716 "Error reading temporary file %s: %s", follow_info->data_out_filename,
718 fclose(data_out_file);
719 data_out_file = NULL;
720 return FRS_READ_ERROR;
723 fclose(data_out_file);
724 data_out_file = NULL;
728 fclose(data_out_file);
729 data_out_file = NULL;
730 return FRS_PRINT_ERROR;
734 * XXX - for text printing, we probably want to wrap lines at 80 characters;
735 * (PostScript printing is doing this already), and perhaps put some kind of
736 * dingbat (to use the technical term) to indicate a wrapped line, along the
737 * lines of what's done when displaying this in a window, as per Warren Young's
741 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg, print_format_e format)
747 /* convert non printable characters */
748 for (i = 0; i < nchars; i++) {
749 if (buffer[i] == '\n' || buffer[i] == '\r')
751 if (! isprint(buffer[i])) {
756 /* convert unterminated char array to a zero terminated string */
757 str = g_malloc(nchars + 1);
758 memcpy(str, buffer, nchars);
760 print_line(fh, /*indent*/ 0, format, str);
767 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
769 follow_info_t *follow_info = data;
771 /* Lock out user from messing with us. (ie. don't free our data!) */
772 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
774 /* Set the display filter. */
775 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
777 /* Run the display filter so it goes in effect. */
778 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
780 /* we force a subsequent close */
781 window_destroy(follow_info->streamwindow);
787 follow_print_stream(GtkWidget * w _U_, gpointer data)
792 follow_info_t *follow_info = data;
794 gboolean win_printer = FALSE;
798 switch (prefs.pr_dest) {
802 /*XXX should use temp file stuff in util routines */
803 print_dest = g_strdup(tmpnam(NULL));
806 print_dest = prefs.pr_cmd;
811 print_dest = prefs.pr_file;
814 default: /* "Can't happen" */
815 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
816 "Couldn't figure out where to send the print "
817 "job. Check your preferences.");
821 fh = open_print_dest(to_file, print_dest);
824 open_failure_alert_box(prefs.pr_file, errno, TRUE);
826 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
827 "Couldn't run print command %s.", prefs.pr_cmd);
832 print_preamble(fh, prefs.pr_format, cfile.filename);
836 switch (follow_read_stream(follow_info, follow_print_text, fh, prefs.pr_format)) {
841 /* XXX - cancel printing? */
842 close_print_dest(to_file, fh);
844 case FRS_PRINT_ERROR:
848 print_finale(fh, prefs.pr_format);
852 if (!close_print_dest(to_file, fh)) {
854 write_failure_alert_box(prefs.pr_file, errno);
856 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
857 "Error closing print destination.");
862 print_mswin(print_dest);
864 /* trash temp file */
872 write_failure_alert_box(prefs.pr_file, errno);
874 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
875 "Error writing to print command: %s", strerror(errno));
877 /* XXX - cancel printing? */
878 close_print_dest(to_file, fh);
882 /* trash temp file */
889 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
890 void *arg, print_format_e format _U_)
892 GtkWidget *text = arg;
894 #if GTK_MAJOR_VERSION >= 2
895 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
902 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
903 /* While our isprint() hack is in place, we
904 * have to use convert some chars to '.' in order
905 * to be able to see the data we *should* see
906 * in the GtkText widget.
910 for (i = 0; i < nchars; i++) {
911 if (buffer[i] == '\n' || buffer[i] == '\r')
913 if (! isprint(buffer[i])) {
920 color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
921 color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
923 color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
924 color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
926 #if GTK_MAJOR_VERSION < 2
927 gtk_text_insert(GTK_TEXT(text), m_r_font, &fg, &bg, buffer, nchars);
929 gtk_text_buffer_get_end_iter(buf, &iter);
930 tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
931 "background-gdk", &bg, "font-desc",
933 convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
934 gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
942 follow_load_text(follow_info_t *follow_info)
944 #if GTK_MAJOR_VERSION < 2
949 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
952 /* Delete any info already in text box */
953 #if GTK_MAJOR_VERSION < 2
954 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
955 if (bytes_already > 0) {
956 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
957 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
960 /* stop the updates while we fill the text box */
961 gtk_text_freeze(GTK_TEXT(follow_info->text));
963 gtk_text_buffer_set_text(buf, "", -1);
965 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text, PR_FMT_TEXT);
966 #if GTK_MAJOR_VERSION < 2
967 gtk_text_thaw(GTK_TEXT(follow_info->text));
973 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
974 * any, so that if somebody tries to do "Save"
975 * while there's already a "Save TCP Follow Stream" window up, we just pop
976 * up the existing one, rather than creating a new one.
979 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
982 follow_info_t *follow_info = data;
984 if (follow_info->follow_save_as_w != NULL) {
985 /* There's already a dialog box; reactivate it. */
986 reactivate_window(follow_info->follow_save_as_w);
990 new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
991 FILE_SELECTION_SAVE);
992 follow_info->follow_save_as_w = new_win;
994 /* Tuck away the follow_info object into the window */
995 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
997 /* If we've opened a file, start out by showing the files in the directory
998 in which that file resided. */
1000 file_selection_set_current_folder(new_win, last_open_dir);
1002 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
1004 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1005 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
1007 follow_save_as_ok_cb(new_win, new_win);
1009 window_destroy(new_win);
1012 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1013 pointer to the file selection box widget */
1014 SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
1015 "clicked", follow_save_as_ok_cb, new_win);
1017 window_set_cancel_button(new_win,
1018 GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
1020 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
1022 SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
1024 gtk_widget_show_all(new_win);
1025 window_present(new_win);
1031 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
1034 follow_info_t *follow_info;
1038 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1039 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1041 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1044 /* Perhaps the user specified a directory instead of a file.
1045 Check whether they did. */
1046 if (test_for_directory(to_name) == EISDIR) {
1047 /* It's a directory - set the file selection box to display that
1048 directory, and leave the selection box displayed. */
1049 set_last_open_dir(to_name);
1051 file_selection_set_current_folder(fs, last_open_dir);
1055 fh = fopen(to_name, "wb");
1057 open_failure_alert_box(to_name, errno, TRUE);
1062 gtk_widget_hide(GTK_WIDGET(fs));
1063 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1064 window_destroy(GTK_WIDGET(fs));
1066 switch (follow_read_stream(follow_info, follow_print_text, fh, PR_FMT_TEXT)) {
1069 if (fclose(fh) == EOF)
1070 write_failure_alert_box(to_name, errno);
1073 case FRS_OPEN_ERROR:
1074 case FRS_READ_ERROR:
1078 case FRS_PRINT_ERROR:
1079 write_failure_alert_box(to_name, errno);
1084 /* Save the directory name for future file dialogs. */
1085 dirname = get_dirname(to_name); /* Overwrites to_name */
1086 set_last_open_dir(dirname);
1091 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1093 follow_info_t *follow_info = data;
1095 /* Note that we no longer have a dialog box. */
1096 follow_info->follow_save_as_w = NULL;