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>
46 #include <../simple_dialog.h>
47 #include <wsutil/file_util.h>
49 #include <gtk/color_utils.h>
50 #include <gtk/stock_icons.h>
51 #include <gtk/dlg_utils.h>
52 #include <gtk/follow_stream.h>
53 #include <gtk/font_utils.h>
54 #include <gtk/file_dlg.h>
55 #include <gtk/gui_utils.h>
56 #include <gtk/help_dlg.h>
60 #include "../tempfile.h"
61 #include "win32/print_win32.h"
64 /* static variable declarations to speed up the performance
65 * of follow_load_text and follow_add_to_gtk_text
67 static GdkColor server_fg, server_bg;
68 static GdkColor client_fg, client_bg;
69 static GtkTextTag *server_tag, *client_tag;
71 static void follow_find_destroy_cb(GtkWidget * win _U_, gpointer data);
72 static void follow_find_button_cb(GtkWidget * w, gpointer data);
73 static gboolean follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs);
74 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
75 static void follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data);
77 GList *follow_infos = NULL;
80 follow_read_stream(follow_info_t *follow_info,
81 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
84 switch(follow_info->follow_type) {
87 return follow_read_tcp_stream(follow_info, print_line_fcn_p, arg);
90 return follow_read_udp_stream(follow_info, print_line_fcn_p, arg);
93 return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg);
96 g_assert_not_reached();
102 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
105 GtkWidget *text = arg;
106 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
109 /* While our isprint() hack is in place, we
110 * have to convert some chars to '.' in order
111 * to be able to see the data we *should* see
112 * in the GtkText widget.
116 for (i = 0; i < nchars; i++) {
117 if (buffer[i] == '\n' || buffer[i] == '\r')
119 if (! g_unichar_isprint((guchar)buffer[i])) {
124 gtk_text_buffer_get_end_iter(buf, &iter);
126 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
129 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
136 * XXX - for text printing, we probably want to wrap lines at 80 characters;
137 * (PostScript printing is doing this already), and perhaps put some kind of
138 * dingbat (to use the technical term) to indicate a wrapped line, along the
139 * lines of what's done when displaying this in a window, as per Warren Young's
143 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
146 print_stream_t *stream = arg;
150 /* convert non printable characters */
151 for (i = 0; i < nchars; i++) {
152 if (buffer[i] == '\n' || buffer[i] == '\r')
154 if (! g_unichar_isprint((guchar)buffer[i])) {
159 /* convert unterminated char array to a zero terminated string */
160 str = g_malloc(nchars + 1);
161 memcpy(str, buffer, nchars);
163 print_line(stream, /*indent*/ 0, str);
170 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
175 nwritten = fwrite(buffer, 1, nchars, fh);
176 if (nwritten != nchars)
182 /* Handles the display style toggling */
184 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
186 follow_info_t *follow_info = data;
189 * A radio button toggles when it goes on and when it goes
190 * off, so when you click a radio button two signals are
191 * delivered. We only want to reprocess the display once,
192 * so we do it only when the button goes on.
194 if (GTK_TOGGLE_BUTTON(w)->active) {
195 if (w == follow_info->ebcdic_bt)
196 follow_info->show_type = SHOW_EBCDIC;
197 else if (w == follow_info->hexdump_bt)
198 follow_info->show_type = SHOW_HEXDUMP;
199 else if (w == follow_info->carray_bt)
200 follow_info->show_type = SHOW_CARRAY;
201 else if (w == follow_info->ascii_bt)
202 follow_info->show_type = SHOW_ASCII;
203 else if (w == follow_info->raw_bt)
204 follow_info->show_type = SHOW_RAW;
205 follow_load_text(follow_info);
210 follow_load_text(follow_info_t *follow_info)
214 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
216 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
217 color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
218 color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
219 color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
220 color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
222 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
223 server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
224 &server_fg, "background-gdk",
225 &server_bg, "font-desc",
226 user_font_get_regular(), NULL);
227 client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
228 &client_fg, "background-gdk",
229 &client_bg, "font-desc",
230 user_font_get_regular(), NULL);
232 /* Delete any info already in text box */
233 gtk_text_buffer_set_text(buf, "", -1);
235 follow_read_stream(follow_info, follow_add_to_gtk_text,
240 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
242 follow_info_t *follow_info = data;
244 /* Lock out user from messing with us. (ie. don't free our data!) */
245 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
247 /* Set the display filter. */
248 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
249 follow_info->filter_out_filter);
251 /* Run the display filter so it goes in effect. */
252 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
254 /* we force a subsequent close */
255 window_destroy(follow_info->streamwindow);
261 follow_find_cb(GtkWidget * w _U_, gpointer data)
263 follow_info_t *follow_info = data;
264 GtkTooltips *tooltips;
265 GtkWidget *find_dlg_w, *main_vb, *buttons_row, *find_lb;
266 GtkWidget *find_hb, *find_text_box, *find_bt, *cancel_bt;
268 tooltips = gtk_tooltips_new();
270 if (follow_info->find_dlg_w != NULL) {
271 /* There's already a dialog box; reactivate it. */
272 reactivate_window(follow_info->find_dlg_w);
276 /* Create the find box */
277 find_dlg_w = dlg_window_new("Wireshark: Find text");
278 gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
279 GTK_WINDOW(follow_info->streamwindow));
280 gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
281 follow_info->find_dlg_w = find_dlg_w;
283 g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
285 g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
288 /* Main vertical box */
289 main_vb = gtk_vbox_new(FALSE, 3);
290 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
291 gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
293 /* Horizontal box for find label, entry field and up/down radio
295 find_hb = gtk_hbox_new(FALSE, 3);
296 gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
297 gtk_widget_show(find_hb);
300 find_lb = gtk_label_new("Find text:");
301 gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
302 gtk_widget_show(find_lb);
305 find_text_box = gtk_entry_new();
306 gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
307 gtk_tooltips_set_tip(tooltips, find_text_box, "Text to search for (case sensitive)", NULL);
308 gtk_widget_show(find_text_box);
311 buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
313 gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
314 find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
315 cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
317 g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
318 g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
319 window_set_cancel_button(find_dlg_w, cancel_bt,
320 window_cancel_button_cb);
322 /* Hitting return in the find field "clicks" the find button */
323 dlg_set_activate(find_text_box, find_bt);
325 /* Show the dialog */
326 gtk_widget_show_all(find_dlg_w);
327 window_present(find_dlg_w);
331 follow_find_button_cb(GtkWidget * w, gpointer data)
334 const gchar *find_string;
335 follow_info_t *follow_info = data;
336 GtkTextBuffer *buffer;
337 GtkTextIter iter, match_start, match_end;
338 GtkTextMark *last_pos_mark;
339 GtkWidget *find_string_w;
341 /* Get the text the user typed into the find field */
342 find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
343 find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
345 /* Get the buffer associated with the follow stream */
346 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
347 gtk_text_buffer_get_start_iter(buffer, &iter);
349 /* Look for the search string in the buffer */
350 last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
352 gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
354 found = gtk_text_iter_forward_search(&iter, find_string, 0,
360 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
361 last_pos_mark = gtk_text_buffer_create_mark (buffer,
364 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
366 /* We didn't find a match */
367 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
368 "%sFind text has reached the end of the followed "
369 "stream%s\n\nThe next search will start from the "
370 "beginning", simple_dialog_primary_start(),
371 simple_dialog_primary_end());
373 gtk_text_buffer_delete_mark(buffer, last_pos_mark);
379 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
381 follow_info_t *follow_info = data;
383 /* Note that we no longer have a dialog box. */
384 follow_info->find_dlg_w = NULL;
388 follow_print_stream(GtkWidget * w _U_, gpointer data)
390 print_stream_t *stream;
393 follow_info_t *follow_info = data;
395 gboolean win_printer = FALSE;
400 switch (prefs.pr_dest) {
404 /* (The code for creating a temp filename is adapted from print_dlg.c). */
405 /* We currently don't have a function in util.h to create just a tempfile */
406 /* name, so simply create a tempfile using the "official" function, */
407 /* then delete this file again. After this, the name MUST be available. */
409 /* Don't use tmpnam() or such, as this will fail under some ACL */
410 /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
411 /* Also: tmpnam is "insecure" and should not be used. */
412 tmp_fd = create_tempfile(&tmp_namebuf, "wshprint");
414 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
415 "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
419 ws_unlink(tmp_namebuf);
420 print_dest = tmp_namebuf;
423 print_dest = prefs.pr_cmd;
428 print_dest = prefs.pr_file;
431 default: /* "Can't happen" */
432 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
433 "Couldn't figure out where to send the print "
434 "job. Check your preferences.");
438 switch (prefs.pr_format) {
441 stream = print_stream_text_new(to_file, print_dest);
445 stream = print_stream_ps_new(to_file, print_dest);
449 g_assert_not_reached();
452 if (stream == NULL) {
454 open_failure_alert_box(print_dest, errno, TRUE);
456 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
457 "Couldn't run print command %s.",
463 if (!print_preamble(stream, cfile.filename))
466 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
471 /* XXX - cancel printing? */
472 destroy_print_stream(stream);
474 case FRS_PRINT_ERROR:
478 if (!print_finale(stream))
481 if (!destroy_print_stream(stream)) {
483 write_failure_alert_box(print_dest, errno);
485 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
486 "Error closing print destination.");
491 print_mswin(print_dest);
493 /* trash temp file */
494 ws_remove(print_dest);
501 write_failure_alert_box(print_dest, errno);
503 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
504 "Error writing to print command: %s",
507 /* XXX - cancel printing? */
508 destroy_print_stream(stream);
512 /* trash temp file */
513 ws_remove(print_dest);
519 * Keep a static pointer to the current "Save Follow Stream As" window, if
520 * any, so that if somebody tries to do "Save"
521 * while there's already a "Save Follow Stream" window up, we just pop
522 * up the existing one, rather than creating a new one.
526 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
529 follow_info_t *follow_info = data;
531 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
532 if (follow_info->follow_save_as_w != NULL) {
533 /* There's already a dialog box; reactivate it. */
534 reactivate_window(follow_info->follow_save_as_w);
538 new_win = file_selection_new("Wireshark: Save Follow Stream As",
539 FILE_SELECTION_SAVE);
540 follow_info->follow_save_as_w = new_win;
541 #if GTK_CHECK_VERSION(2,8,0)
542 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(new_win), TRUE);
545 /* Tuck away the follow_info object into the window */
546 g_object_set_data(G_OBJECT(new_win), E_FOLLOW_INFO_KEY, follow_info);
548 g_signal_connect(new_win, "destroy", G_CALLBACK(follow_save_as_destroy_cb),
552 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
554 follow_save_as_ok_cb(new_win, new_win);
556 window_destroy(new_win);
559 /* "Run" the GtkFileChooserDialog. */
560 /* Upon exit: If "Accept" run the OK callback. */
561 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
562 /* If not accept (ie: cancel) destroy the window. */
563 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
564 /* return with a TRUE status so that the dialog window will be destroyed. */
565 /* Trying to re-run the dialog after popping up an alert box will not work */
566 /* since the user will not be able to dismiss the alert box. */
567 /* The (somewhat unfriendly) effect: the user must re-invoke the */
568 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
570 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
571 /* GtkFileChooserDialog. */
572 while (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT) {
573 if (follow_save_as_ok_cb(NULL, new_win)) {
574 break; /* we're done */
577 window_destroy(new_win);
582 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
585 follow_info_t *follow_info;
587 print_stream_t *stream = NULL;
590 to_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
592 /* Perhaps the user specified a directory instead of a file.
593 Check whether they did. */
594 if (test_for_directory(to_name) == EISDIR) {
595 /* It's a directory - set the file selection box to display that
596 directory, and leave the selection box displayed. */
597 set_last_open_dir(to_name);
599 file_selection_set_current_folder(fs, get_last_open_dir());
600 gtk_file_chooser_set_current_name(fs, "");
601 return FALSE; /* do gtk_dialog_run again */
604 follow_info = g_object_get_data(G_OBJECT(fs), E_FOLLOW_INFO_KEY);
606 if (follow_info->show_type == SHOW_RAW) {
607 /* Write the data out as raw binary data */
608 fh = ws_fopen(to_name, "wb");
610 /* Write it out as text */
611 fh = ws_fopen(to_name, "w");
614 open_failure_alert_box(to_name, errno, TRUE);
619 #if 0 /* handled by caller (for now) .... */
620 gtk_widget_hide(GTK_WIDGET(fs));
621 window_destroy(GTK_WIDGET(fs));
623 if (follow_info->show_type == SHOW_RAW) {
624 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
626 if (fclose(fh) == EOF)
627 write_failure_alert_box(to_name, errno);
635 case FRS_PRINT_ERROR:
636 write_failure_alert_box(to_name, errno);
641 stream = print_stream_text_stdio_new(fh);
642 switch (follow_read_stream(follow_info, follow_print_text,
645 if (!destroy_print_stream(stream))
646 write_failure_alert_box(to_name, errno);
651 destroy_print_stream(stream);
654 case FRS_PRINT_ERROR:
655 write_failure_alert_box(to_name, errno);
656 destroy_print_stream(stream);
661 /* Save the directory name for future file dialogs. */
662 dirname = get_dirname(to_name); /* Overwrites to_name */
663 set_last_open_dir(dirname);
669 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
671 follow_info_t *follow_info = data;
673 /* Note that we no longer have a dialog box. */
674 follow_info->follow_save_as_w = NULL;
678 follow_stream_direction_changed(GtkWidget *w, gpointer data)
680 follow_info_t *follow_info = data;
682 switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
685 follow_info->show_stream = BOTH_HOSTS;
686 follow_load_text(follow_info);
689 follow_info->show_stream = FROM_CLIENT;
690 follow_load_text(follow_info);
693 follow_info->show_stream = FROM_SERVER;
694 follow_load_text(follow_info);
699 /* Add a "follow_info_t" structure to the list. */
701 remember_follow_info(follow_info_t *follow_info)
703 follow_infos = g_list_append(follow_infos, follow_info);
706 #define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
707 /* Remove a "follow_info_t" structure from the list. */
709 forget_follow_info(follow_info_t *follow_info)
711 follow_infos = g_list_remove(follow_infos, follow_info);
715 follow_stream(gchar *title, follow_info_t *follow_info,
716 gchar *both_directions_string,
717 gchar *server_to_client_string, gchar *client_to_server_string)
719 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text;
720 GtkWidget *hbox, *bbox, *button, *radio_bt;
721 GtkWidget *stream_fr, *stream_vb;
722 GtkWidget *stream_cmb;
723 GtkTooltips *tooltips;
724 follow_stats_t stats;
726 follow_info->show_type = SHOW_RAW;
728 streamwindow = dlg_window_new(title);
730 /* needed in follow_filter_out_stream(), is there a better way? */
731 follow_info->streamwindow = streamwindow;
733 gtk_widget_set_name(streamwindow, title);
734 gtk_window_set_default_size(GTK_WINDOW(streamwindow),
735 DEF_WIDTH, DEF_HEIGHT);
736 gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
738 /* setup the container */
739 tooltips = gtk_tooltips_new ();
741 vbox = gtk_vbox_new(FALSE, 6);
742 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
745 if (incomplete_tcp_stream) {
746 stream_fr = gtk_frame_new("Stream Content (incomplete)");
748 stream_fr = gtk_frame_new("Stream Content");
750 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
751 gtk_widget_show(stream_fr);
753 stream_vb = gtk_vbox_new(FALSE, 6);
754 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
755 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
757 /* create a scrolled window for the text */
758 txt_scrollw = scrolled_window_new(NULL, NULL);
759 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
761 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
763 /* create a text box */
764 text = gtk_text_view_new();
765 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
766 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
768 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
769 follow_info->text = text;
772 hbox = gtk_hbox_new(FALSE, 1);
773 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
775 /* Create Find Button */
776 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
777 g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
778 gtk_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
779 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
781 /* Create Save As Button */
782 button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
783 g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
784 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed", NULL);
785 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
787 /* Create Print Button */
788 button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
789 g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
790 gtk_tooltips_set_tip(tooltips, button, "Print the content as currently displayed", NULL);
791 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
794 follow_stats(&stats);
796 follow_info->is_ipv6 = stats.is_ipv6;
798 stream_cmb = gtk_combo_box_new_text();
800 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
801 both_directions_string);
802 follow_info->show_stream = BOTH_HOSTS;
804 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
805 server_to_client_string);
807 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
808 client_to_server_string);
810 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect */
811 /* so callback not triggered */
813 g_signal_connect(stream_cmb, "changed",
814 G_CALLBACK(follow_stream_direction_changed),
817 gtk_tooltips_set_tip (tooltips, stream_cmb,
818 "Select the stream direction to display", NULL);
819 gtk_box_pack_start(GTK_BOX(hbox), stream_cmb, FALSE, FALSE, 0);
821 /* ASCII radio button */
822 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
823 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
824 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
825 IS_SHOW_TYPE(SHOW_ASCII));
826 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
827 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
829 follow_info->ascii_bt = radio_bt;
831 /* EBCDIC radio button */
832 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
833 (GTK_RADIO_BUTTON(radio_bt)),
835 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
836 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
837 IS_SHOW_TYPE(SHOW_EBCDIC));
838 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
839 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
841 follow_info->ebcdic_bt = radio_bt;
843 /* HEX DUMP radio button */
844 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
845 (GTK_RADIO_BUTTON(radio_bt)),
847 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
848 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
849 IS_SHOW_TYPE(SHOW_HEXDUMP));
850 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
851 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
853 follow_info->hexdump_bt = radio_bt;
855 /* C Array radio button */
856 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
857 (GTK_RADIO_BUTTON(radio_bt)),
859 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
860 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
861 IS_SHOW_TYPE(SHOW_CARRAY));
862 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
863 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
865 follow_info->carray_bt = radio_bt;
867 /* Raw radio button */
868 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
869 (GTK_RADIO_BUTTON(radio_bt)),
871 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);
872 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
873 IS_SHOW_TYPE(SHOW_RAW));
874 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
875 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
877 follow_info->raw_bt = radio_bt;
879 /* Button row: help, filter out, close button */
880 bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
881 GTK_STOCK_CLOSE, GTK_STOCK_HELP,
883 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
886 button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
887 gtk_tooltips_set_tip (tooltips, button,
888 "Build a display filter which cuts this stream from the capture", NULL);
889 g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
892 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
893 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
894 gtk_tooltips_set_tip (tooltips, button,
895 "Close the dialog and keep the current display filter", NULL);
896 gtk_widget_grab_default(button);
898 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
899 g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
900 (gpointer)HELP_FOLLOW_STREAM_DIALOG);
902 /* Tuck away the follow_info object into the window */
903 g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
905 follow_load_text(follow_info);
906 remember_follow_info(follow_info);
909 g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
910 g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
912 /* Make sure this widget gets destroyed if we quit the main loop,
913 so that if we exit, we clean up any temporary files we have
914 for "Follow TCP Stream" windows. */
915 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
917 gtk_widget_show_all(streamwindow);
918 window_present(streamwindow);
921 /* The destroy call back has the responsibility of
922 * unlinking the temporary file
923 * and freeing the filter_out_filter */
925 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
927 follow_info_t *follow_info;
928 follow_record_t *follow_record;
932 follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
934 switch(follow_info->follow_type) {
937 i = ws_unlink(follow_info->data_out_filename);
939 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", follow_info->data_out_filename, strerror(errno), errno);
944 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
946 follow_record = cur->data;
947 if(follow_record->data)
948 g_byte_array_free(follow_record->data,
951 g_free(follow_record);
954 g_list_free(follow_info->payload);
958 /* free decrypted data list*/
959 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
965 g_list_free (follow_info->payload);
969 g_free(follow_info->data_out_filename);
970 g_free(follow_info->filter_out_filter);
971 g_free((gpointer)follow_info->client_ip.data);
972 forget_follow_info(follow_info);
977 follow_show(follow_info_t *follow_info,
978 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
979 char *buffer, size_t nchars, gboolean is_server, void *arg,
980 guint32 *global_pos, guint32 *server_packet_count,
981 guint32 *client_packet_count)
985 static const gchar hexchars[16] = "0123456789abcdef";
987 switch (follow_info->show_type) {
990 /* If our native arch is ASCII, call: */
991 EBCDIC_to_ASCII(buffer, (guint) nchars);
992 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
993 return FRS_PRINT_ERROR;
997 /* If our native arch is EBCDIC, call:
998 * ASCII_TO_EBCDIC(buffer, nchars);
1000 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1001 return FRS_PRINT_ERROR;
1005 /* Don't translate, no matter what the native arch
1008 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1009 return FRS_PRINT_ERROR;
1014 while (current_pos < nchars) {
1017 gchar *cur = hexbuf, *ascii_start;
1019 /* is_server indentation : put 4 spaces at the
1020 * beginning of the string */
1021 /* XXX - We might want to prepend each line with "C" or "S" instead. */
1022 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
1023 memset(cur, ' ', 4);
1026 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
1027 /* 49 is space consumed by hex chars */
1028 ascii_start = cur + 49;
1029 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1031 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1033 hexchars[buffer[current_pos + i] & 0x0f];
1038 /* Fill it up if column isn't complete */
1039 while (cur < ascii_start)
1042 /* Now dump bytes as text */
1043 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1045 (g_unichar_isprint((guchar)buffer[current_pos + i]) ?
1046 buffer[current_pos + i] : '.' );
1055 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1056 return FRS_PRINT_ERROR;
1062 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1064 is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1065 if (!(*print_line_fcn_p) (initbuf, strlen(initbuf), is_server, arg))
1066 return FRS_PRINT_ERROR;
1068 while (current_pos < nchars) {
1073 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1074 /* Prepend entries with "0x" */
1075 hexbuf[cur++] = '0';
1076 hexbuf[cur++] = 'x';
1078 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1080 hexchars[buffer[current_pos + i] & 0x0f];
1082 /* Delimit array entries with a comma */
1083 if (current_pos + i + 1 < nchars)
1084 hexbuf[cur++] = ',';
1086 hexbuf[cur++] = ' ';
1089 /* Terminate the array if we are at the end */
1090 if (current_pos + i == nchars) {
1091 hexbuf[cur++] = '}';
1092 hexbuf[cur++] = ';';
1097 hexbuf[cur++] = '\n';
1099 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1100 return FRS_PRINT_ERROR;