3 * $Id: follow_dlg.c,v 1.35 2004/01/24 02:01:44 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 */
41 #ifdef NEED_SNPRINTF_H
42 # include "snprintf.h"
48 #include "color_utils.h"
50 #include "follow_dlg.h"
52 #include "dlg_utils.h"
55 #include "gtkglobals.h"
57 #include "simple_dialog.h"
58 #include "packet-ipv6.h"
60 #include <epan/resolv.h>
63 #include <epan/epan_dissect.h>
64 #include <epan/filesystem.h>
65 #include "compat_macros.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, GtkFileSelection * 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 extern 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, *radio_bt;
155 GtkWidget *stream_om, *stream_menu, *stream_mi;
157 gchar *follow_filter;
158 const gchar *previous_filter;
159 const char *hostname0, *hostname1;
162 follow_tcp_stats_t stats;
163 follow_info_t *follow_info;
165 /* we got tcp so we can follow */
166 if (cfile.edt->pi.ipproto != 6) {
167 simple_dialog(ESD_TYPE_CRIT, NULL,
168 "Error following stream. Please make\n"
169 "sure you have a TCP packet selected.");
173 follow_info = g_new0(follow_info_t, 1);
175 /* Create a temporary file into which to dump the reassembled data
176 from the TCP stream, and set "data_out_file" to refer to it, so
177 that the TCP code will write to it.
179 XXX - it might be nicer to just have the TCP code directly
180 append stuff to the text widget for the TCP stream window,
181 if we can arrange that said window not pop up until we're
183 tmp_fd = create_tempfile(follow_info->data_out_filename,
184 sizeof follow_info->data_out_filename, "follow");
187 simple_dialog(ESD_TYPE_WARN, NULL,
188 "Could not create temporary file %s: %s",
189 follow_info->data_out_filename, strerror(errno));
194 data_out_file = fdopen(tmp_fd, "wb");
195 if (data_out_file == NULL) {
196 simple_dialog(ESD_TYPE_WARN, NULL,
197 "Could not create temporary file %s: %s",
198 follow_info->data_out_filename, strerror(errno));
200 unlink(follow_info->data_out_filename);
205 /* Create a new filter that matches all packets in the TCP stream,
206 and set the display filter entry accordingly */
207 reset_tcp_reassembly();
208 follow_filter = build_follow_filter(&cfile.edt->pi);
210 /* Set the display filter entry accordingly */
211 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
213 /* needed in follow_filter_out_stream(), is there a better way? */
214 follow_info->filter_te = filter_te;
216 /* save previous filter, const since we're not supposed to alter */
218 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
220 /* allocate our new filter. API claims g_malloc terminates program on failure */
221 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
222 follow_info->filter_out_filter =
223 (gchar *)g_malloc(strlen(follow_filter) + strlen(previous_filter) + 16);
225 /* append the negation */
226 if(strlen(previous_filter)) {
227 sprintf(follow_info->filter_out_filter, "%s \nand !(%s)", previous_filter, follow_filter);
229 sprintf(follow_info->filter_out_filter, "!(%s)", follow_filter);
232 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
234 /* Run the display filter so it goes in effect. */
235 filter_packets(&cfile, follow_filter);
237 /* Free the filter string, as we're done with it. */
238 g_free(follow_filter);
240 /* The data_out_file should now be full of the streams information */
241 fclose(data_out_file);
243 /* The data_out_filename file now has all the text that was in the session */
244 streamwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
246 /* needed in follow_filter_out_stream(), is there a better way? */
247 follow_info->streamwindow = streamwindow;
249 gtk_widget_set_name(streamwindow, "TCP stream window");
251 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
252 SIGNAL_CONNECT(streamwindow, "realize", window_icon_realize_cb, NULL);
253 if (incomplete_tcp_stream) {
254 gtk_window_set_title(GTK_WINDOW(streamwindow),
255 "Contents of TCP stream (incomplete)");
257 gtk_window_set_title(GTK_WINDOW(streamwindow),
258 "Contents of TCP stream");
260 WIDGET_SET_SIZE(streamwindow, DEF_WIDTH, DEF_HEIGHT);
261 gtk_container_border_width(GTK_CONTAINER(streamwindow), 2);
263 /* setup the container */
264 vbox = gtk_vbox_new(FALSE, 0);
265 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
267 /* create a scrolled window for the text */
268 txt_scrollw = scrolled_window_new(NULL, NULL);
269 gtk_box_pack_start(GTK_BOX(vbox), txt_scrollw, TRUE, TRUE, 0);
270 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
271 GTK_POLICY_AUTOMATIC,
272 GTK_POLICY_AUTOMATIC);
274 /* create a text box */
275 #if GTK_MAJOR_VERSION < 2
276 text = gtk_text_new(NULL, NULL);
277 gtk_text_set_editable(GTK_TEXT(text), FALSE);
279 text = gtk_text_view_new();
280 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
282 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
283 follow_info->text = text;
286 hbox = gtk_hbox_new(FALSE, 1);
287 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
291 follow_tcp_stats(&stats);
294 struct e_in6_addr ipaddr;
295 memcpy(&ipaddr, stats.ip_address[0], 16);
296 hostname0 = get_hostname6(&ipaddr);
297 memcpy(&ipaddr, stats.ip_address[0], 16);
298 hostname1 = get_hostname6(&ipaddr);
301 memcpy(&ipaddr, stats.ip_address[0], 4);
302 hostname0 = get_hostname(ipaddr);
303 memcpy(&ipaddr, stats.ip_address[1], 4);
304 hostname1 = get_hostname(ipaddr);
307 port0 = get_tcp_port(stats.tcp_port[0]);
308 port1 = get_tcp_port(stats.tcp_port[1]);
310 follow_info->is_ipv6 = stats.is_ipv6;
312 stream_om = gtk_option_menu_new();
313 stream_menu = gtk_menu_new();
316 snprintf(string, sizeof(string),
317 "Entire conversation (%u bytes)",
318 stats.bytes_written[0] + stats.bytes_written[1]);
319 stream_mi = gtk_menu_item_new_with_label(string);
320 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
322 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
323 gtk_widget_show(stream_mi);
324 follow_info->show_stream = BOTH_HOSTS;
326 /* Host 0 --> Host 1 */
327 snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
328 hostname0, port0, hostname1, port1,
329 stats.bytes_written[0]);
330 stream_mi = gtk_menu_item_new_with_label(string);
331 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
333 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
334 gtk_widget_show(stream_mi);
336 /* Host 1 --> Host 0 */
337 snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
338 hostname1, port1, hostname0, port0,
339 stats.bytes_written[1]);
340 stream_mi = gtk_menu_item_new_with_label(string);
341 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
343 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
344 gtk_widget_show(stream_mi);
346 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
347 /* Set history to 0th item, i.e., the first item. */
348 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
349 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
351 /* ASCII radio button */
352 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
353 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
354 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
355 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
357 follow_info->ascii_bt = radio_bt;
358 follow_info->show_type = SHOW_ASCII;
360 /* EBCDIC radio button */
361 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
362 (GTK_RADIO_BUTTON(radio_bt)),
364 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
365 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
366 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
368 follow_info->ebcdic_bt = radio_bt;
370 /* HEX DUMP radio button */
371 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
372 (GTK_RADIO_BUTTON(radio_bt)),
374 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
375 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
376 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
378 follow_info->hexdump_bt = radio_bt;
380 /* C Array radio button */
381 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
382 (GTK_RADIO_BUTTON(radio_bt)),
384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
385 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
386 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
388 follow_info->carray_bt = radio_bt;
390 /* Create Close Button */
391 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
392 SIGNAL_CONNECT_OBJECT(button, "clicked", gtk_widget_destroy,
394 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
396 /* Catch the "key_press_event" signal in the window, so that we can catch
397 the ESC key being pressed and act as if the "Cancel" button had
399 dlg_set_cancel(streamwindow, button);
401 /* Create exclude stream button */
402 button = gtk_button_new_with_label("Filter out this stream");
403 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
405 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
407 /* Create Save As Button */
408 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
409 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
410 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
412 /* Create Print Button */
413 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
414 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
415 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
418 /* Tuck away the follow_info object into the window */
419 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
421 follow_load_text(follow_info);
422 remember_follow_info(follow_info);
424 data_out_file = NULL;
426 /* Make sure this widget gets destroyed if we quit the main loop,
427 so that if we exit, we clean up any temporary files we have
428 for "Follow TCP Stream" windows. */
429 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
430 gtk_widget_show_all(streamwindow);
433 /* The destroy call back has the responsibility of
434 * unlinking the temporary file
435 * and freeing the filter_out_filter */
437 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
439 follow_info_t *follow_info;
441 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
442 unlink(follow_info->data_out_filename);
443 g_free(follow_info->filter_out_filter);
444 gtk_widget_destroy(w);
445 forget_follow_info(follow_info);
449 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
450 * 3 different functions here? */
452 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
454 follow_info_t *follow_info = data;
455 follow_info->show_stream = BOTH_HOSTS;
456 follow_load_text(follow_info);
460 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
462 follow_info_t *follow_info = data;
463 follow_info->show_stream = FROM_CLIENT;
464 follow_load_text(follow_info);
468 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
470 follow_info_t *follow_info = data;
471 follow_info->show_stream = FROM_SERVER;
472 follow_load_text(follow_info);
476 /* Handles the ASCII/EBCDIC toggling */
478 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
480 follow_info_t *follow_info = data;
482 if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
483 follow_info->show_type = SHOW_EBCDIC;
484 else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
485 follow_info->show_type = SHOW_HEXDUMP;
486 else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
487 follow_info->show_type = SHOW_CARRAY;
488 else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
489 follow_info->show_type = SHOW_ASCII;
491 g_assert_not_reached();
493 follow_load_text(follow_info);
496 #define FLT_BUF_SIZE 1024
498 follow_read_stream(follow_info_t *follow_info,
499 void (*print_line) (char *, int, gboolean, void *),
504 guint8 client_addr[MAX_IPADDR_LEN];
505 guint16 client_port = 0;
507 guint16 current_pos, global_client_pos = 0, global_server_pos = 0;
511 guint32 server_packet_count = 0;
512 guint32 client_packet_count = 0;
514 iplen = (follow_info->is_ipv6) ? 16 : 4;
516 data_out_file = fopen(follow_info->data_out_filename, "rb");
518 char buffer[FLT_BUF_SIZE];
520 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
521 if (client_port == 0) {
522 memcpy(client_addr, sc.src_addr, iplen);
523 client_port = sc.src_port;
526 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
527 client_port == sc.src_port) {
529 global_pos = &global_client_pos;
530 if (follow_info->show_stream == FROM_SERVER) {
536 global_pos = &global_server_pos;
537 if (follow_info->show_stream == FROM_CLIENT) {
542 while (sc.dlen > 0) {
543 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
544 nchars = fread(buffer, 1, bcount, data_out_file);
549 switch (follow_info->show_type) {
551 /* If our native arch is ASCII, call: */
552 EBCDIC_to_ASCII(buffer, nchars);
553 (*print_line) (buffer, nchars, is_server, arg);
556 /* If our native arch is EBCDIC, call:
557 * ASCII_TO_EBCDIC(buffer, nchars);
559 (*print_line) (buffer, nchars, is_server, arg);
563 while (current_pos < nchars) {
565 gchar hexchars[] = "0123456789abcdef";
567 /* is_server indentation : put 63 spaces at the begenning
569 sprintf(hexbuf, (is_server &&
570 follow_info->show_stream == BOTH_HOSTS) ?
573 "%08X ", *global_pos);
574 cur = strlen(hexbuf);
575 for (i = 0; i < 16 && current_pos + i < nchars;
578 hexchars[(buffer[current_pos + i] & 0xf0)
581 hexchars[buffer[current_pos + i] & 0x0f];
588 /* Fill it up if column isn't complete */
592 for (j = i; j < 16; j++) {
602 /* Now dump bytes as text */
603 for (i = 0; i < 16 && current_pos + i < nchars;
606 (isprint((guchar)buffer[current_pos + i]) ?
607 buffer[current_pos + i] : '.' );
614 hexbuf[cur++] = '\n';
616 (*print_line) (hexbuf, strlen(hexbuf), is_server, arg);
621 sprintf(initbuf, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
622 is_server ? server_packet_count++ : client_packet_count++);
623 (*print_line) (initbuf, strlen(initbuf), is_server, arg);
624 while (current_pos < nchars) {
626 gchar hexchars[] = "0123456789abcdef";
630 for (i = 0; i < 8 && current_pos + i < nchars;
632 /* Prepend entries with "0x" */
636 hexchars[(buffer[current_pos + i] & 0xf0)
639 hexchars[buffer[current_pos + i] & 0x0f];
641 /* Delimit array entries with a comma */
642 if (current_pos + i + 1 < nchars)
648 /* Terminate the array if we are at the end */
649 if (current_pos + i == nchars) {
656 hexbuf[cur++] = '\n';
658 (*print_line) (hexbuf, strlen(hexbuf), is_server, arg);
665 if (ferror(data_out_file)) {
666 simple_dialog(ESD_TYPE_WARN, NULL,
667 "Error reading temporary file %s: %s", follow_info->data_out_filename,
670 fclose(data_out_file);
671 data_out_file = NULL;
673 simple_dialog(ESD_TYPE_WARN, NULL,
674 "Could not open temporary file %s: %s", follow_info->data_out_filename,
680 * XXX - for text printing, we probably want to wrap lines at 80 characters;
681 * for PostScript printing, we probably want to wrap them at the appropriate
682 * width, and perhaps put some kind of dingbat (to use the technical term)
683 * to indicate a wrapped line, along the lines of what's done when displaying
684 * this in a window, as per Warren Young's suggestion.
686 * For now, we support only text printing.
689 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg)
693 fwrite(buffer, nchars, 1, fh);
697 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
699 follow_info_t *follow_info = data;
701 /* Lock out user from messing with us. (ie. don't free our data!) */
702 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
704 /* Set the display filter. */
705 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
707 /* Run the display filter so it goes in effect. */
708 filter_packets(&cfile, follow_info->filter_out_filter);
710 /* we force a subsequent close */
711 gtk_widget_destroy(follow_info->streamwindow);
717 follow_print_stream(GtkWidget * w _U_, gpointer data)
722 follow_info_t *follow_info = data;
724 switch (prefs.pr_dest) {
726 print_dest = prefs.pr_cmd;
731 print_dest = prefs.pr_file;
734 default: /* "Can't happen" */
735 simple_dialog(ESD_TYPE_CRIT, NULL,
736 "Couldn't figure out where to send the print "
737 "job. Check your preferences.");
741 fh = open_print_dest(to_file, print_dest);
745 simple_dialog(ESD_TYPE_WARN, NULL,
746 "Couldn't run print command %s.", prefs.pr_cmd);
750 simple_dialog(ESD_TYPE_WARN, NULL,
751 file_write_error_message(errno), prefs.pr_file);
757 print_preamble(fh, PR_FMT_TEXT);
758 follow_read_stream(follow_info, follow_print_text, fh);
759 print_finale(fh, PR_FMT_TEXT);
760 close_print_dest(to_file, fh);
764 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
767 GtkWidget *text = arg;
769 #if GTK_MAJOR_VERSION >= 2
770 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
777 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
778 /* While our isprint() hack is in place, we
779 * have to use convert some chars to '.' in order
780 * to be able to see the data we *should* see
781 * in the GtkText widget.
785 for (i = 0; i < nchars; i++) {
786 if (buffer[i] == '\n' || buffer[i] == '\r')
788 if (! isprint(buffer[i])) {
795 color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
796 color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
798 color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
799 color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
801 #if GTK_MAJOR_VERSION < 2
802 gtk_text_insert(GTK_TEXT(text), m_r_font, &fg, &bg, buffer, nchars);
804 gtk_text_buffer_get_end_iter(buf, &iter);
805 tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
806 "background-gdk", &bg, "font-desc",
808 convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
809 gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
816 follow_load_text(follow_info_t *follow_info)
818 #if GTK_MAJOR_VERSION < 2
823 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
826 /* Delete any info already in text box */
827 #if GTK_MAJOR_VERSION < 2
828 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
829 if (bytes_already > 0) {
830 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
831 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
834 /* stop the updates while we fill the text box */
835 gtk_text_freeze(GTK_TEXT(follow_info->text));
837 gtk_text_buffer_set_text(buf, "", -1);
839 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
840 #if GTK_MAJOR_VERSION < 2
841 gtk_text_thaw(GTK_TEXT(follow_info->text));
847 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
848 * any, so that if somebody tries to do "Save"
849 * while there's already a "Save TCP Follow Stream" window up, we just pop
850 * up the existing one, rather than creating a new one.
853 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
855 GtkWidget *ok_bt, *new_win;
856 follow_info_t *follow_info = data;
858 if (follow_info->follow_save_as_w != NULL) {
859 /* There's already a dialog box; reactivate it. */
860 reactivate_window(follow_info->follow_save_as_w);
864 new_win = gtk_file_selection_new("Ethereal: Save TCP Follow Stream As");
865 follow_info->follow_save_as_w = new_win;
866 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
868 /* Tuck away the follow_info object into the window */
869 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
871 /* If we've opened a file, start out by showing the files in the directory
872 in which that file resided. */
874 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win),
877 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
878 pointer to the file selection box widget */
879 ok_bt = GTK_FILE_SELECTION(new_win)->ok_button;
880 SIGNAL_CONNECT(ok_bt, "clicked", follow_save_as_ok_cb, new_win);
882 /* Connect the cancel_button to destroy the widget */
883 SIGNAL_CONNECT_OBJECT(GTK_FILE_SELECTION(new_win)->cancel_button, "clicked",
884 gtk_widget_destroy, new_win);
886 /* Catch the "key_press_event" signal in the window, so that we can catch
887 the ESC key being pressed and act as if the "Cancel" button had
889 dlg_set_cancel(new_win,
890 GTK_FILE_SELECTION(new_win)->cancel_button);
892 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
893 gtk_widget_show_all(new_win);
898 follow_save_as_ok_cb(GtkWidget * w _U_, GtkFileSelection * fs)
901 follow_info_t *follow_info;
905 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
907 /* Perhaps the user specified a directory instead of a file.
908 Check whether they did. */
909 if (test_for_directory(to_name) == EISDIR) {
910 /* It's a directory - set the file selection box to display that
911 directory, and leave the selection box displayed. */
912 set_last_open_dir(to_name);
914 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs),
919 fh = fopen(to_name, "wb");
921 simple_dialog(ESD_TYPE_WARN, NULL,
922 file_open_error_message(errno, TRUE), to_name);
927 gtk_widget_hide(GTK_WIDGET(fs));
928 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
929 gtk_widget_destroy(GTK_WIDGET(fs));
931 follow_read_stream(follow_info, follow_print_text, fh);
934 /* Save the directory name for future file dialogs. */
935 dirname = get_dirname(to_name); /* Overwrites to_name */
936 set_last_open_dir(dirname);
941 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
943 follow_info_t *follow_info = data;
945 /* Note that we no longer have a dialog box. */
946 follow_info->follow_save_as_w = NULL;