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_find_destroy_cb(GtkWidget * win _U_, gpointer data);
73 static void follow_find_button_cb(GtkWidget * w, gpointer data);
74 static gboolean follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs);
75 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
76 static void follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data);
78 GList *follow_infos = NULL;
81 follow_read_stream(follow_info_t *follow_info,
82 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
85 switch(follow_info->follow_type) {
88 return follow_read_tcp_stream(follow_info, print_line_fcn_p, arg);
91 return follow_read_udp_stream(follow_info, print_line_fcn_p, arg);
94 return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg);
97 g_assert_not_reached();
103 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
106 GtkWidget *text = arg;
107 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
110 /* While our isprint() hack is in place, we
111 * have to convert some chars to '.' in order
112 * to be able to see the data we *should* see
113 * in the GtkText widget.
117 for (i = 0; i < nchars; i++) {
118 if (buffer[i] == '\n' || buffer[i] == '\r')
120 if (! isprint((guchar)buffer[i])) {
125 gtk_text_buffer_get_end_iter(buf, &iter);
127 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
130 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
137 * XXX - for text printing, we probably want to wrap lines at 80 characters;
138 * (PostScript printing is doing this already), and perhaps put some kind of
139 * dingbat (to use the technical term) to indicate a wrapped line, along the
140 * lines of what's done when displaying this in a window, as per Warren Young's
144 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
147 print_stream_t *stream = arg;
151 /* convert non printable characters */
152 for (i = 0; i < nchars; i++) {
153 if (buffer[i] == '\n' || buffer[i] == '\r')
155 if (! isprint((guchar)buffer[i])) {
160 /* convert unterminated char array to a zero terminated string */
161 str = g_malloc(nchars + 1);
162 memcpy(str, buffer, nchars);
164 print_line(stream, /*indent*/ 0, str);
171 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
176 nwritten = fwrite(buffer, 1, nchars, fh);
177 if (nwritten != nchars)
183 /* Handles the display style toggling */
185 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
187 follow_info_t *follow_info = data;
190 * A radio button toggles when it goes on and when it goes
191 * off, so when you click a radio button two signals are
192 * delivered. We only want to reprocess the display once,
193 * so we do it only when the button goes on.
195 if (GTK_TOGGLE_BUTTON(w)->active) {
196 if (w == follow_info->ebcdic_bt)
197 follow_info->show_type = SHOW_EBCDIC;
198 else if (w == follow_info->hexdump_bt)
199 follow_info->show_type = SHOW_HEXDUMP;
200 else if (w == follow_info->carray_bt)
201 follow_info->show_type = SHOW_CARRAY;
202 else if (w == follow_info->ascii_bt)
203 follow_info->show_type = SHOW_ASCII;
204 else if (w == follow_info->raw_bt)
205 follow_info->show_type = SHOW_RAW;
206 follow_load_text(follow_info);
211 follow_load_text(follow_info_t *follow_info)
215 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
217 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
218 color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
219 color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
220 color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
221 color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
223 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
224 server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
225 &server_fg, "background-gdk",
226 &server_bg, "font-desc",
227 user_font_get_regular(), NULL);
228 client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
229 &client_fg, "background-gdk",
230 &client_bg, "font-desc",
231 user_font_get_regular(), NULL);
233 /* Delete any info already in text box */
234 gtk_text_buffer_set_text(buf, "", -1);
236 follow_read_stream(follow_info, follow_add_to_gtk_text,
241 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
243 follow_info_t *follow_info = data;
245 /* Lock out user from messing with us. (ie. don't free our data!) */
246 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
248 /* Set the display filter. */
249 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
250 follow_info->filter_out_filter);
252 /* Run the display filter so it goes in effect. */
253 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
255 /* we force a subsequent close */
256 window_destroy(follow_info->streamwindow);
262 follow_find_cb(GtkWidget * w _U_, gpointer data)
264 follow_info_t *follow_info = data;
265 GtkTooltips *tooltips;
266 GtkWidget *find_dlg_w, *main_vb, *buttons_row, *find_lb;
267 GtkWidget *find_hb, *find_text_box, *find_bt, *cancel_bt;
269 tooltips = gtk_tooltips_new();
271 if (follow_info->find_dlg_w != NULL) {
272 /* There's already a dialog box; reactivate it. */
273 reactivate_window(follow_info->find_dlg_w);
277 /* Create the find box */
278 find_dlg_w = dlg_window_new("Wireshark: Find text");
279 gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
280 GTK_WINDOW(follow_info->streamwindow));
281 gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
282 follow_info->find_dlg_w = find_dlg_w;
284 g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
286 g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
289 /* Main vertical box */
290 main_vb = gtk_vbox_new(FALSE, 3);
291 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
292 gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
294 /* Horizontal box for find label, entry field and up/down radio
296 find_hb = gtk_hbox_new(FALSE, 3);
297 gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
298 gtk_widget_show(find_hb);
301 find_lb = gtk_label_new("Find text:");
302 gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
303 gtk_widget_show(find_lb);
306 find_text_box = gtk_entry_new();
307 gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
308 gtk_tooltips_set_tip(tooltips, find_text_box, "Text to search for (case sensitive)", NULL);
309 gtk_widget_show(find_text_box);
312 buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
314 gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
315 find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
316 cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
318 g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
319 g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
320 window_set_cancel_button(find_dlg_w, cancel_bt,
321 window_cancel_button_cb);
323 /* Hitting return in the find field "clicks" the find button */
324 dlg_set_activate(find_text_box, find_bt);
326 /* Show the dialog */
327 gtk_widget_show_all(find_dlg_w);
328 window_present(find_dlg_w);
332 follow_find_button_cb(GtkWidget * w, gpointer data)
335 const gchar *find_string;
336 follow_info_t *follow_info = data;
337 GtkTextBuffer *buffer;
338 GtkTextIter iter, match_start, match_end;
339 GtkTextMark *last_pos_mark;
340 GtkWidget *find_string_w;
342 /* Get the text the user typed into the find field */
343 find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
344 find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
346 /* Get the buffer associated with the follow stream */
347 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
348 gtk_text_buffer_get_start_iter(buffer, &iter);
350 /* Look for the search string in the buffer */
351 last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
353 gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
355 found = gtk_text_iter_forward_search(&iter, find_string, 0,
361 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
362 last_pos_mark = gtk_text_buffer_create_mark (buffer,
365 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
367 /* We didn't find a match */
368 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
369 "%sFind text has reached the end of the followed "
370 "stream%s\n\nThe next search will start from the "
371 "beginning", simple_dialog_primary_start(),
372 simple_dialog_primary_end());
374 gtk_text_buffer_delete_mark(buffer, last_pos_mark);
380 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
382 follow_info_t *follow_info = data;
384 /* Note that we no longer have a dialog box. */
385 follow_info->find_dlg_w = NULL;
389 follow_print_stream(GtkWidget * w _U_, gpointer data)
391 print_stream_t *stream;
394 follow_info_t *follow_info = data;
396 gboolean win_printer = FALSE;
401 switch (prefs.pr_dest) {
405 /* (The code for creating a temp filename is adapted from print_dlg.c). */
406 /* We currently don't have a function in util.h to create just a tempfile */
407 /* name, so simply create a tempfile using the "official" function, */
408 /* then delete this file again. After this, the name MUST be available. */
410 /* Don't use tmpnam() or such, as this will fail under some ACL */
411 /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
412 /* Also: tmpnam is "insecure" and should not be used. */
413 tmp_fd = create_tempfile(&tmp_namebuf, "wshprint");
415 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
416 "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
420 ws_unlink(tmp_namebuf);
421 print_dest = tmp_namebuf;
424 print_dest = prefs.pr_cmd;
429 print_dest = prefs.pr_file;
432 default: /* "Can't happen" */
433 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
434 "Couldn't figure out where to send the print "
435 "job. Check your preferences.");
439 switch (prefs.pr_format) {
442 stream = print_stream_text_new(to_file, print_dest);
446 stream = print_stream_ps_new(to_file, print_dest);
450 g_assert_not_reached();
453 if (stream == NULL) {
455 open_failure_alert_box(print_dest, errno, TRUE);
457 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
458 "Couldn't run print command %s.",
464 if (!print_preamble(stream, cfile.filename))
467 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
472 /* XXX - cancel printing? */
473 destroy_print_stream(stream);
475 case FRS_PRINT_ERROR:
479 if (!print_finale(stream))
482 if (!destroy_print_stream(stream)) {
484 write_failure_alert_box(print_dest, errno);
486 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
487 "Error closing print destination.");
492 print_mswin(print_dest);
494 /* trash temp file */
495 ws_remove(print_dest);
502 write_failure_alert_box(print_dest, errno);
504 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
505 "Error writing to print command: %s",
508 /* XXX - cancel printing? */
509 destroy_print_stream(stream);
513 /* trash temp file */
514 ws_remove(print_dest);
520 * Keep a static pointer to the current "Save Follow Stream As" window, if
521 * any, so that if somebody tries to do "Save"
522 * while there's already a "Save Follow Stream" window up, we just pop
523 * up the existing one, rather than creating a new one.
527 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
530 follow_info_t *follow_info = data;
532 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
533 if (follow_info->follow_save_as_w != NULL) {
534 /* There's already a dialog box; reactivate it. */
535 reactivate_window(follow_info->follow_save_as_w);
539 new_win = file_selection_new("Wireshark: Save Follow Stream As",
540 FILE_SELECTION_SAVE);
541 follow_info->follow_save_as_w = new_win;
542 #if GTK_CHECK_VERSION(2,8,0)
543 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(new_win), TRUE);
546 /* Tuck away the follow_info object into the window */
547 g_object_set_data(G_OBJECT(new_win), E_FOLLOW_INFO_KEY, follow_info);
549 g_signal_connect(new_win, "destroy", G_CALLBACK(follow_save_as_destroy_cb),
553 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
555 follow_save_as_ok_cb(new_win, new_win);
557 window_destroy(new_win);
560 /* "Run" the GtkFileChooserDialog. */
561 /* Upon exit: If "Accept" run the OK callback. */
562 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
563 /* If not accept (ie: cancel) destroy the window. */
564 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
565 /* return with a TRUE status so that the dialog window will be destroyed. */
566 /* Trying to re-run the dialog after popping up an alert box will not work */
567 /* since the user will not be able to dismiss the alert box. */
568 /* The (somewhat unfriendly) effect: the user must re-invoke the */
569 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
571 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
572 /* GtkFileChooserDialog. */
573 while (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT) {
574 if (follow_save_as_ok_cb(NULL, new_win)) {
575 break; /* we're done */
578 window_destroy(new_win);
583 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
586 follow_info_t *follow_info;
588 print_stream_t *stream = NULL;
591 to_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
593 /* Perhaps the user specified a directory instead of a file.
594 Check whether they did. */
595 if (test_for_directory(to_name) == EISDIR) {
596 /* It's a directory - set the file selection box to display that
597 directory, and leave the selection box displayed. */
598 set_last_open_dir(to_name);
600 file_selection_set_current_folder(fs, get_last_open_dir());
601 gtk_file_chooser_set_current_name(fs, "");
602 return FALSE; /* do gtk_dialog_run again */
605 follow_info = g_object_get_data(G_OBJECT(fs), E_FOLLOW_INFO_KEY);
607 if (follow_info->show_type == SHOW_RAW) {
608 /* Write the data out as raw binary data */
609 fh = ws_fopen(to_name, "wb");
611 /* Write it out as text */
612 fh = ws_fopen(to_name, "w");
615 open_failure_alert_box(to_name, errno, TRUE);
620 #if 0 /* handled by caller (for now) .... */
621 gtk_widget_hide(GTK_WIDGET(fs));
622 window_destroy(GTK_WIDGET(fs));
624 if (follow_info->show_type == SHOW_RAW) {
625 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
627 if (fclose(fh) == EOF)
628 write_failure_alert_box(to_name, errno);
636 case FRS_PRINT_ERROR:
637 write_failure_alert_box(to_name, errno);
642 stream = print_stream_text_stdio_new(fh);
643 switch (follow_read_stream(follow_info, follow_print_text,
646 if (!destroy_print_stream(stream))
647 write_failure_alert_box(to_name, errno);
652 destroy_print_stream(stream);
655 case FRS_PRINT_ERROR:
656 write_failure_alert_box(to_name, errno);
657 destroy_print_stream(stream);
662 /* Save the directory name for future file dialogs. */
663 dirname = get_dirname(to_name); /* Overwrites to_name */
664 set_last_open_dir(dirname);
670 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
672 follow_info_t *follow_info = data;
674 /* Note that we no longer have a dialog box. */
675 follow_info->follow_save_as_w = NULL;
679 follow_stream_direction_changed(GtkWidget *w, gpointer data)
681 follow_info_t *follow_info = data;
683 switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
686 follow_info->show_stream = BOTH_HOSTS;
687 follow_load_text(follow_info);
690 follow_info->show_stream = FROM_CLIENT;
691 follow_load_text(follow_info);
694 follow_info->show_stream = FROM_SERVER;
695 follow_load_text(follow_info);
700 /* Add a "follow_info_t" structure to the list. */
702 remember_follow_info(follow_info_t *follow_info)
704 follow_infos = g_list_append(follow_infos, follow_info);
707 #define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
708 /* Remove a "follow_info_t" structure from the list. */
710 forget_follow_info(follow_info_t *follow_info)
712 follow_infos = g_list_remove(follow_infos, follow_info);
716 follow_stream(gchar *title, follow_info_t *follow_info,
717 gchar *both_directions_string,
718 gchar *server_to_client_string, gchar *client_to_server_string)
720 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text;
721 GtkWidget *hbox, *bbox, *button, *radio_bt;
722 GtkWidget *stream_fr, *stream_vb;
723 GtkWidget *stream_cmb;
724 GtkTooltips *tooltips;
725 follow_stats_t stats;
727 follow_info->show_type = SHOW_RAW;
729 streamwindow = dlg_window_new(title);
731 /* needed in follow_filter_out_stream(), is there a better way? */
732 follow_info->streamwindow = streamwindow;
734 gtk_widget_set_name(streamwindow, title);
735 gtk_window_set_default_size(GTK_WINDOW(streamwindow),
736 DEF_WIDTH, DEF_HEIGHT);
737 gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
739 /* setup the container */
740 tooltips = gtk_tooltips_new ();
742 vbox = gtk_vbox_new(FALSE, 6);
743 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
746 if (incomplete_tcp_stream) {
747 stream_fr = gtk_frame_new("Stream Content (incomplete)");
749 stream_fr = gtk_frame_new("Stream Content");
751 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
752 gtk_widget_show(stream_fr);
754 stream_vb = gtk_vbox_new(FALSE, 6);
755 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
756 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
758 /* create a scrolled window for the text */
759 txt_scrollw = scrolled_window_new(NULL, NULL);
760 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
762 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
764 /* create a text box */
765 text = gtk_text_view_new();
766 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
767 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
769 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
770 follow_info->text = text;
773 hbox = gtk_hbox_new(FALSE, 1);
774 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
776 /* Create Find Button */
777 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
778 g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
779 gtk_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
780 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
782 /* Create Save As Button */
783 button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
784 g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
785 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed", NULL);
786 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
788 /* Create Print Button */
789 button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
790 g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
791 gtk_tooltips_set_tip(tooltips, button, "Print the content as currently displayed", NULL);
792 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
795 follow_stats(&stats);
797 follow_info->is_ipv6 = stats.is_ipv6;
799 stream_cmb = gtk_combo_box_new_text();
801 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
802 both_directions_string);
803 follow_info->show_stream = BOTH_HOSTS;
805 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
806 server_to_client_string);
808 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
809 client_to_server_string);
811 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect */
812 /* so callback not triggered */
814 g_signal_connect(stream_cmb, "changed",
815 G_CALLBACK(follow_stream_direction_changed),
818 gtk_tooltips_set_tip (tooltips, stream_cmb,
819 "Select the stream direction to display", NULL);
820 gtk_box_pack_start(GTK_BOX(hbox), stream_cmb, FALSE, FALSE, 0);
822 /* ASCII radio button */
823 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
824 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
825 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
826 IS_SHOW_TYPE(SHOW_ASCII));
827 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
828 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
830 follow_info->ascii_bt = radio_bt;
832 /* EBCDIC radio button */
833 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
834 (GTK_RADIO_BUTTON(radio_bt)),
836 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
837 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
838 IS_SHOW_TYPE(SHOW_EBCDIC));
839 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
840 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
842 follow_info->ebcdic_bt = radio_bt;
844 /* HEX DUMP radio button */
845 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
846 (GTK_RADIO_BUTTON(radio_bt)),
848 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
849 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
850 IS_SHOW_TYPE(SHOW_HEXDUMP));
851 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
852 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
854 follow_info->hexdump_bt = radio_bt;
856 /* C Array radio button */
857 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
858 (GTK_RADIO_BUTTON(radio_bt)),
860 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
862 IS_SHOW_TYPE(SHOW_CARRAY));
863 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
864 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
866 follow_info->carray_bt = radio_bt;
868 /* Raw radio button */
869 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
870 (GTK_RADIO_BUTTON(radio_bt)),
872 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);
873 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
874 IS_SHOW_TYPE(SHOW_RAW));
875 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
876 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
878 follow_info->raw_bt = radio_bt;
880 /* Button row: help, filter out, close button */
881 bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
882 GTK_STOCK_CLOSE, GTK_STOCK_HELP,
884 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
887 button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
888 gtk_tooltips_set_tip (tooltips, button,
889 "Build a display filter which cuts this stream from the capture", NULL);
890 g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
893 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
894 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
895 gtk_tooltips_set_tip (tooltips, button,
896 "Close the dialog and keep the current display filter", NULL);
897 gtk_widget_grab_default(button);
899 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
900 g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
901 (gpointer)HELP_FOLLOW_STREAM_DIALOG);
903 /* Tuck away the follow_info object into the window */
904 g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
906 follow_load_text(follow_info);
907 remember_follow_info(follow_info);
910 g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
911 g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
913 /* Make sure this widget gets destroyed if we quit the main loop,
914 so that if we exit, we clean up any temporary files we have
915 for "Follow TCP Stream" windows. */
916 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
918 gtk_widget_show_all(streamwindow);
919 window_present(streamwindow);
922 /* The destroy call back has the responsibility of
923 * unlinking the temporary file
924 * and freeing the filter_out_filter */
926 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
928 follow_info_t *follow_info;
929 follow_record_t *follow_record;
933 follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
935 switch(follow_info->follow_type) {
938 i = ws_unlink(follow_info->data_out_filename);
940 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", follow_info->data_out_filename, strerror(errno), errno);
945 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
947 follow_record = cur->data;
948 if(follow_record->data)
949 g_byte_array_free(follow_record->data,
952 g_free(follow_record);
955 g_list_free(follow_info->payload);
959 /* free decrypted data list*/
960 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
966 g_list_free (follow_info->payload);
970 g_free(follow_info->data_out_filename);
971 g_free(follow_info->filter_out_filter);
972 g_free((gpointer)follow_info->client_ip.data);
973 forget_follow_info(follow_info);
978 follow_show(follow_info_t *follow_info,
979 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
980 char *buffer, size_t nchars, gboolean is_server, void *arg,
981 guint32 *global_pos, guint32 *server_packet_count,
982 guint32 *client_packet_count)
986 static const gchar hexchars[16] = "0123456789abcdef";
988 switch (follow_info->show_type) {
991 /* If our native arch is ASCII, call: */
992 EBCDIC_to_ASCII(buffer, (guint) nchars);
993 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
994 return FRS_PRINT_ERROR;
998 /* If our native arch is EBCDIC, call:
999 * ASCII_TO_EBCDIC(buffer, nchars);
1001 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1002 return FRS_PRINT_ERROR;
1006 /* Don't translate, no matter what the native arch
1009 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1010 return FRS_PRINT_ERROR;
1015 while (current_pos < nchars) {
1018 gchar *cur = hexbuf, *ascii_start;
1020 /* is_server indentation : put 4 spaces at the
1021 * beginning of the string */
1022 /* XXX - We might want to prepend each line with "C" or "S" instead. */
1023 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
1024 memset(cur, ' ', 4);
1027 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
1028 /* 49 is space consumed by hex chars */
1029 ascii_start = cur + 49;
1030 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1032 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1034 hexchars[buffer[current_pos + i] & 0x0f];
1039 /* Fill it up if column isn't complete */
1040 while (cur < ascii_start)
1043 /* Now dump bytes as text */
1044 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1046 (isprint((guchar)buffer[current_pos + i]) ?
1047 buffer[current_pos + i] : '.' );
1056 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1057 return FRS_PRINT_ERROR;
1063 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1065 is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1066 if (!(*print_line_fcn_p) (initbuf, strlen(initbuf), is_server, arg))
1067 return FRS_PRINT_ERROR;
1069 while (current_pos < nchars) {
1074 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1075 /* Prepend entries with "0x" */
1076 hexbuf[cur++] = '0';
1077 hexbuf[cur++] = 'x';
1079 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1081 hexchars[buffer[current_pos + i] & 0x0f];
1083 /* Delimit array entries with a comma */
1084 if (current_pos + i + 1 < nchars)
1085 hexbuf[cur++] = ',';
1087 hexbuf[cur++] = ' ';
1090 /* Terminate the array if we are at the end */
1091 if (current_pos + i == nchars) {
1092 hexbuf[cur++] = '}';
1093 hexbuf[cur++] = ';';
1098 hexbuf[cur++] = '\n';
1100 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1101 return FRS_PRINT_ERROR;