add parameter dcerpc_info to PIDL_dissect_ipv?address()
[wireshark-wip.git] / ui / gtk / follow_stream.c
blobf43fdaf7f5f365fb0032d0b43da5e55d6c1d48da
1 /* follow_stream.c
2 * Common routines for following data streams
4 * $Id$
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,
23 * USA.
26 #include "config.h"
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <ctype.h>
32 #include <string.h>
34 #include <gtk/gtk.h>
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"
62 #ifdef _WIN32
63 #include "wsutil/tempfile.h"
64 #include "ui/win32/print_win32.h"
65 #endif
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;
82 static frs_return_t
83 follow_read_stream(follow_info_t *follow_info,
84 gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
85 void *arg)
87 switch(follow_info->follow_type) {
89 case FOLLOW_TCP :
90 return follow_read_tcp_stream(follow_info, print_line_fcn_p, arg);
92 case FOLLOW_UDP :
93 return follow_read_udp_stream(follow_info, print_line_fcn_p, arg);
95 case FOLLOW_SSL :
96 return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg);
98 default :
99 g_assert_not_reached();
100 return (frs_return_t)0;
104 gboolean
105 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_from_server,
106 void *arg)
108 GtkWidget *text = (GtkWidget *)arg;
109 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
110 GtkTextIter iter;
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.
117 size_t i;
119 for (i = 0; i < nchars; i++) {
120 if (buffer[i] == '\n' || buffer[i] == '\r')
121 continue;
122 if (! isprint((guchar)buffer[i])) {
123 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,
130 server_tag, NULL);
131 } else {
132 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
133 client_tag, NULL);
135 return TRUE;
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
143 * suggestion.
145 static gboolean
146 follow_print_text(char *buffer, size_t nchars, gboolean is_from_server _U_,
147 void *arg)
149 print_stream_t *stream = (print_stream_t *)arg;
150 size_t i;
151 char *str;
153 /* convert non printable characters */
154 for (i = 0; i < nchars; i++) {
155 if (buffer[i] == '\n' || buffer[i] == '\r')
156 continue;
157 if (! isprint((guchar)buffer[i])) {
158 buffer[i] = '.';
162 /* convert unterminated char array to a zero terminated string */
163 str = (char *)g_malloc(nchars + 1);
164 memcpy(str, buffer, nchars);
165 str[nchars] = 0;
166 print_line(stream, /*indent*/ 0, str);
167 g_free(str);
169 return TRUE;
172 static gboolean
173 follow_write_raw(char *buffer, size_t nchars, gboolean is_from_server _U_, void *arg)
175 FILE *fh = (FILE *)arg;
176 size_t nwritten;
178 nwritten = fwrite(buffer, 1, nchars, fh);
179 if (nwritten != nchars)
180 return FALSE;
182 return TRUE;
185 /* Handles the display style toggling */
186 static void
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);
212 void
213 follow_load_text(follow_info_t *follow_info)
215 GtkTextBuffer *buf;
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,
239 follow_info->text);
242 void
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);
260 return;
263 static void
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);
273 return;
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),
285 follow_info);
286 g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
287 NULL);
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
295 buttons */
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);
300 /* Find label */
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);
305 /* Find field */
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);
311 /* Buttons row */
312 buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
313 NULL);
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);
331 static void
332 follow_find_button_cb(GtkWidget * w, gpointer data)
334 gboolean found;
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");
352 if(last_pos_mark)
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,
356 &match_start,
357 &match_end,
358 NULL);
360 if(found) {
361 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
362 last_pos_mark = gtk_text_buffer_create_mark (buffer,
363 "last_position",
364 &match_end, FALSE);
365 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
366 } else {
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());
373 if(last_pos_mark)
374 gtk_text_buffer_delete_mark(buffer, last_pos_mark);
379 static void
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;
388 static void
389 follow_print_stream(GtkWidget * w _U_, gpointer data)
391 print_stream_t *stream;
392 gboolean to_file;
393 const char *print_dest;
394 follow_info_t *follow_info =(follow_info_t *) data;
395 #ifdef _WIN32
396 gboolean win_printer = FALSE;
397 int tmp_fd;
398 char *tmp_namebuf;
399 #endif
401 switch (prefs.pr_dest) {
402 case PR_DEST_CMD:
403 #ifdef _WIN32
404 win_printer = TRUE;
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. */
409 /* */
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");
414 if(tmp_fd == -1) {
415 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
416 "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
417 return;
419 ws_close(tmp_fd);
420 ws_unlink(tmp_namebuf);
421 print_dest = tmp_namebuf;
422 to_file = TRUE;
423 #else
424 print_dest = prefs.pr_cmd;
425 to_file = FALSE;
426 #endif
427 break;
428 case PR_DEST_FILE:
429 print_dest = prefs.pr_file;
430 to_file = TRUE;
431 break;
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.");
436 return;
439 switch (prefs.pr_format) {
441 case PR_FMT_TEXT:
442 stream = print_stream_text_new(to_file, print_dest);
443 break;
445 case PR_FMT_PS:
446 stream = print_stream_ps_new(to_file, print_dest);
447 break;
449 default:
450 g_assert_not_reached();
451 stream = NULL;
453 if (stream == NULL) {
454 if (to_file) {
455 open_failure_alert_box(print_dest, errno, TRUE);
456 } else {
457 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
458 "Couldn't run print command %s.",
459 prefs.pr_cmd);
461 return;
464 if (!print_preamble(stream, cfile.filename, wireshark_svnversion))
465 goto print_error;
467 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
468 case FRS_OK:
469 break;
470 case FRS_OPEN_ERROR:
471 case FRS_READ_ERROR:
472 /* XXX - cancel printing? */
473 destroy_print_stream(stream);
474 return;
475 case FRS_PRINT_ERROR:
476 goto print_error;
479 if (!print_finale(stream))
480 goto print_error;
482 if (!destroy_print_stream(stream)) {
483 if (to_file) {
484 write_failure_alert_box(print_dest, errno);
485 } else {
486 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
487 "Error closing print destination.");
490 #ifdef _WIN32
491 if (win_printer) {
492 print_mswin(print_dest);
494 /* trash temp file */
495 ws_remove(print_dest);
497 #endif
498 return;
500 print_error:
501 if (to_file) {
502 write_failure_alert_box(print_dest, errno);
503 } else {
504 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
505 "Error writing to print command: %s",
506 g_strerror(errno));
508 /* XXX - cancel printing? */
509 destroy_print_stream(stream);
511 #ifdef _WIN32
512 if (win_printer) {
513 /* trash temp file */
514 ws_remove(print_dest);
516 #endif
519 static char *
520 gtk_follow_save_as_file(GtkWidget *caller)
522 GtkWidget *new_win;
523 char *pathname;
525 new_win = file_selection_new("Wireshark: Save Follow Stream As",
526 GTK_WINDOW(caller),
527 FILE_SELECTION_SAVE);
528 pathname = file_selection_run(new_win);
529 if (pathname == NULL) {
530 /* User cancelled or closed the dialog. */
531 return NULL;
534 /* We've crosed the Rubicon; get rid of the dialog box. */
535 window_destroy(new_win);
537 return pathname;
540 static gboolean
541 follow_save_as_ok_cb(gchar *to_name, follow_info_t *follow_info)
543 FILE *fh;
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");
549 } else {
550 /* Write it out as text */
551 fh = ws_fopen(to_name, "w");
553 if (fh == NULL) {
554 open_failure_alert_box(to_name, errno, TRUE);
555 return FALSE;
558 if (follow_info->show_type == SHOW_RAW) {
559 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
560 case FRS_OK:
561 if (fclose(fh) == EOF) {
562 write_failure_alert_box(to_name, errno);
563 return FALSE;
565 break;
567 case FRS_OPEN_ERROR:
568 case FRS_READ_ERROR:
569 fclose(fh);
570 return FALSE;
572 case FRS_PRINT_ERROR:
573 write_failure_alert_box(to_name, errno);
574 fclose(fh);
575 return FALSE;
577 } else {
578 stream = print_stream_text_stdio_new(fh);
579 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
580 case FRS_OK:
581 if (!destroy_print_stream(stream)) {
582 write_failure_alert_box(to_name, errno);
583 return FALSE;
585 break;
587 case FRS_OPEN_ERROR:
588 case FRS_READ_ERROR:
589 destroy_print_stream(stream);
590 return FALSE;
592 case FRS_PRINT_ERROR:
593 write_failure_alert_box(to_name, errno);
594 destroy_print_stream(stream);
595 return FALSE;
599 return TRUE;
602 static void
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;
607 char *pathname;
610 * Loop until the user either selects a file or gives up.
612 for (;;) {
613 pathname = gtk_follow_save_as_file(caller);
614 if (pathname == NULL) {
615 /* User gave up. */
616 break;
618 if (follow_save_as_ok_cb(pathname, follow_info)) {
619 /* We succeeded. */
620 g_free(pathname);
621 break;
623 /* Dump failed; let the user select another file or give up. */
624 g_free(pathname);
628 static void
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))) {
635 case 0 :
636 follow_info->show_stream = BOTH_HOSTS;
637 follow_load_text(follow_info);
638 break;
639 case 1 :
640 follow_info->show_stream = FROM_SERVER;
641 follow_load_text(follow_info);
642 break;
643 case 2 :
644 follow_info->show_stream = FROM_CLIENT;
645 follow_load_text(follow_info);
646 break;
650 /* Add a "follow_info_t" structure to the list. */
651 static void
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. */
659 static void
660 forget_follow_info(follow_info_t *follow_info)
662 follow_infos = g_list_remove(follow_infos, follow_info);
665 void
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);
691 /* content frame */
692 if (incomplete_tcp_stream) {
693 stream_fr = gtk_frame_new("Stream Content (incomplete)");
694 } else {
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;
717 /* direction hbox */
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);
738 /* stream hbox */
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);
760 /* Stream to show */
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)),
775 "EBCDIC");
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)),
784 "Hex Dump");
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)),
793 "C Arrays");
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)),
802 "Raw");
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,
814 NULL);
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 */
855 static void
856 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
858 follow_info_t *follow_info;
859 follow_record_t *follow_record;
860 GList *cur;
861 int i;
863 follow_info = (follow_info_t *)g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
865 switch(follow_info->follow_type) {
867 case FOLLOW_TCP :
868 i = ws_unlink(follow_info->data_out_filename);
869 if(i != 0) {
870 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
871 follow_info->data_out_filename, g_strerror(errno), errno);
873 break;
875 case FOLLOW_UDP :
876 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
877 if(cur->data) {
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);
886 break;
888 case FOLLOW_SSL :
889 /* free decrypted data list*/
890 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
891 if (cur->data)
893 g_free(cur->data);
894 cur->data = NULL;
896 g_list_free (follow_info->payload);
897 break;
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);
904 g_free(follow_info);
905 gtk_widget_destroy(w);
908 frs_return_t
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)
915 gchar initbuf[256];
916 guint32 current_pos;
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) {
921 case SHOW_EBCDIC:
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;
926 break;
928 case SHOW_ASCII:
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;
934 break;
936 case SHOW_RAW:
937 /* Don't translate, no matter what the native arch
938 * is.
940 if (!(*print_line_fcn_p) (buffer, nchars, is_from_server, arg))
941 return FRS_PRINT_ERROR;
942 break;
944 case SHOW_HEXDUMP:
945 current_pos = 0;
946 while (current_pos < nchars) {
947 gchar hexbuf[256];
948 int i;
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) {
955 memset(cur, ' ', 4);
956 cur += 4;
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++) {
962 *cur++ =
963 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
964 *cur++ =
965 hexchars[buffer[current_pos + i] & 0x0f];
966 *cur++ = ' ';
967 if (i == 7)
968 *cur++ = ' ';
970 /* Fill it up if column isn't complete */
971 while (cur < ascii_start)
972 *cur++ = ' ';
974 /* Now dump bytes as text */
975 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
976 *cur++ =
977 (isprint((guchar)buffer[current_pos + i]) ?
978 buffer[current_pos + i] : '.' );
979 if (i == 7) {
980 *cur++ = ' ';
983 current_pos += i;
984 (*global_pos) += i;
985 *cur++ = '\n';
986 *cur = 0;
987 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_from_server, arg))
988 return FRS_PRINT_ERROR;
990 break;
992 case SHOW_CARRAY:
993 current_pos = 0;
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) {
1001 gchar hexbuf[256];
1002 int i, cur;
1004 cur = 0;
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++] = ';';
1025 current_pos += i;
1026 (*global_pos) += i;
1027 hexbuf[cur++] = '\n';
1028 hexbuf[cur] = 0;
1029 if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_from_server, arg))
1030 return FRS_PRINT_ERROR;
1032 break;
1035 return FRS_OK;
1039 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1041 * Local variables:
1042 * c-basic-offset: 4
1043 * tab-width: 8
1044 * indent-tabs-mode: nil
1045 * End:
1047 * vi: set shiftwidth=4 tabstop=8 expandtab:
1048 * :indentSize=4:tabSize=8:noTabs=true: