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 */
48 #include "follow_dlg.h"
50 #include "dlg_utils.h"
54 #include "alert_box.h"
55 #include "simple_dialog.h"
56 #include <epan/dissectors/packet-ipv6.h>
58 #include <epan/addr_resolv.h>
61 #include <epan/epan_dissect.h>
62 #include <epan/filesystem.h>
63 #include "compat_macros.h"
66 #include "print_mswin.h"
67 #include "font_utils.h"
85 show_stream_t show_stream;
86 show_type_t show_type;
87 char data_out_filename[128 + 1];
91 GtkWidget *hexdump_bt;
93 GtkWidget *follow_save_as_w;
95 char *filter_out_filter;
97 GtkWidget *streamwindow;
100 static void follow_destroy_cb(GtkWidget * win, gpointer data);
101 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
102 static void follow_load_text(follow_info_t *follow_info);
103 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
104 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
105 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
106 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
107 static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
108 static void follow_stream_om_both(GtkWidget * w, gpointer data);
109 static void follow_stream_om_client(GtkWidget * w, gpointer data);
110 static void follow_stream_om_server(GtkWidget * w, gpointer data);
113 /* With MSVC and a libethereal.dll, we need a special declaration. */
114 ETH_VAR_IMPORT FILE *data_out_file;
116 #define E_FOLLOW_INFO_KEY "follow_info_key"
118 /* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
119 so we can redraw them all if the colors or font changes. */
120 static GList *follow_infos;
122 /* Add a "follow_info_t" structure to the list. */
124 remember_follow_info(follow_info_t *follow_info)
126 follow_infos = g_list_append(follow_infos, follow_info);
129 /* Remove a "follow_info_t" structure from the list. */
131 forget_follow_info(follow_info_t *follow_info)
133 follow_infos = g_list_remove(follow_infos, follow_info);
137 follow_redraw(gpointer data, gpointer user_data _U_)
139 follow_load_text((follow_info_t *)data);
142 /* Redraw the text in all "Follow TCP Stream" windows. */
144 follow_redraw_all(void)
146 g_list_foreach(follow_infos, follow_redraw, NULL);
149 /* Follow the TCP stream, if any, to which the last packet that we called
150 a dissection routine on belongs (this might be the most recently
151 selected packet, or it might be the last packet in the file). */
153 follow_stream_cb(GtkWidget * w, gpointer data _U_)
155 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
156 GtkWidget *hbox, *button_hbox, *button, *radio_bt;
157 GtkWidget *stream_fr, *stream_vb;
158 GtkWidget *stream_om, *stream_menu, *stream_mi;
159 GtkTooltips *tooltips;
161 gchar *follow_filter;
162 const gchar *previous_filter;
163 int filter_out_filter_len;
164 const char *hostname0, *hostname1;
167 follow_tcp_stats_t stats;
168 follow_info_t *follow_info;
170 /* we got tcp so we can follow */
171 if (cfile.edt->pi.ipproto != 6) {
172 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
173 "Error following stream. Please make\n"
174 "sure you have a TCP packet selected.");
178 follow_info = g_new0(follow_info_t, 1);
180 /* Create a temporary file into which to dump the reassembled data
181 from the TCP stream, and set "data_out_file" to refer to it, so
182 that the TCP code will write to it.
184 XXX - it might be nicer to just have the TCP code directly
185 append stuff to the text widget for the TCP stream window,
186 if we can arrange that said window not pop up until we're
188 tmp_fd = create_tempfile(follow_info->data_out_filename,
189 sizeof follow_info->data_out_filename, "follow");
192 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
193 "Could not create temporary file %s: %s",
194 follow_info->data_out_filename, strerror(errno));
199 data_out_file = fdopen(tmp_fd, "wb");
200 if (data_out_file == NULL) {
201 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
202 "Could not create temporary file %s: %s",
203 follow_info->data_out_filename, strerror(errno));
205 unlink(follow_info->data_out_filename);
210 /* Create a new filter that matches all packets in the TCP stream,
211 and set the display filter entry accordingly */
212 reset_tcp_reassembly();
213 follow_filter = build_follow_filter(&cfile.edt->pi);
215 /* Set the display filter entry accordingly */
216 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
218 /* needed in follow_filter_out_stream(), is there a better way? */
219 follow_info->filter_te = filter_te;
221 /* save previous filter, const since we're not supposed to alter */
223 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
225 /* allocate our new filter. API claims g_malloc terminates program on failure */
226 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
227 filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
228 follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
230 /* append the negation */
231 if(strlen(previous_filter)) {
232 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
233 "%s and !(%s)", previous_filter, follow_filter);
235 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
236 "!(%s)", follow_filter);
240 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
242 /* Run the display filter so it goes in effect - even if it's the
243 same as the previous display filter. */
244 main_filter_packets(&cfile, follow_filter, TRUE);
246 /* Free the filter string, as we're done with it. */
247 g_free(follow_filter);
249 /* The data_out_file should now be full of the streams information */
250 fclose(data_out_file);
252 /* The data_out_filename file now has all the text that was in the session */
253 streamwindow = dlg_window_new("Follow TCP stream");
255 /* needed in follow_filter_out_stream(), is there a better way? */
256 follow_info->streamwindow = streamwindow;
258 gtk_widget_set_name(streamwindow, "TCP stream window");
259 gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
260 gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
262 /* setup the container */
263 tooltips = gtk_tooltips_new ();
265 vbox = gtk_vbox_new(FALSE, 6);
266 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
269 if (incomplete_tcp_stream) {
270 stream_fr = gtk_frame_new("Stream Content (incomplete)");
272 stream_fr = gtk_frame_new("Stream Content");
274 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
275 gtk_widget_show(stream_fr);
277 stream_vb = gtk_vbox_new(FALSE, 6);
278 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
279 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
281 /* create a scrolled window for the text */
282 txt_scrollw = scrolled_window_new(NULL, NULL);
283 #if GTK_MAJOR_VERSION >= 2
284 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
287 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
289 /* create a text box */
290 #if GTK_MAJOR_VERSION < 2
291 text = gtk_text_new(NULL, NULL);
292 gtk_text_set_editable(GTK_TEXT(text), FALSE);
294 text = gtk_text_view_new();
295 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
297 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
298 follow_info->text = text;
302 hbox = gtk_hbox_new(FALSE, 1);
303 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
305 /* Create Save As Button */
306 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
307 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
308 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
309 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
311 /* Create Print Button */
312 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
313 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
314 gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
315 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
318 follow_tcp_stats(&stats);
321 struct e_in6_addr ipaddr;
322 memcpy(&ipaddr, stats.ip_address[0], 16);
323 hostname0 = get_hostname6(&ipaddr);
324 memcpy(&ipaddr, stats.ip_address[0], 16);
325 hostname1 = get_hostname6(&ipaddr);
328 memcpy(&ipaddr, stats.ip_address[0], 4);
329 hostname0 = get_hostname(ipaddr);
330 memcpy(&ipaddr, stats.ip_address[1], 4);
331 hostname1 = get_hostname(ipaddr);
334 port0 = get_tcp_port(stats.tcp_port[0]);
335 port1 = get_tcp_port(stats.tcp_port[1]);
337 follow_info->is_ipv6 = stats.is_ipv6;
339 stream_om = gtk_option_menu_new();
340 stream_menu = gtk_menu_new();
343 g_snprintf(string, sizeof(string),
344 "Entire conversation (%u bytes)",
345 stats.bytes_written[0] + stats.bytes_written[1]);
346 stream_mi = gtk_menu_item_new_with_label(string);
347 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
349 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
350 gtk_widget_show(stream_mi);
351 follow_info->show_stream = BOTH_HOSTS;
353 /* Host 0 --> Host 1 */
354 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
355 hostname0, port0, hostname1, port1,
356 stats.bytes_written[0]);
357 stream_mi = gtk_menu_item_new_with_label(string);
358 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
360 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
361 gtk_widget_show(stream_mi);
363 /* Host 1 --> Host 0 */
364 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
365 hostname1, port1, hostname0, port0,
366 stats.bytes_written[1]);
367 stream_mi = gtk_menu_item_new_with_label(string);
368 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
370 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
371 gtk_widget_show(stream_mi);
373 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
374 /* Set history to 0th item, i.e., the first item. */
375 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
376 gtk_tooltips_set_tip (tooltips, stream_om,
377 "Select the stream direction to display", NULL);
378 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
380 /* ASCII radio button */
381 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
382 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
383 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
384 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
386 follow_info->ascii_bt = radio_bt;
387 follow_info->show_type = SHOW_ASCII;
389 /* EBCDIC radio button */
390 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
391 (GTK_RADIO_BUTTON(radio_bt)),
393 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
394 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
395 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
397 follow_info->ebcdic_bt = radio_bt;
399 /* HEX DUMP radio button */
400 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
401 (GTK_RADIO_BUTTON(radio_bt)),
403 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
404 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
405 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
407 follow_info->hexdump_bt = radio_bt;
409 /* C Array radio button */
410 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
411 (GTK_RADIO_BUTTON(radio_bt)),
413 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
414 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
415 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
417 follow_info->carray_bt = radio_bt;
420 button_hbox = gtk_hbutton_box_new();
421 gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
422 gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
423 gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
425 /* Create exclude stream button */
426 button = gtk_button_new_with_label("Filter out this stream");
427 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
428 gtk_tooltips_set_tip (tooltips, button,
429 "Build a display filter which cuts this stream from the capture", NULL);
430 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
432 /* Create Close Button */
433 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
434 gtk_tooltips_set_tip (tooltips, button,
435 "Close the dialog and keep the current display filter", NULL);
436 gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
437 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
439 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
441 /* Tuck away the follow_info object into the window */
442 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
444 follow_load_text(follow_info);
445 remember_follow_info(follow_info);
447 data_out_file = NULL;
449 SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
450 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
452 /* Make sure this widget gets destroyed if we quit the main loop,
453 so that if we exit, we clean up any temporary files we have
454 for "Follow TCP Stream" windows. */
455 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
457 gtk_widget_show_all(streamwindow);
458 window_present(streamwindow);
461 /* The destroy call back has the responsibility of
462 * unlinking the temporary file
463 * and freeing the filter_out_filter */
465 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
467 follow_info_t *follow_info;
470 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
471 i = unlink(follow_info->data_out_filename);
473 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
474 follow_info->data_out_filename, strerror(errno), errno);
476 g_free(follow_info->filter_out_filter);
477 forget_follow_info(follow_info);
481 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
482 * 3 different functions here? */
484 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
486 follow_info_t *follow_info = data;
487 follow_info->show_stream = BOTH_HOSTS;
488 follow_load_text(follow_info);
492 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
494 follow_info_t *follow_info = data;
495 follow_info->show_stream = FROM_CLIENT;
496 follow_load_text(follow_info);
500 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
502 follow_info_t *follow_info = data;
503 follow_info->show_stream = FROM_SERVER;
504 follow_load_text(follow_info);
508 /* Handles the ASCII/EBCDIC toggling */
510 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
512 follow_info_t *follow_info = data;
514 if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
515 follow_info->show_type = SHOW_EBCDIC;
516 else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
517 follow_info->show_type = SHOW_HEXDUMP;
518 else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
519 follow_info->show_type = SHOW_CARRAY;
520 else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
521 follow_info->show_type = SHOW_ASCII;
523 g_assert_not_reached();
525 follow_load_text(follow_info);
528 #define FLT_BUF_SIZE 1024
538 follow_read_stream(follow_info_t *follow_info,
539 gboolean (*print_line) (char *, int, gboolean, void *),
544 guint8 client_addr[MAX_IPADDR_LEN];
545 guint16 client_port = 0;
547 guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
551 guint32 server_packet_count = 0;
552 guint32 client_packet_count = 0;
553 char buffer[FLT_BUF_SIZE];
556 iplen = (follow_info->is_ipv6) ? 16 : 4;
558 data_out_file = fopen(follow_info->data_out_filename, "rb");
559 if (data_out_file == NULL) {
560 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
561 "Could not open temporary file %s: %s", follow_info->data_out_filename,
563 return FRS_OPEN_ERROR;
566 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
567 if (client_port == 0) {
568 memcpy(client_addr, sc.src_addr, iplen);
569 client_port = sc.src_port;
572 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
573 client_port == sc.src_port) {
575 global_pos = &global_client_pos;
576 if (follow_info->show_stream == FROM_SERVER) {
582 global_pos = &global_server_pos;
583 if (follow_info->show_stream == FROM_CLIENT) {
588 while (sc.dlen > 0) {
589 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
590 nchars = fread(buffer, 1, bcount, data_out_file);
595 switch (follow_info->show_type) {
598 /* If our native arch is ASCII, call: */
599 EBCDIC_to_ASCII(buffer, nchars);
600 if (!(*print_line) (buffer, nchars, is_server, arg))
605 /* If our native arch is EBCDIC, call:
606 * ASCII_TO_EBCDIC(buffer, nchars);
608 if (!(*print_line) (buffer, nchars, is_server, arg))
614 while (current_pos < nchars) {
616 gchar hexchars[] = "0123456789abcdef";
619 /* is_server indentation : put 63 spaces at the
620 * beginning of the string */
621 g_snprintf(hexbuf, sizeof(hexbuf),
622 (is_server && follow_info->show_stream == BOTH_HOSTS) ?
625 "%08X ", *global_pos);
626 cur = strlen(hexbuf);
627 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
629 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
631 hexchars[buffer[current_pos + i] & 0x0f];
638 /* Fill it up if column isn't complete */
642 for (j = i; j < 16; j++) {
652 /* Now dump bytes as text */
653 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
655 (isprint((guchar)buffer[current_pos + i]) ?
656 buffer[current_pos + i] : '.' );
663 hexbuf[cur++] = '\n';
665 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
672 g_snprintf(initbuf, 256, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
673 is_server ? server_packet_count++ : client_packet_count++);
674 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
676 while (current_pos < nchars) {
678 gchar hexchars[] = "0123456789abcdef";
682 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
683 /* Prepend entries with "0x" */
687 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
689 hexchars[buffer[current_pos + i] & 0x0f];
691 /* Delimit array entries with a comma */
692 if (current_pos + i + 1 < nchars)
698 /* Terminate the array if we are at the end */
699 if (current_pos + i == nchars) {
706 hexbuf[cur++] = '\n';
708 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
716 if (ferror(data_out_file)) {
717 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
718 "Error reading temporary file %s: %s", follow_info->data_out_filename,
720 fclose(data_out_file);
721 data_out_file = NULL;
722 return FRS_READ_ERROR;
725 fclose(data_out_file);
726 data_out_file = NULL;
730 fclose(data_out_file);
731 data_out_file = NULL;
732 return FRS_PRINT_ERROR;
736 * XXX - for text printing, we probably want to wrap lines at 80 characters;
737 * (PostScript printing is doing this already), and perhaps put some kind of
738 * dingbat (to use the technical term) to indicate a wrapped line, along the
739 * lines of what's done when displaying this in a window, as per Warren Young's
743 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg)
745 print_stream_t *stream = arg;
749 /* convert non printable characters */
750 for (i = 0; i < nchars; i++) {
751 if (buffer[i] == '\n' || buffer[i] == '\r')
753 if (! isprint(buffer[i])) {
758 /* convert unterminated char array to a zero terminated string */
759 str = g_malloc(nchars + 1);
760 memcpy(str, buffer, nchars);
762 print_line(stream, /*indent*/ 0, str);
769 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
771 follow_info_t *follow_info = data;
773 /* Lock out user from messing with us. (ie. don't free our data!) */
774 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
776 /* Set the display filter. */
777 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
779 /* Run the display filter so it goes in effect. */
780 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
782 /* we force a subsequent close */
783 window_destroy(follow_info->streamwindow);
789 follow_print_stream(GtkWidget * w _U_, gpointer data)
791 print_stream_t *stream;
794 follow_info_t *follow_info = data;
796 gboolean win_printer = FALSE;
799 switch (prefs.pr_dest) {
803 /*XXX should use temp file stuff in util routines */
804 print_dest = g_strdup(tmpnam(NULL));
807 print_dest = prefs.pr_cmd;
812 print_dest = prefs.pr_file;
815 default: /* "Can't happen" */
816 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
817 "Couldn't figure out where to send the print "
818 "job. Check your preferences.");
822 switch (prefs.pr_format) {
825 stream = print_stream_text_new(to_file, print_dest);
829 stream = print_stream_ps_new(to_file, print_dest);
833 g_assert_not_reached();
836 if (stream == NULL) {
838 open_failure_alert_box(prefs.pr_file, errno, TRUE);
840 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
841 "Couldn't run print command %s.", prefs.pr_cmd);
846 if (!print_preamble(stream, cfile.filename))
849 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
854 /* XXX - cancel printing? */
855 destroy_print_stream(stream);
857 case FRS_PRINT_ERROR:
861 if (!print_finale(stream))
864 if (!destroy_print_stream(stream)) {
866 write_failure_alert_box(prefs.pr_file, errno);
868 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
869 "Error closing print destination.");
874 print_mswin(print_dest);
876 /* trash temp file */
884 write_failure_alert_box(prefs.pr_file, errno);
886 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
887 "Error writing to print command: %s", strerror(errno));
889 /* XXX - cancel printing? */
890 destroy_print_stream(stream);
894 /* trash temp file */
901 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
904 GtkWidget *text = arg;
906 #if GTK_MAJOR_VERSION >= 2
907 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
914 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
915 /* While our isprint() hack is in place, we
916 * have to use convert some chars to '.' in order
917 * to be able to see the data we *should* see
918 * in the GtkText widget.
922 for (i = 0; i < nchars; i++) {
923 if (buffer[i] == '\n' || buffer[i] == '\r')
925 if (! isprint(buffer[i])) {
932 color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
933 color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
935 color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
936 color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
938 #if GTK_MAJOR_VERSION < 2
939 gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &fg, &bg, buffer, nchars);
941 gtk_text_buffer_get_end_iter(buf, &iter);
942 tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
943 "background-gdk", &bg, "font-desc",
944 user_font_get_regular(), NULL);
945 convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
946 gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
954 follow_load_text(follow_info_t *follow_info)
956 #if GTK_MAJOR_VERSION < 2
961 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
964 /* Delete any info already in text box */
965 #if GTK_MAJOR_VERSION < 2
966 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
967 if (bytes_already > 0) {
968 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
969 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
972 /* stop the updates while we fill the text box */
973 gtk_text_freeze(GTK_TEXT(follow_info->text));
975 gtk_text_buffer_set_text(buf, "", -1);
977 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
978 #if GTK_MAJOR_VERSION < 2
979 gtk_text_thaw(GTK_TEXT(follow_info->text));
985 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
986 * any, so that if somebody tries to do "Save"
987 * while there's already a "Save TCP Follow Stream" window up, we just pop
988 * up the existing one, rather than creating a new one.
991 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
994 follow_info_t *follow_info = data;
996 if (follow_info->follow_save_as_w != NULL) {
997 /* There's already a dialog box; reactivate it. */
998 reactivate_window(follow_info->follow_save_as_w);
1002 new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
1003 FILE_SELECTION_SAVE);
1004 follow_info->follow_save_as_w = new_win;
1006 /* Tuck away the follow_info object into the window */
1007 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
1009 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
1011 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1012 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
1014 follow_save_as_ok_cb(new_win, new_win);
1016 window_destroy(new_win);
1019 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1020 pointer to the file selection box widget */
1021 SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
1022 "clicked", follow_save_as_ok_cb, new_win);
1024 window_set_cancel_button(new_win,
1025 GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
1027 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
1029 SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
1031 gtk_widget_show_all(new_win);
1032 window_present(new_win);
1038 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
1041 follow_info_t *follow_info;
1043 print_stream_t *stream;
1046 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1047 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1049 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1052 /* Perhaps the user specified a directory instead of a file.
1053 Check whether they did. */
1054 if (test_for_directory(to_name) == EISDIR) {
1055 /* It's a directory - set the file selection box to display that
1056 directory, and leave the selection box displayed. */
1057 set_last_open_dir(to_name);
1059 file_selection_set_current_folder(fs, get_last_open_dir());
1063 fh = fopen(to_name, "wb");
1065 open_failure_alert_box(to_name, errno, TRUE);
1069 stream = print_stream_text_stdio_new(fh);
1071 gtk_widget_hide(GTK_WIDGET(fs));
1072 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1073 window_destroy(GTK_WIDGET(fs));
1075 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
1078 if (!destroy_print_stream(stream))
1079 write_failure_alert_box(to_name, errno);
1082 case FRS_OPEN_ERROR:
1083 case FRS_READ_ERROR:
1084 destroy_print_stream(stream);
1087 case FRS_PRINT_ERROR:
1088 write_failure_alert_box(to_name, errno);
1089 destroy_print_stream(stream);
1093 /* Save the directory name for future file dialogs. */
1094 dirname = get_dirname(to_name); /* Overwrites to_name */
1095 set_last_open_dir(dirname);
1100 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1102 follow_info_t *follow_info = data;
1104 /* Note that we no longer have a dialog box. */
1105 follow_info->follow_save_as_w = NULL;