2 * Common routines for following data streams
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
38 #include <epan/addr_resolv.h>
39 #include <epan/follow.h>
40 #include <epan/filesystem.h>
41 #include <epan/prefs.h>
42 #include <epan/charsets.h>
44 #include <../alert_box.h>
45 #include <../isprint.h>
47 #include <../simple_dialog.h>
48 #include <wsutil/file_util.h>
50 #include <gtk/color_utils.h>
51 #include <gtk/stock_icons.h>
52 #include <gtk/dlg_utils.h>
53 #include <gtk/follow_stream.h>
54 #include <gtk/font_utils.h>
55 #include <gtk/file_dlg.h>
56 #include <gtk/gui_utils.h>
57 #include <gtk/help_dlg.h>
61 #include "../tempfile.h"
62 #include "gtk/print_win32.h"
65 /* static variable declarations to speed up the performance
66 * of follow_load_text and follow_add_to_gtk_text
68 static GdkColor server_fg, server_bg;
69 static GdkColor client_fg, client_bg;
70 static GtkTextTag *server_tag, *client_tag;
72 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
74 GList *follow_infos = NULL;
77 follow_read_stream(follow_info_t *follow_info,
78 gboolean (*print_line)(char *, size_t, gboolean, void *),
81 switch(follow_info->follow_type) {
84 return follow_read_tcp_stream(follow_info, print_line, arg);
87 return follow_read_udp_stream(follow_info, print_line, arg);
90 return follow_read_ssl_stream(follow_info, print_line, arg);
93 g_assert_not_reached();
99 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
102 GtkWidget *text = arg;
103 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
106 /* While our isprint() hack is in place, we
107 * have to convert some chars to '.' in order
108 * to be able to see the data we *should* see
109 * in the GtkText widget.
113 for (i = 0; i < nchars; i++) {
114 if (buffer[i] == '\n' || buffer[i] == '\r')
116 if (! isprint((guchar)buffer[i])) {
121 gtk_text_buffer_get_end_iter(buf, &iter);
123 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
126 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
133 * XXX - for text printing, we probably want to wrap lines at 80 characters;
134 * (PostScript printing is doing this already), and perhaps put some kind of
135 * dingbat (to use the technical term) to indicate a wrapped line, along the
136 * lines of what's done when displaying this in a window, as per Warren Young's
140 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
143 print_stream_t *stream = arg;
147 /* convert non printable characters */
148 for (i = 0; i < nchars; i++) {
149 if (buffer[i] == '\n' || buffer[i] == '\r')
151 if (! isprint((guchar)buffer[i])) {
156 /* convert unterminated char array to a zero terminated string */
157 str = g_malloc(nchars + 1);
158 memcpy(str, buffer, nchars);
160 print_line(stream, /*indent*/ 0, str);
167 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
172 nwritten = fwrite(buffer, 1, nchars, fh);
173 if (nwritten != nchars)
179 /* Handles the display style toggling */
181 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
183 follow_info_t *follow_info = data;
186 * A radio button toggles when it goes on and when it goes
187 * off, so when you click a radio button two signals are
188 * delivered. We only want to reprocess the display once,
189 * so we do it only when the button goes on.
191 if (GTK_TOGGLE_BUTTON(w)->active) {
192 if (w == follow_info->ebcdic_bt)
193 follow_info->show_type = SHOW_EBCDIC;
194 else if (w == follow_info->hexdump_bt)
195 follow_info->show_type = SHOW_HEXDUMP;
196 else if (w == follow_info->carray_bt)
197 follow_info->show_type = SHOW_CARRAY;
198 else if (w == follow_info->ascii_bt)
199 follow_info->show_type = SHOW_ASCII;
200 else if (w == follow_info->raw_bt)
201 follow_info->show_type = SHOW_RAW;
202 follow_load_text(follow_info);
207 follow_load_text(follow_info_t *follow_info)
211 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
213 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
214 color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
215 color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
216 color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
217 color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
219 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
220 server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
221 &server_fg, "background-gdk",
222 &server_bg, "font-desc",
223 user_font_get_regular(), NULL);
224 client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
225 &client_fg, "background-gdk",
226 &client_bg, "font-desc",
227 user_font_get_regular(), NULL);
229 /* Delete any info already in text box */
230 gtk_text_buffer_set_text(buf, "", -1);
232 follow_read_stream(follow_info, follow_add_to_gtk_text,
237 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
239 follow_info_t *follow_info = data;
241 /* Lock out user from messing with us. (ie. don't free our data!) */
242 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
244 /* Set the display filter. */
245 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
246 follow_info->filter_out_filter);
248 /* Run the display filter so it goes in effect. */
249 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
251 /* we force a subsequent close */
252 window_destroy(follow_info->streamwindow);
258 follow_find_cb(GtkWidget * w _U_, gpointer data)
260 follow_info_t *follow_info = data;
261 GtkTooltips *tooltips;
262 GtkWidget *find_dlg_w, *main_vb, *buttons_row, *find_lb;
263 GtkWidget *find_hb, *find_text_box, *find_bt, *cancel_bt;
265 tooltips = gtk_tooltips_new();
267 if (follow_info->find_dlg_w != NULL) {
268 /* There's already a dialog box; reactivate it. */
269 reactivate_window(follow_info->find_dlg_w);
273 /* Create the find box */
274 find_dlg_w = dlg_window_new("Wireshark: Find text");
275 gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
276 GTK_WINDOW(follow_info->streamwindow));
277 gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
278 follow_info->find_dlg_w = find_dlg_w;
280 g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
282 g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
285 /* Main vertical box */
286 main_vb = gtk_vbox_new(FALSE, 3);
287 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
288 gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
290 /* Horizontal box for find label, entry field and up/down radio
292 find_hb = gtk_hbox_new(FALSE, 3);
293 gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
294 gtk_widget_show(find_hb);
297 find_lb = gtk_label_new("Find text:");
298 gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
299 gtk_widget_show(find_lb);
302 find_text_box = gtk_entry_new();
303 gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
304 gtk_tooltips_set_tip(tooltips, find_text_box, "Text to search for (case sensitive)", NULL);
305 gtk_widget_show(find_text_box);
308 buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
310 gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
311 find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
312 cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
314 g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
315 g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
316 window_set_cancel_button(find_dlg_w, cancel_bt,
317 window_cancel_button_cb);
319 /* Hitting return in the find field "clicks" the find button */
320 dlg_set_activate(find_text_box, find_bt);
322 /* Show the dialog */
323 gtk_widget_show_all(find_dlg_w);
324 window_present(find_dlg_w);
328 follow_find_button_cb(GtkWidget * w, gpointer data)
331 const gchar *find_string;
332 follow_info_t *follow_info = data;
333 GtkTextBuffer *buffer;
334 GtkTextIter iter, match_start, match_end;
335 GtkTextMark *last_pos_mark;
336 GtkWidget *find_string_w;
338 /* Get the text the user typed into the find field */
339 find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
340 find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
342 /* Get the buffer associated with the follow stream */
343 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
344 gtk_text_buffer_get_start_iter(buffer, &iter);
346 /* Look for the search string in the buffer */
347 last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
349 gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
351 found = gtk_text_iter_forward_search(&iter, find_string, 0,
357 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
358 last_pos_mark = gtk_text_buffer_create_mark (buffer,
361 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
363 /* We didn't find a match */
364 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
365 "%sFind text has reached the end of the followed "
366 "stream%s\n\nThe next search will start from the "
367 "beginning", simple_dialog_primary_start(),
368 simple_dialog_primary_end());
370 gtk_text_buffer_delete_mark(buffer, last_pos_mark);
376 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
378 follow_info_t *follow_info = data;
380 /* Note that we no longer have a dialog box. */
381 follow_info->find_dlg_w = NULL;
385 follow_print_stream(GtkWidget * w _U_, gpointer data)
387 print_stream_t *stream;
390 follow_info_t *follow_info = data;
392 gboolean win_printer = FALSE;
394 char tmp_namebuf[128+1]; /* see create_tmpfile which says [128+1]; why ? */
397 switch (prefs.pr_dest) {
401 /* (The code for creating a temp filename is adapted from print_dlg.c). */
402 /* We currently don't have a function in util.h to create just a tempfile */
403 /* name, so simply create a tempfile using the "official" function, */
404 /* then delete this file again. After this, the name MUST be available. */
406 /* Don't use tmpnam() or such, as this will fail under some ACL */
407 /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
408 /* Also: tmpnam is "insecure" and should not be used. */
409 tmp_fd = create_tempfile(tmp_namebuf, sizeof(tmp_namebuf), "wshprint");
411 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
412 "Couldn't create a temporary file for printing.");
416 ws_unlink(tmp_namebuf);
417 print_dest = tmp_namebuf;
420 print_dest = prefs.pr_cmd;
425 print_dest = prefs.pr_file;
428 default: /* "Can't happen" */
429 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
430 "Couldn't figure out where to send the print "
431 "job. Check your preferences.");
435 switch (prefs.pr_format) {
438 stream = print_stream_text_new(to_file, print_dest);
442 stream = print_stream_ps_new(to_file, print_dest);
446 g_assert_not_reached();
449 if (stream == NULL) {
451 open_failure_alert_box(print_dest, errno, TRUE);
453 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
454 "Couldn't run print command %s.",
460 if (!print_preamble(stream, cfile.filename))
463 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
468 /* XXX - cancel printing? */
469 destroy_print_stream(stream);
471 case FRS_PRINT_ERROR:
475 if (!print_finale(stream))
478 if (!destroy_print_stream(stream)) {
480 write_failure_alert_box(print_dest, errno);
482 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
483 "Error closing print destination.");
488 print_mswin(print_dest);
490 /* trash temp file */
491 ws_remove(print_dest);
498 write_failure_alert_box(print_dest, errno);
500 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
501 "Error writing to print command: %s",
504 /* XXX - cancel printing? */
505 destroy_print_stream(stream);
509 /* trash temp file */
510 ws_remove(print_dest);
516 * Keep a static pointer to the current "Save Follow Stream As" window, if
517 * any, so that if somebody tries to do "Save"
518 * while there's already a "Save Follow Stream" window up, we just pop
519 * up the existing one, rather than creating a new one.
522 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
525 follow_info_t *follow_info = data;
527 if (follow_info->follow_save_as_w != NULL) {
528 /* There's already a dialog box; reactivate it. */
529 reactivate_window(follow_info->follow_save_as_w);
533 new_win = file_selection_new("Wireshark: Save Follow Stream As",
534 FILE_SELECTION_SAVE);
535 follow_info->follow_save_as_w = new_win;
537 /* Tuck away the follow_info object into the window */
538 g_object_set_data(G_OBJECT(new_win), E_FOLLOW_INFO_KEY, follow_info);
540 g_signal_connect(new_win, "destroy", G_CALLBACK(follow_save_as_destroy_cb),
543 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
545 follow_save_as_ok_cb(new_win, new_win);
547 window_destroy(new_win);
553 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
556 follow_info_t *follow_info;
558 print_stream_t *stream = NULL;
561 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
563 /* Perhaps the user specified a directory instead of a file.
564 Check whether they did. */
565 if (test_for_directory(to_name) == EISDIR) {
566 /* It's a directory - set the file selection box to display that
567 directory, and leave the selection box displayed. */
568 set_last_open_dir(to_name);
570 file_selection_set_current_folder(fs, get_last_open_dir());
574 follow_info = g_object_get_data(G_OBJECT(fs), E_FOLLOW_INFO_KEY);
575 if (follow_info->show_type == SHOW_RAW) {
576 /* Write the data out as raw binary data */
577 fh = ws_fopen(to_name, "wb");
579 /* Write it out as text */
580 fh = ws_fopen(to_name, "w");
583 open_failure_alert_box(to_name, errno, TRUE);
588 gtk_widget_hide(GTK_WIDGET(fs));
589 window_destroy(GTK_WIDGET(fs));
591 if (follow_info->show_type == SHOW_RAW) {
592 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
594 if (fclose(fh) == EOF)
595 write_failure_alert_box(to_name, errno);
603 case FRS_PRINT_ERROR:
604 write_failure_alert_box(to_name, errno);
609 stream = print_stream_text_stdio_new(fh);
610 switch (follow_read_stream(follow_info, follow_print_text,
613 if (!destroy_print_stream(stream))
614 write_failure_alert_box(to_name, errno);
619 destroy_print_stream(stream);
622 case FRS_PRINT_ERROR:
623 write_failure_alert_box(to_name, errno);
624 destroy_print_stream(stream);
629 /* Save the directory name for future file dialogs. */
630 dirname = get_dirname(to_name); /* Overwrites to_name */
631 set_last_open_dir(dirname);
636 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
638 follow_info_t *follow_info = data;
640 /* Note that we no longer have a dialog box. */
641 follow_info->follow_save_as_w = NULL;
645 follow_stream_direction_changed(GtkWidget *w, gpointer data)
647 follow_info_t *follow_info = data;
649 switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
652 follow_info->show_stream = BOTH_HOSTS;
653 follow_load_text(follow_info);
656 follow_info->show_stream = FROM_CLIENT;
657 follow_load_text(follow_info);
660 follow_info->show_stream = FROM_SERVER;
661 follow_load_text(follow_info);
666 /* Add a "follow_info_t" structure to the list. */
668 remember_follow_info(follow_info_t *follow_info)
670 follow_infos = g_list_append(follow_infos, follow_info);
673 #define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
674 /* Remove a "follow_info_t" structure from the list. */
676 forget_follow_info(follow_info_t *follow_info)
678 follow_infos = g_list_remove(follow_infos, follow_info);
682 follow_stream(gchar *title, follow_info_t *follow_info,
683 gchar *both_directions_string,
684 gchar *server_to_client_string, gchar *client_to_server_string)
686 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text;
687 GtkWidget *hbox, *bbox, *button, *radio_bt;
688 GtkWidget *stream_fr, *stream_vb;
689 GtkWidget *stream_cmb;
690 GtkTooltips *tooltips;
691 follow_stats_t stats;
693 follow_info->show_type = SHOW_RAW;
695 streamwindow = dlg_window_new(title);
697 /* needed in follow_filter_out_stream(), is there a better way? */
698 follow_info->streamwindow = streamwindow;
700 gtk_widget_set_name(streamwindow, title);
701 gtk_window_set_default_size(GTK_WINDOW(streamwindow),
702 DEF_WIDTH, DEF_HEIGHT);
703 gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
705 /* setup the container */
706 tooltips = gtk_tooltips_new ();
708 vbox = gtk_vbox_new(FALSE, 6);
709 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
712 if (incomplete_tcp_stream) {
713 stream_fr = gtk_frame_new("Stream Content (incomplete)");
715 stream_fr = gtk_frame_new("Stream Content");
717 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
718 gtk_widget_show(stream_fr);
720 stream_vb = gtk_vbox_new(FALSE, 6);
721 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
722 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
724 /* create a scrolled window for the text */
725 txt_scrollw = scrolled_window_new(NULL, NULL);
726 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
728 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
730 /* create a text box */
731 text = gtk_text_view_new();
732 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
733 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
735 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
736 follow_info->text = text;
739 hbox = gtk_hbox_new(FALSE, 1);
740 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
742 /* Create Find Button */
743 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
744 g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
745 gtk_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
746 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
748 /* Create Save As Button */
749 button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
750 g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
751 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed", NULL);
752 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
754 /* Create Print Button */
755 button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
756 g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
757 gtk_tooltips_set_tip(tooltips, button, "Print the content as currently displayed", NULL);
758 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
761 follow_stats(&stats);
763 follow_info->is_ipv6 = stats.is_ipv6;
765 stream_cmb = gtk_combo_box_new_text();
767 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
768 both_directions_string);
769 follow_info->show_stream = BOTH_HOSTS;
771 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
772 server_to_client_string);
774 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
775 client_to_server_string);
777 g_signal_connect(stream_cmb, "changed",
778 G_CALLBACK(follow_stream_direction_changed),
781 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0);
783 gtk_tooltips_set_tip (tooltips, stream_cmb,
784 "Select the stream direction to display", NULL);
785 gtk_box_pack_start(GTK_BOX(hbox), stream_cmb, FALSE, FALSE, 0);
787 /* ASCII radio button */
788 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
789 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
790 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
791 IS_SHOW_TYPE(SHOW_ASCII));
792 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
793 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
795 follow_info->ascii_bt = radio_bt;
797 /* EBCDIC radio button */
798 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
799 (GTK_RADIO_BUTTON(radio_bt)),
801 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
802 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
803 IS_SHOW_TYPE(SHOW_EBCDIC));
804 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
805 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
807 follow_info->ebcdic_bt = radio_bt;
809 /* HEX DUMP radio button */
810 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
811 (GTK_RADIO_BUTTON(radio_bt)),
813 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
814 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
815 IS_SHOW_TYPE(SHOW_HEXDUMP));
816 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
817 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
819 follow_info->hexdump_bt = radio_bt;
821 /* C Array radio button */
822 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
823 (GTK_RADIO_BUTTON(radio_bt)),
825 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
826 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
827 IS_SHOW_TYPE(SHOW_CARRAY));
828 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
829 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
831 follow_info->carray_bt = radio_bt;
833 /* Raw radio button */
834 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
835 (GTK_RADIO_BUTTON(radio_bt)),
837 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Raw\" (binary) format. As this contains non printable characters, the screen output will be in ASCII format", NULL);
838 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
839 IS_SHOW_TYPE(SHOW_RAW));
840 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
841 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
843 follow_info->raw_bt = radio_bt;
845 /* Button row: help, filter out, close button */
846 bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
847 GTK_STOCK_CLOSE, GTK_STOCK_HELP,
849 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
852 button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
853 gtk_tooltips_set_tip (tooltips, button,
854 "Build a display filter which cuts this stream from the capture", NULL);
855 g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
858 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
859 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
860 gtk_tooltips_set_tip (tooltips, button,
861 "Close the dialog and keep the current display filter", NULL);
862 gtk_widget_grab_default(button);
864 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
865 g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
866 (gpointer)HELP_FOLLOW_STREAM_DIALOG);
868 /* Tuck away the follow_info object into the window */
869 g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
871 follow_load_text(follow_info);
872 remember_follow_info(follow_info);
875 g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
876 g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
878 /* Make sure this widget gets destroyed if we quit the main loop,
879 so that if we exit, we clean up any temporary files we have
880 for "Follow TCP Stream" windows. */
881 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
883 gtk_widget_show_all(streamwindow);
884 window_present(streamwindow);
887 /* The destroy call back has the responsibility of
888 * unlinking the temporary file
889 * and freeing the filter_out_filter */
891 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
893 follow_info_t *follow_info;
894 follow_record_t *follow_record;
898 follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
900 switch(follow_info->follow_type) {
903 i = ws_unlink(follow_info->data_out_filename);
905 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", follow_info->data_out_filename, strerror(errno), errno);
910 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
912 follow_record = cur->data;
913 if(follow_record->data)
914 g_byte_array_free(follow_record->data,
917 g_free(follow_record);
920 g_list_free(follow_info->payload);
924 /* free decrypted data list*/
925 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
931 g_list_free (follow_info->payload);
935 g_free(follow_info->filter_out_filter);
936 g_free((gpointer)follow_info->client_ip.data);
937 forget_follow_info(follow_info);
942 follow_show(follow_info_t *follow_info,
943 gboolean (*print_line)(char *, size_t, gboolean, void *),
944 char *buffer, size_t nchars, gboolean is_server, void *arg,
945 guint32 *global_pos, guint32 *server_packet_count,
946 guint32 *client_packet_count)
950 static const gchar hexchars[16] = "0123456789abcdef";
952 switch (follow_info->show_type) {
955 /* If our native arch is ASCII, call: */
956 EBCDIC_to_ASCII(buffer, (guint) nchars);
957 if (!(*print_line) (buffer, nchars, is_server, arg))
958 return FRS_PRINT_ERROR;
962 /* If our native arch is EBCDIC, call:
963 * ASCII_TO_EBCDIC(buffer, nchars);
965 if (!(*print_line) (buffer, nchars, is_server, arg))
966 return FRS_PRINT_ERROR;
970 /* Don't translate, no matter what the native arch
973 if (!(*print_line) (buffer, nchars, is_server, arg))
974 return FRS_PRINT_ERROR;
979 while (current_pos < nchars) {
982 gchar *cur = hexbuf, *ascii_start;
984 /* is_server indentation : put 78 spaces at the
985 * beginning of the string */
986 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
987 memset(cur, ' ', 78);
990 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
991 /* 49 is space consumed by hex chars */
992 ascii_start = cur + 49;
993 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
995 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
997 hexchars[buffer[current_pos + i] & 0x0f];
1002 /* Fill it up if column isn't complete */
1003 while (cur < ascii_start)
1006 /* Now dump bytes as text */
1007 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1009 (isprint((guchar)buffer[current_pos + i]) ?
1010 buffer[current_pos + i] : '.' );
1019 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1020 return FRS_PRINT_ERROR;
1026 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1028 is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1029 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
1030 return FRS_PRINT_ERROR;
1032 while (current_pos < nchars) {
1037 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1038 /* Prepend entries with "0x" */
1039 hexbuf[cur++] = '0';
1040 hexbuf[cur++] = 'x';
1042 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1044 hexchars[buffer[current_pos + i] & 0x0f];
1046 /* Delimit array entries with a comma */
1047 if (current_pos + i + 1 < nchars)
1048 hexbuf[cur++] = ',';
1050 hexbuf[cur++] = ' ';
1053 /* Terminate the array if we are at the end */
1054 if (current_pos + i == nchars) {
1055 hexbuf[cur++] = '}';
1056 hexbuf[cur++] = ';';
1061 hexbuf[cur++] = '\n';
1063 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1064 return FRS_PRINT_ERROR;