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 <../isprint.h>
47 #include <ui/alert_box.h>
48 #include <ui/last_open_dir.h>
49 #include <ui/simple_dialog.h>
51 #include <wsutil/file_util.h>
53 #include "ui/gtk/color_utils.h"
54 #include "ui/gtk/stock_icons.h"
55 #include "ui/gtk/dlg_utils.h"
56 #include "ui/gtk/follow_stream.h"
57 #include "ui/gtk/font_utils.h"
58 #include "ui/gtk/file_dlg.h"
59 #include "ui/gtk/gui_utils.h"
60 #include "ui/gtk/help_dlg.h"
61 #include "ui/gtk/main.h"
62 #include "ui/gtk/old-gtk-compat.h"
65 #include "../tempfile.h"
66 #include "ui/win32/print_win32.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 gboolean follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs);
79 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
80 static void follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data);
82 GList *follow_infos = NULL;
85 follow_read_stream(follow_info_t *follow_info,
86 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
89 switch(follow_info->follow_type) {
92 return follow_read_tcp_stream(follow_info, print_line_fcn_p, arg);
95 return follow_read_udp_stream(follow_info, print_line_fcn_p, arg);
98 return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg);
101 g_assert_not_reached();
107 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
110 GtkWidget *text = arg;
111 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
114 /* While our isprint() hack is in place, we
115 * have to convert some chars to '.' in order
116 * to be able to see the data we *should* see
117 * in the GtkText widget.
121 for (i = 0; i < nchars; i++) {
122 if (buffer[i] == '\n' || buffer[i] == '\r')
124 if (! isprint((guchar)buffer[i])) {
129 gtk_text_buffer_get_end_iter(buf, &iter);
131 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
134 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
141 * XXX - for text printing, we probably want to wrap lines at 80 characters;
142 * (PostScript printing is doing this already), and perhaps put some kind of
143 * dingbat (to use the technical term) to indicate a wrapped line, along the
144 * lines of what's done when displaying this in a window, as per Warren Young's
148 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
151 print_stream_t *stream = arg;
155 /* convert non printable characters */
156 for (i = 0; i < nchars; i++) {
157 if (buffer[i] == '\n' || buffer[i] == '\r')
159 if (! isprint((guchar)buffer[i])) {
164 /* convert unterminated char array to a zero terminated string */
165 str = g_malloc(nchars + 1);
166 memcpy(str, buffer, nchars);
168 print_line(stream, /*indent*/ 0, str);
175 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
180 nwritten = fwrite(buffer, 1, nchars, fh);
181 if (nwritten != nchars)
187 /* Handles the display style toggling */
189 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
191 follow_info_t *follow_info = data;
194 * A radio button toggles when it goes on and when it goes
195 * off, so when you click a radio button two signals are
196 * delivered. We only want to reprocess the display once,
197 * so we do it only when the button goes on.
199 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
200 if (w == follow_info->ebcdic_bt)
201 follow_info->show_type = SHOW_EBCDIC;
202 else if (w == follow_info->hexdump_bt)
203 follow_info->show_type = SHOW_HEXDUMP;
204 else if (w == follow_info->carray_bt)
205 follow_info->show_type = SHOW_CARRAY;
206 else if (w == follow_info->ascii_bt)
207 follow_info->show_type = SHOW_ASCII;
208 else if (w == follow_info->raw_bt)
209 follow_info->show_type = SHOW_RAW;
210 follow_load_text(follow_info);
215 follow_load_text(follow_info_t *follow_info)
219 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
221 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
222 color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
223 color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
224 color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
225 color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
227 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
228 server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
229 &server_fg, "background-gdk",
230 &server_bg, "font-desc",
231 user_font_get_regular(), NULL);
232 client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
233 &client_fg, "background-gdk",
234 &client_bg, "font-desc",
235 user_font_get_regular(), NULL);
237 /* Delete any info already in text box */
238 gtk_text_buffer_set_text(buf, "", -1);
240 follow_read_stream(follow_info, follow_add_to_gtk_text,
245 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
247 follow_info_t *follow_info = data;
249 /* Lock out user from messing with us. (ie. don't free our data!) */
250 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
252 /* Set the display filter. */
253 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
254 follow_info->filter_out_filter);
256 /* Run the display filter so it goes in effect. */
257 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
259 /* we force a subsequent close */
260 window_destroy(follow_info->streamwindow);
266 follow_find_cb(GtkWidget * w _U_, gpointer data)
268 follow_info_t *follow_info = data;
269 GtkWidget *find_dlg_w, *main_vb, *buttons_row, *find_lb;
270 GtkWidget *find_hb, *find_text_box, *find_bt, *cancel_bt;
272 if (follow_info->find_dlg_w != NULL) {
273 /* There's already a dialog box; reactivate it. */
274 reactivate_window(follow_info->find_dlg_w);
278 /* Create the find box */
279 find_dlg_w = dlg_window_new("Wireshark: Find text");
280 gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
281 GTK_WINDOW(follow_info->streamwindow));
282 gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
283 follow_info->find_dlg_w = find_dlg_w;
285 g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
287 g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
290 /* Main vertical box */
291 main_vb = gtk_vbox_new(FALSE, 3);
292 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
293 gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
295 /* Horizontal box for find label, entry field and up/down radio
297 find_hb = gtk_hbox_new(FALSE, 3);
298 gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
299 gtk_widget_show(find_hb);
302 find_lb = gtk_label_new("Find text:");
303 gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
304 gtk_widget_show(find_lb);
307 find_text_box = gtk_entry_new();
308 gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
309 gtk_widget_set_tooltip_text(find_text_box, "Text to search for (case sensitive)");
310 gtk_widget_show(find_text_box);
313 buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
315 gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
316 find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
317 cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
319 g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
320 g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
321 window_set_cancel_button(find_dlg_w, cancel_bt,
322 window_cancel_button_cb);
324 /* Hitting return in the find field "clicks" the find button */
325 dlg_set_activate(find_text_box, find_bt);
327 /* Show the dialog */
328 gtk_widget_show_all(find_dlg_w);
329 window_present(find_dlg_w);
333 follow_find_button_cb(GtkWidget * w, gpointer data)
336 const gchar *find_string;
337 follow_info_t *follow_info = data;
338 GtkTextBuffer *buffer;
339 GtkTextIter iter, match_start, match_end;
340 GtkTextMark *last_pos_mark;
341 GtkWidget *find_string_w;
343 /* Get the text the user typed into the find field */
344 find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
345 find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
347 /* Get the buffer associated with the follow stream */
348 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
349 gtk_text_buffer_get_start_iter(buffer, &iter);
351 /* Look for the search string in the buffer */
352 last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
354 gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
356 found = gtk_text_iter_forward_search(&iter, find_string, 0,
362 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
363 last_pos_mark = gtk_text_buffer_create_mark (buffer,
366 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
368 /* We didn't find a match */
369 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
370 "%sFind text has reached the end of the followed "
371 "stream%s\n\nThe next search will start from the "
372 "beginning", simple_dialog_primary_start(),
373 simple_dialog_primary_end());
375 gtk_text_buffer_delete_mark(buffer, last_pos_mark);
381 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
383 follow_info_t *follow_info = data;
385 /* Note that we no longer have a dialog box. */
386 follow_info->find_dlg_w = NULL;
390 follow_print_stream(GtkWidget * w _U_, gpointer data)
392 print_stream_t *stream;
395 follow_info_t *follow_info = data;
397 gboolean win_printer = FALSE;
402 switch (prefs.pr_dest) {
406 /* (The code for creating a temp filename is adapted from print_dlg.c). */
407 /* We currently don't have a function in util.h to create just a tempfile */
408 /* name, so simply create a tempfile using the "official" function, */
409 /* then delete this file again. After this, the name MUST be available. */
411 /* Don't use tmpnam() or such, as this will fail under some ACL */
412 /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
413 /* Also: tmpnam is "insecure" and should not be used. */
414 tmp_fd = create_tempfile(&tmp_namebuf, "wshprint");
416 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
417 "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
421 ws_unlink(tmp_namebuf);
422 print_dest = tmp_namebuf;
425 print_dest = prefs.pr_cmd;
430 print_dest = prefs.pr_file;
433 default: /* "Can't happen" */
434 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
435 "Couldn't figure out where to send the print "
436 "job. Check your preferences.");
440 switch (prefs.pr_format) {
443 stream = print_stream_text_new(to_file, print_dest);
447 stream = print_stream_ps_new(to_file, print_dest);
451 g_assert_not_reached();
454 if (stream == NULL) {
456 open_failure_alert_box(print_dest, errno, TRUE);
458 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
459 "Couldn't run print command %s.",
465 if (!print_preamble(stream, cfile.filename))
468 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
473 /* XXX - cancel printing? */
474 destroy_print_stream(stream);
476 case FRS_PRINT_ERROR:
480 if (!print_finale(stream))
483 if (!destroy_print_stream(stream)) {
485 write_failure_alert_box(print_dest, errno);
487 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
488 "Error closing print destination.");
493 print_mswin(print_dest);
495 /* trash temp file */
496 ws_remove(print_dest);
503 write_failure_alert_box(print_dest, errno);
505 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
506 "Error writing to print command: %s",
509 /* XXX - cancel printing? */
510 destroy_print_stream(stream);
514 /* trash temp file */
515 ws_remove(print_dest);
521 * Keep a static pointer to the current "Save Follow Stream As" window, if
522 * any, so that if somebody tries to do "Save"
523 * while there's already a "Save Follow Stream" window up, we just pop
524 * up the existing one, rather than creating a new one.
528 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
531 follow_info_t *follow_info = data;
533 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
534 if (follow_info->follow_save_as_w != NULL) {
535 /* There's already a dialog box; reactivate it. */
536 reactivate_window(follow_info->follow_save_as_w);
540 new_win = file_selection_new("Wireshark: Save Follow Stream As",
541 FILE_SELECTION_SAVE);
542 follow_info->follow_save_as_w = new_win;
543 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, *direction_hbox;
722 GtkWidget *stream_cmb;
723 follow_stats_t stats;
725 follow_info->show_type = SHOW_RAW;
727 streamwindow = dlg_window_new(title);
729 /* needed in follow_filter_out_stream(), is there a better way? */
730 follow_info->streamwindow = streamwindow;
732 gtk_widget_set_name(streamwindow, title);
733 gtk_window_set_default_size(GTK_WINDOW(streamwindow),
734 DEF_WIDTH, DEF_HEIGHT);
735 gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
737 /* setup the container */
738 vbox = gtk_vbox_new(FALSE, 6);
739 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
742 if (incomplete_tcp_stream) {
743 stream_fr = gtk_frame_new("Stream Content (incomplete)");
745 stream_fr = gtk_frame_new("Stream Content");
747 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
748 gtk_widget_show(stream_fr);
750 stream_vb = gtk_vbox_new(FALSE, 6);
751 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
752 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
754 /* create a scrolled window for the text */
755 txt_scrollw = scrolled_window_new(NULL, NULL);
756 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
758 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
760 /* create a text box */
761 text = gtk_text_view_new();
762 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
763 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
765 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
766 follow_info->text = text;
769 direction_hbox = gtk_hbox_new(FALSE, 1);
770 gtk_box_pack_start(GTK_BOX(stream_vb), direction_hbox, FALSE, FALSE, 0);
772 stream_cmb = gtk_combo_box_text_new();
774 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
775 both_directions_string);
776 follow_info->show_stream = BOTH_HOSTS;
778 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
779 server_to_client_string);
781 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
782 client_to_server_string);
784 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect */
785 /* so callback not triggered */
787 g_signal_connect(stream_cmb, "changed",
788 G_CALLBACK(follow_stream_direction_changed),
791 gtk_widget_set_tooltip_text(stream_cmb, "Select the stream direction to display");
792 gtk_box_pack_start(GTK_BOX(direction_hbox), stream_cmb, TRUE, TRUE, 0);
795 hbox = gtk_hbox_new(FALSE, 1);
796 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
798 /* Create Find Button */
799 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
800 g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
801 gtk_widget_set_tooltip_text(button, "Find text in the displayed content");
802 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
804 /* Create Save As Button */
805 button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
806 g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
807 gtk_widget_set_tooltip_text(button, "Save the content as currently displayed");
808 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
810 /* Create Print Button */
811 button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
812 g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
813 gtk_widget_set_tooltip_text(button, "Print the content as currently displayed");
814 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
817 follow_stats(&stats);
819 follow_info->is_ipv6 = stats.is_ipv6;
821 /* ASCII radio button */
822 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
823 gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"ASCII\" format");
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, TRUE, TRUE, 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_widget_set_tooltip_text(radio_bt, "Stream data output in \"EBCDIC\" format");
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, TRUE, TRUE, 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_widget_set_tooltip_text(radio_bt, "Stream data output in \"Hexdump\" format");
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, TRUE, TRUE, 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_widget_set_tooltip_text(radio_bt, "Stream data output in \"C Array\" format");
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, TRUE, TRUE, 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_widget_set_tooltip_text(radio_bt,
872 "Stream data output in \"Raw\" (binary) format. "
873 "As this contains non printable characters, "
874 "the screen output will be in ASCII format");
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
876 IS_SHOW_TYPE(SHOW_RAW));
877 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
878 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
880 follow_info->raw_bt = radio_bt;
882 /* Button row: help, filter out, close button */
883 bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
884 GTK_STOCK_CLOSE, GTK_STOCK_HELP,
886 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
889 button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
890 gtk_widget_set_tooltip_text(button, "Build a display filter which cuts this stream from the capture");
891 g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
894 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
895 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
896 gtk_widget_set_tooltip_text(button, "Close the dialog and keep the current display filter");
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 is deprecated and should not be used in newly-written code.
917 This function is going to be removed in GTK+ 3.0
918 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
921 gtk_widget_show_all(streamwindow);
922 window_present(streamwindow);
925 /* The destroy call back has the responsibility of
926 * unlinking the temporary file
927 * and freeing the filter_out_filter */
929 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
931 follow_info_t *follow_info;
932 follow_record_t *follow_record;
936 follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
938 switch(follow_info->follow_type) {
941 i = ws_unlink(follow_info->data_out_filename);
943 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
944 follow_info->data_out_filename, g_strerror(errno), errno);
949 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
951 follow_record = cur->data;
952 if(follow_record->data)
953 g_byte_array_free(follow_record->data,
956 g_free(follow_record);
959 g_list_free(follow_info->payload);
963 /* free decrypted data list*/
964 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
970 g_list_free (follow_info->payload);
974 g_free(follow_info->data_out_filename);
975 g_free(follow_info->filter_out_filter);
976 g_free((gpointer)follow_info->client_ip.data);
977 forget_follow_info(follow_info);
979 gtk_widget_destroy(w);
983 follow_show(follow_info_t *follow_info,
984 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
985 char *buffer, size_t nchars, gboolean is_server, void *arg,
986 guint32 *global_pos, guint32 *server_packet_count,
987 guint32 *client_packet_count)
991 static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
993 switch (follow_info->show_type) {
996 /* If our native arch is ASCII, call: */
997 EBCDIC_to_ASCII(buffer, (guint) nchars);
998 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
999 return FRS_PRINT_ERROR;
1003 /* If our native arch is EBCDIC, call:
1004 * ASCII_TO_EBCDIC(buffer, nchars);
1006 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1007 return FRS_PRINT_ERROR;
1011 /* Don't translate, no matter what the native arch
1014 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1015 return FRS_PRINT_ERROR;
1020 while (current_pos < nchars) {
1023 gchar *cur = hexbuf, *ascii_start;
1025 /* is_server indentation : put 4 spaces at the
1026 * beginning of the string */
1027 /* XXX - We might want to prepend each line with "C" or "S" instead. */
1028 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
1029 memset(cur, ' ', 4);
1032 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
1033 /* 49 is space consumed by hex chars */
1034 ascii_start = cur + 49;
1035 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1037 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1039 hexchars[buffer[current_pos + i] & 0x0f];
1044 /* Fill it up if column isn't complete */
1045 while (cur < ascii_start)
1048 /* Now dump bytes as text */
1049 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1051 (isprint((guchar)buffer[current_pos + i]) ?
1052 buffer[current_pos + i] : '.' );
1061 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1062 return FRS_PRINT_ERROR;
1068 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1070 is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1071 if (!(*print_line_fcn_p) (initbuf, strlen(initbuf), is_server, arg))
1072 return FRS_PRINT_ERROR;
1074 while (current_pos < nchars) {
1079 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1080 /* Prepend entries with "0x" */
1081 hexbuf[cur++] = '0';
1082 hexbuf[cur++] = 'x';
1084 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1086 hexchars[buffer[current_pos + i] & 0x0f];
1088 /* Delimit array entries with a comma */
1089 if (current_pos + i + 1 < nchars)
1090 hexbuf[cur++] = ',';
1092 hexbuf[cur++] = ' ';
1095 /* Terminate the array if we are at the end */
1096 if (current_pos + i == nchars) {
1097 hexbuf[cur++] = '}';
1098 hexbuf[cur++] = ';';
1103 hexbuf[cur++] = '\n';
1105 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1106 return FRS_PRINT_ERROR;