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,
36 #include <epan/addr_resolv.h>
37 #include <epan/follow.h>
38 #include <epan/filesystem.h>
39 #include <epan/prefs.h>
40 #include <epan/charsets.h>
42 #include <../isprint.h>
43 #include <epan/print.h>
45 #include <ui/alert_box.h>
46 #include <ui/last_open_dir.h>
47 #include <ui/simple_dialog.h>
49 #include <wsutil/file_util.h>
51 #include "ui/gtk/color_utils.h"
52 #include "ui/gtk/stock_icons.h"
53 #include "ui/gtk/dlg_utils.h"
54 #include "ui/gtk/follow_stream.h"
55 #include "ui/gtk/font_utils.h"
56 #include "ui/gtk/file_dlg.h"
57 #include "ui/gtk/gui_utils.h"
58 #include "ui/gtk/help_dlg.h"
59 #include "ui/gtk/main.h"
60 #include "ui/gtk/old-gtk-compat.h"
63 #include "wsutil/tempfile.h"
64 #include "ui/win32/print_win32.h"
67 #include "version_info.h"
69 /* static variable declarations to speed up the performance
70 * of follow_load_text and follow_add_to_gtk_text
72 static GdkColor server_fg
, server_bg
;
73 static GdkColor client_fg
, client_bg
;
74 static GtkTextTag
*server_tag
, *client_tag
;
76 static void follow_find_destroy_cb(GtkWidget
* win _U_
, gpointer data
);
77 static void follow_find_button_cb(GtkWidget
* w
, gpointer data
);
78 static void follow_destroy_cb(GtkWidget
*w
, gpointer data _U_
);
80 GList
*follow_infos
= NULL
;
83 follow_read_stream(follow_info_t
*follow_info
,
84 gboolean (*print_line_fcn_p
)(char *, size_t, gboolean
, void *),
87 switch(follow_info
->follow_type
) {
90 return follow_read_tcp_stream(follow_info
, print_line_fcn_p
, arg
);
93 return follow_read_udp_stream(follow_info
, print_line_fcn_p
, arg
);
96 return follow_read_ssl_stream(follow_info
, print_line_fcn_p
, arg
);
99 g_assert_not_reached();
100 return (frs_return_t
)0;
105 follow_add_to_gtk_text(char *buffer
, size_t nchars
, gboolean is_from_server
,
108 GtkWidget
*text
= (GtkWidget
*)arg
;
109 GtkTextBuffer
*buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
112 /* While our isprint() hack is in place, we
113 * have to convert some chars to '.' in order
114 * to be able to see the data we *should* see
115 * in the GtkText widget.
119 for (i
= 0; i
< nchars
; i
++) {
120 if (buffer
[i
] == '\n' || buffer
[i
] == '\r')
122 if (! isprint((guchar
)buffer
[i
])) {
127 gtk_text_buffer_get_end_iter(buf
, &iter
);
128 if (is_from_server
) {
129 gtk_text_buffer_insert_with_tags(buf
, &iter
, buffer
, (gint
) nchars
,
132 gtk_text_buffer_insert_with_tags(buf
, &iter
, buffer
, (gint
) nchars
,
139 * XXX - for text printing, we probably want to wrap lines at 80 characters;
140 * (PostScript printing is doing this already), and perhaps put some kind of
141 * dingbat (to use the technical term) to indicate a wrapped line, along the
142 * lines of what's done when displaying this in a window, as per Warren Young's
146 follow_print_text(char *buffer
, size_t nchars
, gboolean is_from_server _U_
,
149 print_stream_t
*stream
= (print_stream_t
*)arg
;
153 /* convert non printable characters */
154 for (i
= 0; i
< nchars
; i
++) {
155 if (buffer
[i
] == '\n' || buffer
[i
] == '\r')
157 if (! isprint((guchar
)buffer
[i
])) {
162 /* convert unterminated char array to a zero terminated string */
163 str
= (char *)g_malloc(nchars
+ 1);
164 memcpy(str
, buffer
, nchars
);
166 print_line(stream
, /*indent*/ 0, str
);
173 follow_write_raw(char *buffer
, size_t nchars
, gboolean is_from_server _U_
, void *arg
)
175 FILE *fh
= (FILE *)arg
;
178 nwritten
= fwrite(buffer
, 1, nchars
, fh
);
179 if (nwritten
!= nchars
)
185 /* Handles the display style toggling */
187 follow_charset_toggle_cb(GtkWidget
* w _U_
, gpointer data
)
189 follow_info_t
*follow_info
= (follow_info_t
*)data
;
192 * A radio button toggles when it goes on and when it goes
193 * off, so when you click a radio button two signals are
194 * delivered. We only want to reprocess the display once,
195 * so we do it only when the button goes on.
197 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w
))) {
198 if (w
== follow_info
->ebcdic_bt
)
199 follow_info
->show_type
= SHOW_EBCDIC
;
200 else if (w
== follow_info
->hexdump_bt
)
201 follow_info
->show_type
= SHOW_HEXDUMP
;
202 else if (w
== follow_info
->carray_bt
)
203 follow_info
->show_type
= SHOW_CARRAY
;
204 else if (w
== follow_info
->ascii_bt
)
205 follow_info
->show_type
= SHOW_ASCII
;
206 else if (w
== follow_info
->raw_bt
)
207 follow_info
->show_type
= SHOW_RAW
;
208 follow_load_text(follow_info
);
213 follow_load_text(follow_info_t
*follow_info
)
217 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info
->text
));
219 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
220 color_t_to_gdkcolor(&server_fg
, &prefs
.st_server_fg
);
221 color_t_to_gdkcolor(&server_bg
, &prefs
.st_server_bg
);
222 color_t_to_gdkcolor(&client_fg
, &prefs
.st_client_fg
);
223 color_t_to_gdkcolor(&client_bg
, &prefs
.st_client_bg
);
225 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
226 server_tag
= gtk_text_buffer_create_tag(buf
, NULL
, "foreground-gdk",
227 &server_fg
, "background-gdk",
228 &server_bg
, "font-desc",
229 user_font_get_regular(), NULL
);
230 client_tag
= gtk_text_buffer_create_tag(buf
, NULL
, "foreground-gdk",
231 &client_fg
, "background-gdk",
232 &client_bg
, "font-desc",
233 user_font_get_regular(), NULL
);
235 /* Delete any info already in text box */
236 gtk_text_buffer_set_text(buf
, "", -1);
238 follow_read_stream(follow_info
, follow_add_to_gtk_text
,
243 follow_filter_out_stream(GtkWidget
* w _U_
, gpointer data
)
245 follow_info_t
*follow_info
= (follow_info_t
*)data
;
247 /* Lock out user from messing with us. (ie. don't free our data!) */
248 gtk_widget_set_sensitive(follow_info
->streamwindow
, FALSE
);
250 /* Set the display filter. */
251 gtk_entry_set_text(GTK_ENTRY(follow_info
->filter_te
),
252 follow_info
->filter_out_filter
);
254 /* Run the display filter so it goes in effect. */
255 main_filter_packets(&cfile
, follow_info
->filter_out_filter
, FALSE
);
257 /* we force a subsequent close */
258 window_destroy(follow_info
->streamwindow
);
264 follow_find_cb(GtkWidget
* w _U_
, gpointer data
)
266 follow_info_t
*follow_info
= (follow_info_t
*)data
;
267 GtkWidget
*find_dlg_w
, *main_vb
, *buttons_row
, *find_lb
;
268 GtkWidget
*find_hb
, *find_text_box
, *find_bt
, *cancel_bt
;
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_widget_set_size_request(find_dlg_w
, 225, -1);
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
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 3, FALSE
);
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
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3, FALSE
);
297 gtk_box_pack_start(GTK_BOX (main_vb
), find_hb
, TRUE
, TRUE
, 0);
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_widget_set_tooltip_text(find_text_box
, "Text to search for (case sensitive)");
309 gtk_widget_show(find_text_box
);
312 buttons_row
= dlg_button_row_new(GTK_STOCK_FIND
, GTK_STOCK_CANCEL
,
314 gtk_box_pack_start(GTK_BOX(main_vb
), buttons_row
, TRUE
, TRUE
, 0);
315 find_bt
= (GtkWidget
*)g_object_get_data(G_OBJECT(buttons_row
), GTK_STOCK_FIND
);
316 cancel_bt
= (GtkWidget
*)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
= (follow_info_t
*)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
, (GtkTextSearchFlags
)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
= (follow_info_t
*)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
;
393 const char *print_dest
;
394 follow_info_t
*follow_info
=(follow_info_t
*) 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
, wireshark_svnversion
))
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 gtk_follow_save_as_file(GtkWidget
*caller
)
525 new_win
= file_selection_new("Wireshark: Save Follow Stream As",
527 FILE_SELECTION_SAVE
);
528 pathname
= file_selection_run(new_win
);
529 if (pathname
== NULL
) {
530 /* User cancelled or closed the dialog. */
534 /* We've crosed the Rubicon; get rid of the dialog box. */
535 window_destroy(new_win
);
541 follow_save_as_ok_cb(gchar
*to_name
, follow_info_t
*follow_info
)
544 print_stream_t
*stream
;
546 if (follow_info
->show_type
== SHOW_RAW
) {
547 /* Write the data out as raw binary data */
548 fh
= ws_fopen(to_name
, "wb");
550 /* Write it out as text */
551 fh
= ws_fopen(to_name
, "w");
554 open_failure_alert_box(to_name
, errno
, TRUE
);
558 if (follow_info
->show_type
== SHOW_RAW
) {
559 switch (follow_read_stream(follow_info
, follow_write_raw
, fh
)) {
561 if (fclose(fh
) == EOF
) {
562 write_failure_alert_box(to_name
, errno
);
572 case FRS_PRINT_ERROR
:
573 write_failure_alert_box(to_name
, errno
);
578 stream
= print_stream_text_stdio_new(fh
);
579 switch (follow_read_stream(follow_info
, follow_print_text
, stream
)) {
581 if (!destroy_print_stream(stream
)) {
582 write_failure_alert_box(to_name
, errno
);
589 destroy_print_stream(stream
);
592 case FRS_PRINT_ERROR
:
593 write_failure_alert_box(to_name
, errno
);
594 destroy_print_stream(stream
);
603 follow_save_as_cmd_cb(GtkWidget
*w
, gpointer data
)
605 GtkWidget
*caller
= gtk_widget_get_toplevel(w
);
606 follow_info_t
*follow_info
= (follow_info_t
*)data
;
610 * Loop until the user either selects a file or gives up.
613 pathname
= gtk_follow_save_as_file(caller
);
614 if (pathname
== NULL
) {
618 if (follow_save_as_ok_cb(pathname
, follow_info
)) {
623 /* Dump failed; let the user select another file or give up. */
629 follow_stream_direction_changed(GtkWidget
*w
, gpointer data
)
631 follow_info_t
*follow_info
= (follow_info_t
*)data
;
633 switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w
))) {
636 follow_info
->show_stream
= BOTH_HOSTS
;
637 follow_load_text(follow_info
);
640 follow_info
->show_stream
= FROM_SERVER
;
641 follow_load_text(follow_info
);
644 follow_info
->show_stream
= FROM_CLIENT
;
645 follow_load_text(follow_info
);
650 /* Add a "follow_info_t" structure to the list. */
652 remember_follow_info(follow_info_t
*follow_info
)
654 follow_infos
= g_list_append(follow_infos
, follow_info
);
657 #define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
658 /* Remove a "follow_info_t" structure from the list. */
660 forget_follow_info(follow_info_t
*follow_info
)
662 follow_infos
= g_list_remove(follow_infos
, follow_info
);
666 follow_stream(const gchar
*title
, follow_info_t
*follow_info
,
667 gchar
*both_directions_string
,
668 gchar
*server_to_client_string
, gchar
*client_to_server_string
)
670 GtkWidget
*streamwindow
, *vbox
, *txt_scrollw
, *text
;
671 GtkWidget
*hbox
, *bbox
, *button
, *radio_bt
;
672 GtkWidget
*stream_fr
, *stream_vb
, *direction_hbox
;
673 GtkWidget
*stream_cmb
;
674 follow_stats_t stats
;
676 follow_info
->show_type
= SHOW_RAW
;
678 streamwindow
= dlg_window_new(title
);
680 /* needed in follow_filter_out_stream(), is there a better way? */
681 follow_info
->streamwindow
= streamwindow
;
683 gtk_widget_set_name(streamwindow
, title
);
684 gtk_window_set_default_size(GTK_WINDOW(streamwindow
), DEF_WIDTH
, DEF_HEIGHT
);
685 gtk_container_set_border_width(GTK_CONTAINER(streamwindow
), 6);
687 /* setup the container */
688 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6, FALSE
);
689 gtk_container_add(GTK_CONTAINER(streamwindow
), vbox
);
692 if (incomplete_tcp_stream
) {
693 stream_fr
= gtk_frame_new("Stream Content (incomplete)");
695 stream_fr
= gtk_frame_new("Stream Content");
697 gtk_box_pack_start(GTK_BOX (vbox
), stream_fr
, TRUE
, TRUE
, 0);
698 gtk_widget_show(stream_fr
);
700 stream_vb
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6, FALSE
);
701 gtk_container_set_border_width( GTK_CONTAINER(stream_vb
) , 6);
702 gtk_container_add(GTK_CONTAINER(stream_fr
), stream_vb
);
704 /* create a scrolled window for the text */
705 txt_scrollw
= scrolled_window_new(NULL
, NULL
);
706 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw
), GTK_SHADOW_IN
);
707 gtk_box_pack_start(GTK_BOX(stream_vb
), txt_scrollw
, TRUE
, TRUE
, 0);
709 /* create a text box */
710 text
= gtk_text_view_new();
711 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
712 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
714 gtk_container_add(GTK_CONTAINER(txt_scrollw
), text
);
715 follow_info
->text
= text
;
718 direction_hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 1, FALSE
);
719 gtk_box_pack_start(GTK_BOX(stream_vb
), direction_hbox
, FALSE
, FALSE
, 0);
721 stream_cmb
= gtk_combo_box_text_new();
723 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb
), both_directions_string
);
724 follow_info
->show_stream
= BOTH_HOSTS
;
726 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb
), server_to_client_string
);
728 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb
), client_to_server_string
);
730 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb
), 0); /* Do this before signal_connect */
731 /* so callback not triggered */
733 g_signal_connect(stream_cmb
, "changed", G_CALLBACK(follow_stream_direction_changed
), follow_info
);
735 gtk_widget_set_tooltip_text(stream_cmb
, "Select the stream direction to display");
736 gtk_box_pack_start(GTK_BOX(direction_hbox
), stream_cmb
, TRUE
, TRUE
, 0);
739 hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 1, FALSE
);
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_widget_set_tooltip_text(button
, "Find text in the displayed content");
746 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 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_widget_set_tooltip_text(button
, "Save the content as currently displayed");
752 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 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_widget_set_tooltip_text(button
, "Print the content as currently displayed");
758 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
761 follow_stats(&stats
);
763 follow_info
->is_ipv6
= stats
.is_ipv6
;
765 /* ASCII radio button */
766 radio_bt
= gtk_radio_button_new_with_label(NULL
, "ASCII");
767 gtk_widget_set_tooltip_text(radio_bt
, "Stream data output in \"ASCII\" format");
768 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt
), IS_SHOW_TYPE(SHOW_ASCII
));
769 gtk_box_pack_start(GTK_BOX(hbox
), radio_bt
, TRUE
, TRUE
, 0);
770 g_signal_connect(radio_bt
, "toggled", G_CALLBACK(follow_charset_toggle_cb
), follow_info
);
771 follow_info
->ascii_bt
= radio_bt
;
773 /* EBCDIC radio button */
774 radio_bt
= gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt
)),
776 gtk_widget_set_tooltip_text(radio_bt
, "Stream data output in \"EBCDIC\" format");
777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt
), IS_SHOW_TYPE(SHOW_EBCDIC
));
778 gtk_box_pack_start(GTK_BOX(hbox
), radio_bt
, TRUE
, TRUE
, 0);
779 g_signal_connect(radio_bt
, "toggled", G_CALLBACK(follow_charset_toggle_cb
), follow_info
);
780 follow_info
->ebcdic_bt
= radio_bt
;
782 /* HEX DUMP radio button */
783 radio_bt
= gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt
)),
785 gtk_widget_set_tooltip_text(radio_bt
, "Stream data output in \"Hexdump\" format");
786 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt
), IS_SHOW_TYPE(SHOW_HEXDUMP
));
787 gtk_box_pack_start(GTK_BOX(hbox
), radio_bt
, TRUE
, TRUE
, 0);
788 g_signal_connect(radio_bt
, "toggled", G_CALLBACK(follow_charset_toggle_cb
),follow_info
);
789 follow_info
->hexdump_bt
= radio_bt
;
791 /* C Array radio button */
792 radio_bt
= gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt
)),
794 gtk_widget_set_tooltip_text(radio_bt
, "Stream data output in \"C Array\" format");
795 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt
), IS_SHOW_TYPE(SHOW_CARRAY
));
796 gtk_box_pack_start(GTK_BOX(hbox
), radio_bt
, TRUE
, TRUE
, 0);
797 g_signal_connect(radio_bt
, "toggled", G_CALLBACK(follow_charset_toggle_cb
), follow_info
);
798 follow_info
->carray_bt
= radio_bt
;
800 /* Raw radio button */
801 radio_bt
= gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt
)),
803 gtk_widget_set_tooltip_text(radio_bt
,
804 "Stream data output in \"Raw\" (binary) format. "
805 "As this contains non printable characters, "
806 "the screen output will be in ASCII format");
807 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt
), IS_SHOW_TYPE(SHOW_RAW
));
808 gtk_box_pack_start(GTK_BOX(hbox
), radio_bt
, TRUE
, TRUE
, 0);
809 g_signal_connect(radio_bt
, "toggled", G_CALLBACK(follow_charset_toggle_cb
), follow_info
);
810 follow_info
->raw_bt
= radio_bt
;
812 /* Button row: help, filter out, close button */
813 bbox
= dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM
, GTK_STOCK_CLOSE
, GTK_STOCK_HELP
,
815 gtk_box_pack_start(GTK_BOX(vbox
), bbox
, FALSE
, FALSE
, 5);
818 button
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), WIRESHARK_STOCK_FILTER_OUT_STREAM
);
819 gtk_widget_set_tooltip_text(button
, "Build a display filter which cuts this stream from the capture");
820 g_signal_connect(button
, "clicked", G_CALLBACK(follow_filter_out_stream
), follow_info
);
822 button
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_CLOSE
);
823 window_set_cancel_button(streamwindow
, button
, window_cancel_button_cb
);
824 gtk_widget_set_tooltip_text(button
, "Close the dialog and keep the current display filter");
825 gtk_widget_grab_default(button
);
827 button
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_HELP
);
828 g_signal_connect(button
, "clicked", G_CALLBACK(topic_cb
), (gpointer
)HELP_FOLLOW_STREAM_DIALOG
);
830 /* Tuck away the follow_info object into the window */
831 g_object_set_data(G_OBJECT(streamwindow
), E_FOLLOW_INFO_KEY
, follow_info
);
833 follow_load_text(follow_info
);
834 remember_follow_info(follow_info
);
837 g_signal_connect(streamwindow
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
838 g_signal_connect(streamwindow
, "destroy", G_CALLBACK(follow_destroy_cb
), NULL
);
840 /* Make sure this widget gets destroyed if we quit the main loop,
841 so that if we exit, we clean up any temporary files we have
842 for "Follow TCP Stream" windows.
843 gtk_quit_add_destroy is deprecated and should not be used in newly-written code.
844 This function is going to be removed in GTK+ 3.0
845 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
848 gtk_widget_show_all(streamwindow
);
849 window_present(streamwindow
);
852 /* The destroy call back has the responsibility of
853 * unlinking the temporary file
854 * and freeing the filter_out_filter */
856 follow_destroy_cb(GtkWidget
*w
, gpointer data _U_
)
858 follow_info_t
*follow_info
;
859 follow_record_t
*follow_record
;
863 follow_info
= (follow_info_t
*)g_object_get_data(G_OBJECT(w
), E_FOLLOW_INFO_KEY
);
865 switch(follow_info
->follow_type
) {
868 i
= ws_unlink(follow_info
->data_out_filename
);
870 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
871 follow_info
->data_out_filename
, g_strerror(errno
), errno
);
876 for(cur
= follow_info
->payload
; cur
; cur
= g_list_next(cur
))
878 follow_record
= (follow_record_t
*)cur
->data
;
879 if(follow_record
->data
)
880 g_byte_array_free(follow_record
->data
, TRUE
);
882 g_free(follow_record
);
885 g_list_free(follow_info
->payload
);
889 /* free decrypted data list*/
890 for (cur
= follow_info
->payload
; cur
; cur
= g_list_next(cur
))
896 g_list_free (follow_info
->payload
);
900 g_free(follow_info
->data_out_filename
);
901 g_free(follow_info
->filter_out_filter
);
902 g_free((gpointer
)follow_info
->client_ip
.data
);
903 forget_follow_info(follow_info
);
905 gtk_widget_destroy(w
);
909 follow_show(follow_info_t
*follow_info
,
910 gboolean (*print_line_fcn_p
)(char *, size_t, gboolean
, void *),
911 char *buffer
, size_t nchars
, gboolean is_from_server
, void *arg
,
912 guint32
*global_pos
, guint32
*server_packet_count
,
913 guint32
*client_packet_count
)
917 static const gchar hexchars
[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
919 switch (follow_info
->show_type
) {
922 /* If our native arch is ASCII, call: */
923 EBCDIC_to_ASCII(buffer
, (guint
) nchars
);
924 if (!(*print_line_fcn_p
) (buffer
, nchars
, is_from_server
, arg
))
925 return FRS_PRINT_ERROR
;
929 /* If our native arch is EBCDIC, call:
930 * ASCII_TO_EBCDIC(buffer, nchars);
932 if (!(*print_line_fcn_p
) (buffer
, nchars
, is_from_server
, arg
))
933 return FRS_PRINT_ERROR
;
937 /* Don't translate, no matter what the native arch
940 if (!(*print_line_fcn_p
) (buffer
, nchars
, is_from_server
, arg
))
941 return FRS_PRINT_ERROR
;
946 while (current_pos
< nchars
) {
949 gchar
*cur
= hexbuf
, *ascii_start
;
951 /* is_from_server indentation : put 4 spaces at the
952 * beginning of the string */
953 /* XXX - We might want to prepend each line with "C" or "S" instead. */
954 if (is_from_server
&& follow_info
->show_stream
== BOTH_HOSTS
) {
958 cur
+= g_snprintf(cur
, 20, "%08X ", *global_pos
);
959 /* 49 is space consumed by hex chars */
960 ascii_start
= cur
+ 49;
961 for (i
= 0; i
< 16 && current_pos
+ i
< nchars
; i
++) {
963 hexchars
[(buffer
[current_pos
+ i
] & 0xf0) >> 4];
965 hexchars
[buffer
[current_pos
+ i
] & 0x0f];
970 /* Fill it up if column isn't complete */
971 while (cur
< ascii_start
)
974 /* Now dump bytes as text */
975 for (i
= 0; i
< 16 && current_pos
+ i
< nchars
; i
++) {
977 (isprint((guchar
)buffer
[current_pos
+ i
]) ?
978 buffer
[current_pos
+ i
] : '.' );
987 if (!(*print_line_fcn_p
) (hexbuf
, strlen(hexbuf
), is_from_server
, arg
))
988 return FRS_PRINT_ERROR
;
994 g_snprintf(initbuf
, sizeof(initbuf
), "char peer%d_%d[] = {\n",
995 is_from_server
? 1 : 0,
996 is_from_server
? (*server_packet_count
)++ : (*client_packet_count
)++);
997 if (!(*print_line_fcn_p
) (initbuf
, strlen(initbuf
), is_from_server
, arg
))
998 return FRS_PRINT_ERROR
;
1000 while (current_pos
< nchars
) {
1005 for (i
= 0; i
< 8 && current_pos
+ i
< nchars
; i
++) {
1006 /* Prepend entries with "0x" */
1007 hexbuf
[cur
++] = '0';
1008 hexbuf
[cur
++] = 'x';
1009 hexbuf
[cur
++] = hexchars
[(buffer
[current_pos
+ i
] & 0xf0) >> 4];
1010 hexbuf
[cur
++] = hexchars
[buffer
[current_pos
+ i
] & 0x0f];
1012 /* Delimit array entries with a comma */
1013 if (current_pos
+ i
+ 1 < nchars
)
1014 hexbuf
[cur
++] = ',';
1016 hexbuf
[cur
++] = ' ';
1019 /* Terminate the array if we are at the end */
1020 if (current_pos
+ i
== nchars
) {
1021 hexbuf
[cur
++] = '}';
1022 hexbuf
[cur
++] = ';';
1027 hexbuf
[cur
++] = '\n';
1029 if (!(*print_line_fcn_p
) (hexbuf
, strlen(hexbuf
), is_from_server
, arg
))
1030 return FRS_PRINT_ERROR
;
1039 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1044 * indent-tabs-mode: nil
1047 * vi: set shiftwidth=4 tabstop=8 expandtab:
1048 * :indentSize=4:tabSize=8:noTabs=true: