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 "win32/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_get_active(GTK_TOGGLE_BUTTON(w))) {
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, *direction_hbox;
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 direction_hbox = gtk_hbox_new(FALSE, 1);
774 gtk_box_pack_start(GTK_BOX(stream_vb), direction_hbox, FALSE, FALSE, 0);
776 stream_cmb = gtk_combo_box_new_text();
778 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
779 both_directions_string);
780 follow_info->show_stream = BOTH_HOSTS;
782 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
783 server_to_client_string);
785 gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
786 client_to_server_string);
788 gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect */
789 /* so callback not triggered */
791 g_signal_connect(stream_cmb, "changed",
792 G_CALLBACK(follow_stream_direction_changed),
795 gtk_tooltips_set_tip (tooltips, stream_cmb,
796 "Select the stream direction to display", NULL);
797 gtk_box_pack_start(GTK_BOX(direction_hbox), stream_cmb, TRUE, TRUE, 0);
800 hbox = gtk_hbox_new(FALSE, 1);
801 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
803 /* Create Find Button */
804 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
805 g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
806 gtk_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
807 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
809 /* Create Save As Button */
810 button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
811 g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
812 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed", NULL);
813 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
815 /* Create Print Button */
816 button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
817 g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
818 gtk_tooltips_set_tip(tooltips, button, "Print the content as currently displayed", NULL);
819 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
822 follow_stats(&stats);
824 follow_info->is_ipv6 = stats.is_ipv6;
826 /* ASCII radio button */
827 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
828 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
829 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
830 IS_SHOW_TYPE(SHOW_ASCII));
831 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
832 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
834 follow_info->ascii_bt = radio_bt;
836 /* EBCDIC radio button */
837 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
838 (GTK_RADIO_BUTTON(radio_bt)),
840 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
841 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
842 IS_SHOW_TYPE(SHOW_EBCDIC));
843 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
844 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
846 follow_info->ebcdic_bt = radio_bt;
848 /* HEX DUMP radio button */
849 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
850 (GTK_RADIO_BUTTON(radio_bt)),
852 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
854 IS_SHOW_TYPE(SHOW_HEXDUMP));
855 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
856 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
858 follow_info->hexdump_bt = radio_bt;
860 /* C Array radio button */
861 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
862 (GTK_RADIO_BUTTON(radio_bt)),
864 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
865 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
866 IS_SHOW_TYPE(SHOW_CARRAY));
867 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
868 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
870 follow_info->carray_bt = radio_bt;
872 /* Raw radio button */
873 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
874 (GTK_RADIO_BUTTON(radio_bt)),
876 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);
877 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
878 IS_SHOW_TYPE(SHOW_RAW));
879 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
880 g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
882 follow_info->raw_bt = radio_bt;
884 /* Button row: help, filter out, close button */
885 bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
886 GTK_STOCK_CLOSE, GTK_STOCK_HELP,
888 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
891 button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
892 gtk_tooltips_set_tip (tooltips, button,
893 "Build a display filter which cuts this stream from the capture", NULL);
894 g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
897 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
898 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
899 gtk_tooltips_set_tip (tooltips, button,
900 "Close the dialog and keep the current display filter", NULL);
901 gtk_widget_grab_default(button);
903 button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
904 g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
905 (gpointer)HELP_FOLLOW_STREAM_DIALOG);
907 /* Tuck away the follow_info object into the window */
908 g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
910 follow_load_text(follow_info);
911 remember_follow_info(follow_info);
914 g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
915 g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
917 /* Make sure this widget gets destroyed if we quit the main loop,
918 so that if we exit, we clean up any temporary files we have
919 for "Follow TCP Stream" windows. */
920 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
922 gtk_widget_show_all(streamwindow);
923 window_present(streamwindow);
926 /* The destroy call back has the responsibility of
927 * unlinking the temporary file
928 * and freeing the filter_out_filter */
930 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
932 follow_info_t *follow_info;
933 follow_record_t *follow_record;
937 follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
939 switch(follow_info->follow_type) {
942 i = ws_unlink(follow_info->data_out_filename);
944 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", 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);
982 follow_show(follow_info_t *follow_info,
983 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
984 char *buffer, size_t nchars, gboolean is_server, void *arg,
985 guint32 *global_pos, guint32 *server_packet_count,
986 guint32 *client_packet_count)
990 static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
992 switch (follow_info->show_type) {
995 /* If our native arch is ASCII, call: */
996 EBCDIC_to_ASCII(buffer, (guint) nchars);
997 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
998 return FRS_PRINT_ERROR;
1002 /* If our native arch is EBCDIC, call:
1003 * ASCII_TO_EBCDIC(buffer, nchars);
1005 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1006 return FRS_PRINT_ERROR;
1010 /* Don't translate, no matter what the native arch
1013 if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
1014 return FRS_PRINT_ERROR;
1019 while (current_pos < nchars) {
1022 gchar *cur = hexbuf, *ascii_start;
1024 /* is_server indentation : put 4 spaces at the
1025 * beginning of the string */
1026 /* XXX - We might want to prepend each line with "C" or "S" instead. */
1027 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
1028 memset(cur, ' ', 4);
1031 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
1032 /* 49 is space consumed by hex chars */
1033 ascii_start = cur + 49;
1034 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1036 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1038 hexchars[buffer[current_pos + i] & 0x0f];
1043 /* Fill it up if column isn't complete */
1044 while (cur < ascii_start)
1047 /* Now dump bytes as text */
1048 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1050 (isprint((guchar)buffer[current_pos + i]) ?
1051 buffer[current_pos + i] : '.' );
1060 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1061 return FRS_PRINT_ERROR;
1067 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1069 is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1070 if (!(*print_line_fcn_p) (initbuf, strlen(initbuf), is_server, arg))
1071 return FRS_PRINT_ERROR;
1073 while (current_pos < nchars) {
1078 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1079 /* Prepend entries with "0x" */
1080 hexbuf[cur++] = '0';
1081 hexbuf[cur++] = 'x';
1083 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1085 hexchars[buffer[current_pos + i] & 0x0f];
1087 /* Delimit array entries with a comma */
1088 if (current_pos + i + 1 < nchars)
1089 hexbuf[cur++] = ',';
1091 hexbuf[cur++] = ' ';
1094 /* Terminate the array if we are at the end */
1095 if (current_pos + i == nchars) {
1096 hexbuf[cur++] = '}';
1097 hexbuf[cur++] = ';';
1102 hexbuf[cur++] = '\n';
1104 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
1105 return FRS_PRINT_ERROR;