More Win64 fixes.
[obnox/wireshark/wip.git] / gtk / follow_stream.c
1 /* follow_stream.c
2  * Common routines for following data streams
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
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.
14  *
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.
19  *
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 #include <ctype.h>
34 #include <string.h>
35
36 #include <gtk/gtk.h>
37
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>
43
44 #include <../alert_box.h>
45 #include <../isprint.h>
46 #include <../print.h>
47 #include <../simple_dialog.h>
48 #include <wsutil/file_util.h>
49
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>
58 #include "gtk/main.h"
59
60 #ifdef _WIN32
61 #include "../tempfile.h"
62 #include "gtk/print_win32.h"
63 #endif
64
65 /* static variable declarations to speed up the performance
66  * of follow_load_text and follow_add_to_gtk_text
67  */
68 static GdkColor server_fg, server_bg;
69 static GdkColor client_fg, client_bg;
70 static GtkTextTag *server_tag, *client_tag;
71
72 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
73
74 GList *follow_infos = NULL;
75
76 frs_return_t
77 follow_read_stream(follow_info_t *follow_info,
78                    gboolean (*print_line)(char *, size_t, gboolean, void *),
79                    void *arg)
80 {
81         switch(follow_info->follow_type) {
82
83         case FOLLOW_TCP :
84                 return follow_read_tcp_stream(follow_info, print_line, arg);
85
86         case FOLLOW_UDP :
87                 return follow_read_udp_stream(follow_info, print_line, arg);
88
89         case FOLLOW_SSL :
90                 return follow_read_ssl_stream(follow_info, print_line, arg);
91
92         default :
93                 g_assert_not_reached();
94                 return 0;
95         }
96 }
97
98 gboolean
99 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
100                        void *arg)
101 {
102         GtkWidget *text = arg;
103         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
104         GtkTextIter    iter;
105
106         /* While our isprint() hack is in place, we
107          * have to convert some chars to '.' in order
108          * to be able to see the data we *should* see
109          * in the GtkText widget.
110          */
111         size_t i;
112
113         for (i = 0; i < nchars; i++) {
114                 if (buffer[i] == '\n' || buffer[i] == '\r')
115                         continue;
116                 if (! isprint((guchar)buffer[i])) {
117                         buffer[i] = '.';
118                 }
119         }
120
121         gtk_text_buffer_get_end_iter(buf, &iter);
122         if (is_server) {
123                 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
124                                                  server_tag, NULL);
125         } else {
126                 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
127                                                  client_tag, NULL);
128         }
129         return TRUE;
130 }
131
132 /*
133  * XXX - for text printing, we probably want to wrap lines at 80 characters;
134  * (PostScript printing is doing this already), and perhaps put some kind of
135  * dingbat (to use the technical term) to indicate a wrapped line, along the
136  * lines of what's done when displaying this in a window, as per Warren Young's
137  * suggestion.
138  */
139 gboolean
140 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
141                   void *arg)
142 {
143         print_stream_t *stream = arg;
144         size_t i;
145         char *str;
146
147         /* convert non printable characters */
148         for (i = 0; i < nchars; i++) {
149                 if (buffer[i] == '\n' || buffer[i] == '\r')
150                         continue;
151                 if (! isprint((guchar)buffer[i])) {
152                         buffer[i] = '.';
153                 }
154         }
155
156         /* convert unterminated char array to a zero terminated string */
157         str = g_malloc(nchars + 1);
158         memcpy(str, buffer, nchars);
159         str[nchars] = 0;
160         print_line(stream, /*indent*/ 0, str);
161         g_free(str);
162
163         return TRUE;
164 }
165
166 gboolean
167 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
168 {
169         FILE *fh = arg;
170         size_t nwritten;
171
172         nwritten = fwrite(buffer, 1, nchars, fh);
173         if (nwritten != nchars)
174                 return FALSE;
175
176         return TRUE;
177 }
178
179 /* Handles the display style toggling */
180 void
181 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
182 {
183         follow_info_t   *follow_info = data;
184
185         /*
186          * A radio button toggles when it goes on and when it goes
187          * off, so when you click a radio button two signals are
188          * delivered.  We only want to reprocess the display once,
189          * so we do it only when the button goes on.
190          */
191         if (GTK_TOGGLE_BUTTON(w)->active) {
192                 if (w == follow_info->ebcdic_bt)
193                         follow_info->show_type = SHOW_EBCDIC;
194                 else if (w == follow_info->hexdump_bt)
195                         follow_info->show_type = SHOW_HEXDUMP;
196                 else if (w == follow_info->carray_bt)
197                         follow_info->show_type = SHOW_CARRAY;
198                 else if (w == follow_info->ascii_bt)
199                         follow_info->show_type = SHOW_ASCII;
200                 else if (w == follow_info->raw_bt)
201                         follow_info->show_type = SHOW_RAW;
202                 follow_load_text(follow_info);
203         }
204 }
205
206 void
207 follow_load_text(follow_info_t *follow_info)
208 {
209         GtkTextBuffer *buf;
210
211         buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
212
213         /* prepare colors one time for repeated use by follow_add_to_gtk_text */
214         color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
215         color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
216         color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
217         color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
218
219         /* prepare tags one time for repeated use by follow_add_to_gtk_text */
220         server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
221                                                 &server_fg, "background-gdk",
222                                                 &server_bg, "font-desc",
223                                                 user_font_get_regular(), NULL);
224         client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
225                                                 &client_fg, "background-gdk",
226                                                 &client_bg, "font-desc",
227                                                 user_font_get_regular(), NULL);
228
229         /* Delete any info already in text box */
230         gtk_text_buffer_set_text(buf, "", -1);
231
232         follow_read_stream(follow_info, follow_add_to_gtk_text,
233                            follow_info->text);
234 }
235
236 void
237 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
238 {
239         follow_info_t   *follow_info = data;
240
241         /* Lock out user from messing with us. (ie. don't free our data!) */
242         gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
243
244         /* Set the display filter. */
245         gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
246                            follow_info->filter_out_filter);
247
248         /* Run the display filter so it goes in effect. */
249         main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
250
251         /* we force a subsequent close */
252         window_destroy(follow_info->streamwindow);
253
254         return;
255 }
256
257 void
258 follow_find_cb(GtkWidget * w _U_, gpointer data)
259 {
260         follow_info_t           *follow_info = data;
261         GtkTooltips             *tooltips;
262         GtkWidget               *find_dlg_w, *main_vb, *buttons_row, *find_lb;
263         GtkWidget               *find_hb, *find_text_box, *find_bt, *cancel_bt;
264
265         tooltips = gtk_tooltips_new();
266
267         if (follow_info->find_dlg_w != NULL) {
268                 /* There's already a dialog box; reactivate it. */
269                 reactivate_window(follow_info->find_dlg_w);
270                 return;
271         }
272
273         /* Create the find box */
274         find_dlg_w = dlg_window_new("Wireshark: Find text");
275         gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
276                                      GTK_WINDOW(follow_info->streamwindow));
277         gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
278         follow_info->find_dlg_w = find_dlg_w;
279
280         g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
281                        follow_info);
282         g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
283                        NULL);
284
285         /* Main vertical box */
286         main_vb = gtk_vbox_new(FALSE, 3);
287         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
288         gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
289
290         /* Horizontal box for find label, entry field and up/down radio
291            buttons */
292         find_hb = gtk_hbox_new(FALSE, 3);
293         gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
294         gtk_widget_show(find_hb);
295
296         /* Find label */
297         find_lb = gtk_label_new("Find text:");
298         gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
299         gtk_widget_show(find_lb);
300
301         /* Find field */
302         find_text_box = gtk_entry_new();
303         gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
304         gtk_tooltips_set_tip(tooltips, find_text_box, "Text to search for (case sensitive)", NULL);
305         gtk_widget_show(find_text_box);
306
307         /* Buttons row */
308         buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
309                                          NULL);
310         gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
311         find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
312         cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
313
314         g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
315         g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
316         window_set_cancel_button(find_dlg_w, cancel_bt,
317                                  window_cancel_button_cb);
318
319         /* Hitting return in the find field "clicks" the find button */
320         dlg_set_activate(find_text_box, find_bt);
321
322         /* Show the dialog */
323         gtk_widget_show_all(find_dlg_w);
324         window_present(find_dlg_w);
325 }
326
327 void
328 follow_find_button_cb(GtkWidget * w, gpointer data)
329 {
330         gboolean                found;
331         const gchar             *find_string;
332         follow_info_t   *follow_info = data;
333         GtkTextBuffer   *buffer;
334         GtkTextIter             iter, match_start, match_end;
335         GtkTextMark             *last_pos_mark;
336         GtkWidget               *find_string_w;
337
338         /* Get the text the user typed into the find field */
339         find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
340         find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
341
342         /* Get the buffer associated with the follow stream */
343         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
344         gtk_text_buffer_get_start_iter(buffer, &iter);
345
346         /* Look for the search string in the buffer */
347         last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
348         if(last_pos_mark)
349                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
350
351         found = gtk_text_iter_forward_search(&iter, find_string, 0,
352                                              &match_start,
353                                              &match_end,
354                                              NULL);
355
356         if(found) {
357                 gtk_text_buffer_select_range(buffer, &match_start, &match_end);
358                 last_pos_mark = gtk_text_buffer_create_mark (buffer,
359                                                              "last_position",
360                                                              &match_end, FALSE);
361                 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
362         } else {
363                 /* We didn't find a match */
364                 simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
365                               "%sFind text has reached the end of the followed "
366                               "stream%s\n\nThe next search will start from the "
367                               "beginning", simple_dialog_primary_start(),
368                               simple_dialog_primary_end());
369                 if(last_pos_mark)
370                         gtk_text_buffer_delete_mark(buffer, last_pos_mark);
371         }
372
373 }
374
375 void
376 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
377 {
378         follow_info_t   *follow_info = data;
379
380         /* Note that we no longer have a dialog box. */
381         follow_info->find_dlg_w = NULL;
382 }
383
384 void
385 follow_print_stream(GtkWidget * w _U_, gpointer data)
386 {
387         print_stream_t  *stream;
388         gboolean         to_file;
389         char            *print_dest;
390         follow_info_t   *follow_info = data;
391 #ifdef _WIN32
392         gboolean         win_printer = FALSE;
393         int              tmp_fd;
394         char             tmp_namebuf[128+1];  /* see create_tmpfile which says [128+1]; why ? */
395 #endif
396
397         switch (prefs.pr_dest) {
398         case PR_DEST_CMD:
399 #ifdef _WIN32
400                 win_printer = TRUE;
401                 /* (The code for creating a temp filename is adapted from print_dlg.c).   */
402                 /* We currently don't have a function in util.h to create just a tempfile */
403                 /* name, so simply create a tempfile using the "official" function,       */
404                 /* then delete this file again. After this, the name MUST be available.   */
405                 /* */
406                 /* Don't use tmpnam() or such, as this will fail under some ACL           */
407                 /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358  */
408                 /* Also: tmpnam is "insecure" and should not be used.                     */
409                 tmp_fd = create_tempfile(tmp_namebuf, sizeof(tmp_namebuf), "wshprint");
410                 if(tmp_fd == -1) {
411                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
412                                       "Couldn't create a temporary file for printing.");
413                         return;
414                 }
415                 ws_close(tmp_fd);
416                 ws_unlink(tmp_namebuf);
417                 print_dest = tmp_namebuf;
418                 to_file = TRUE;
419 #else
420                 print_dest = prefs.pr_cmd;
421                 to_file = FALSE;
422 #endif
423                 break;
424         case PR_DEST_FILE:
425                 print_dest = prefs.pr_file;
426                 to_file = TRUE;
427                 break;
428         default:                        /* "Can't happen" */
429                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
430                               "Couldn't figure out where to send the print "
431                               "job. Check your preferences.");
432                 return;
433         }
434
435         switch (prefs.pr_format) {
436
437         case PR_FMT_TEXT:
438                 stream = print_stream_text_new(to_file, print_dest);
439                 break;
440
441         case PR_FMT_PS:
442                 stream = print_stream_ps_new(to_file, print_dest);
443                 break;
444
445         default:
446                 g_assert_not_reached();
447                 stream = NULL;
448         }
449         if (stream == NULL) {
450                 if (to_file) {
451                         open_failure_alert_box(print_dest, errno, TRUE);
452                 } else {
453                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
454                                       "Couldn't run print command %s.",
455                                       prefs.pr_cmd);
456                 }
457                 return;
458         }
459
460         if (!print_preamble(stream, cfile.filename))
461                 goto print_error;
462
463         switch (follow_read_stream(follow_info, follow_print_text, stream)) {
464         case FRS_OK:
465                 break;
466         case FRS_OPEN_ERROR:
467         case FRS_READ_ERROR:
468                 /* XXX - cancel printing? */
469                 destroy_print_stream(stream);
470                 return;
471         case FRS_PRINT_ERROR:
472                 goto print_error;
473         }
474
475         if (!print_finale(stream))
476                 goto print_error;
477
478         if (!destroy_print_stream(stream)) {
479                 if (to_file) {
480                         write_failure_alert_box(print_dest, errno);
481                 } else {
482                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
483                                       "Error closing print destination.");
484                 }
485         }
486 #ifdef _WIN32
487         if (win_printer) {
488                 print_mswin(print_dest);
489
490                 /* trash temp file */
491                 ws_remove(print_dest);
492         }
493 #endif
494         return;
495
496  print_error:
497         if (to_file) {
498                 write_failure_alert_box(print_dest, errno);
499         } else {
500                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
501                               "Error writing to print command: %s",
502                               strerror(errno));
503         }
504         /* XXX - cancel printing? */
505         destroy_print_stream(stream);
506
507 #ifdef _WIN32
508         if (win_printer) {
509                 /* trash temp file */
510                 ws_remove(print_dest);
511         }
512 #endif
513 }
514
515 /*
516  * Keep a static pointer to the current "Save Follow Stream As" window, if
517  * any, so that if somebody tries to do "Save"
518  * while there's already a "Save Follow Stream" window up, we just pop
519  * up the existing one, rather than creating a new one.
520  */
521 void
522 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
523 {
524         GtkWidget               *new_win;
525         follow_info_t   *follow_info = data;
526
527         if (follow_info->follow_save_as_w != NULL) {
528                 /* There's already a dialog box; reactivate it. */
529                 reactivate_window(follow_info->follow_save_as_w);
530                 return;
531         }
532
533         new_win = file_selection_new("Wireshark: Save Follow Stream As",
534                                      FILE_SELECTION_SAVE);
535         follow_info->follow_save_as_w = new_win;
536
537         /* Tuck away the follow_info object into the window */
538         g_object_set_data(G_OBJECT(new_win), E_FOLLOW_INFO_KEY, follow_info);
539
540         g_signal_connect(new_win, "destroy", G_CALLBACK(follow_save_as_destroy_cb),
541                        follow_info);
542
543         if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
544                 {
545                         follow_save_as_ok_cb(new_win, new_win);
546                 } else {
547                 window_destroy(new_win);
548         }
549 }
550
551
552 void
553 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
554 {
555         gchar           *to_name;
556         follow_info_t   *follow_info;
557         FILE            *fh;
558         print_stream_t  *stream = NULL;
559         gchar           *dirname;
560
561         to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
562
563         /* Perhaps the user specified a directory instead of a file.
564            Check whether they did. */
565         if (test_for_directory(to_name) == EISDIR) {
566                 /* It's a directory - set the file selection box to display that
567                    directory, and leave the selection box displayed. */
568                 set_last_open_dir(to_name);
569                 g_free(to_name);
570                 file_selection_set_current_folder(fs, get_last_open_dir());
571                 return;
572         }
573
574         follow_info = g_object_get_data(G_OBJECT(fs), E_FOLLOW_INFO_KEY);
575         if (follow_info->show_type == SHOW_RAW) {
576                 /* Write the data out as raw binary data */
577                 fh = ws_fopen(to_name, "wb");
578         } else {
579                 /* Write it out as text */
580                 fh = ws_fopen(to_name, "w");
581         }
582         if (fh == NULL) {
583                 open_failure_alert_box(to_name, errno, TRUE);
584                 g_free(to_name);
585                 return;
586         }
587
588         gtk_widget_hide(GTK_WIDGET(fs));
589         window_destroy(GTK_WIDGET(fs));
590
591         if (follow_info->show_type == SHOW_RAW) {
592                 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
593                 case FRS_OK:
594                         if (fclose(fh) == EOF)
595                                 write_failure_alert_box(to_name, errno);
596                         break;
597
598                 case FRS_OPEN_ERROR:
599                 case FRS_READ_ERROR:
600                         fclose(fh);
601                         break;
602
603                 case FRS_PRINT_ERROR:
604                         write_failure_alert_box(to_name, errno);
605                         fclose(fh);
606                         break;
607                 }
608         } else {
609                 stream = print_stream_text_stdio_new(fh);
610                 switch (follow_read_stream(follow_info, follow_print_text,
611                                            stream)) {
612                 case FRS_OK:
613                         if (!destroy_print_stream(stream))
614                                 write_failure_alert_box(to_name, errno);
615                         break;
616
617                 case FRS_OPEN_ERROR:
618                 case FRS_READ_ERROR:
619                         destroy_print_stream(stream);
620                         break;
621
622                 case FRS_PRINT_ERROR:
623                         write_failure_alert_box(to_name, errno);
624                         destroy_print_stream(stream);
625                         break;
626                 }
627         }
628
629         /* Save the directory name for future file dialogs. */
630         dirname = get_dirname(to_name);  /* Overwrites to_name */
631         set_last_open_dir(dirname);
632         g_free(to_name);
633 }
634
635 void
636 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
637 {
638         follow_info_t   *follow_info = data;
639
640         /* Note that we no longer have a dialog box. */
641         follow_info->follow_save_as_w = NULL;
642 }
643
644 void
645 follow_stream_direction_changed(GtkWidget *w, gpointer data)
646 {
647         follow_info_t *follow_info = data;
648
649         switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
650
651         case 0 :
652                 follow_info->show_stream = BOTH_HOSTS;
653                 follow_load_text(follow_info);
654                 break;
655         case 1 :
656                 follow_info->show_stream = FROM_CLIENT;
657                 follow_load_text(follow_info);
658                 break;
659         case 2 :
660                 follow_info->show_stream = FROM_SERVER;
661                 follow_load_text(follow_info);
662                 break;
663         }
664 }
665
666 /* Add a "follow_info_t" structure to the list. */
667 void
668 remember_follow_info(follow_info_t *follow_info)
669 {
670         follow_infos = g_list_append(follow_infos, follow_info);
671 }
672
673 #define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
674 /* Remove a "follow_info_t" structure from the list. */
675 void
676 forget_follow_info(follow_info_t *follow_info)
677 {
678         follow_infos = g_list_remove(follow_infos, follow_info);
679 }
680
681 void
682 follow_stream(gchar *title, follow_info_t *follow_info,
683               gchar *both_directions_string,
684               gchar *server_to_client_string, gchar *client_to_server_string)
685 {
686         GtkWidget       *streamwindow, *vbox, *txt_scrollw, *text;
687         GtkWidget       *hbox, *bbox, *button, *radio_bt;
688         GtkWidget       *stream_fr, *stream_vb;
689         GtkWidget       *stream_cmb;
690         GtkTooltips     *tooltips;
691         follow_stats_t stats;
692
693         follow_info->show_type = SHOW_RAW;
694
695         streamwindow = dlg_window_new(title);
696
697         /* needed in follow_filter_out_stream(), is there a better way? */
698         follow_info->streamwindow = streamwindow;
699
700         gtk_widget_set_name(streamwindow, title);
701         gtk_window_set_default_size(GTK_WINDOW(streamwindow),
702                                     DEF_WIDTH, DEF_HEIGHT);
703         gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
704
705         /* setup the container */
706         tooltips = gtk_tooltips_new ();
707
708         vbox = gtk_vbox_new(FALSE, 6);
709         gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
710
711         /* content frame */
712         if (incomplete_tcp_stream) {
713                 stream_fr = gtk_frame_new("Stream Content (incomplete)");
714         } else {
715                 stream_fr = gtk_frame_new("Stream Content");
716         }
717         gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
718         gtk_widget_show(stream_fr);
719
720         stream_vb = gtk_vbox_new(FALSE, 6);
721         gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
722         gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
723
724         /* create a scrolled window for the text */
725         txt_scrollw = scrolled_window_new(NULL, NULL);
726         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
727                                             GTK_SHADOW_IN);
728         gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
729
730         /* create a text box */
731         text = gtk_text_view_new();
732         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
733         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
734
735         gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
736         follow_info->text = text;
737
738         /* stream hbox */
739         hbox = gtk_hbox_new(FALSE, 1);
740         gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
741
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_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
746         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
747
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_tooltips_set_tip (tooltips, button, "Save the content as currently displayed", NULL);
752         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
753
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_tooltips_set_tip(tooltips, button, "Print the content as currently displayed", NULL);
758         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
759
760         /* Stream to show */
761         follow_stats(&stats);
762
763         follow_info->is_ipv6 = stats.is_ipv6;
764
765         stream_cmb = gtk_combo_box_new_text();
766
767         gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
768                                   both_directions_string);
769         follow_info->show_stream = BOTH_HOSTS;
770
771         gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
772                                   server_to_client_string);
773
774         gtk_combo_box_append_text(GTK_COMBO_BOX(stream_cmb),
775                                    client_to_server_string);
776
777         g_signal_connect(stream_cmb, "changed",
778                          G_CALLBACK(follow_stream_direction_changed),
779                          follow_info);
780
781         gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0);
782
783         gtk_tooltips_set_tip (tooltips, stream_cmb,
784                               "Select the stream direction to display", NULL);
785         gtk_box_pack_start(GTK_BOX(hbox), stream_cmb, FALSE, FALSE, 0);
786
787         /* ASCII radio button */
788         radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
789         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
790         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
791                 IS_SHOW_TYPE(SHOW_ASCII));
792         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
793         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
794                        follow_info);
795         follow_info->ascii_bt = radio_bt;
796
797         /* EBCDIC radio button */
798         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
799                                                    (GTK_RADIO_BUTTON(radio_bt)),
800                                                    "EBCDIC");
801         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
802         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
803                 IS_SHOW_TYPE(SHOW_EBCDIC));
804         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
805         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
806                        follow_info);
807         follow_info->ebcdic_bt = radio_bt;
808
809         /* HEX DUMP radio button */
810         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
811                                                    (GTK_RADIO_BUTTON(radio_bt)),
812                                                    "Hex Dump");
813         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
814         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
815                 IS_SHOW_TYPE(SHOW_HEXDUMP));
816         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
817         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
818                        follow_info);
819         follow_info->hexdump_bt = radio_bt;
820
821         /* C Array radio button */
822         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
823                                                    (GTK_RADIO_BUTTON(radio_bt)),
824                                                    "C Arrays");
825         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
826         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
827                 IS_SHOW_TYPE(SHOW_CARRAY));
828         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
829         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
830                        follow_info);
831         follow_info->carray_bt = radio_bt;
832
833         /* Raw radio button */
834         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
835                                                    (GTK_RADIO_BUTTON(radio_bt)),
836                                                    "Raw");
837         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);
838         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
839                 IS_SHOW_TYPE(SHOW_RAW));
840         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
841         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
842                        follow_info);
843         follow_info->raw_bt = radio_bt;
844
845         /* Button row: help, filter out, close button */
846         bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
847                                   GTK_STOCK_CLOSE, GTK_STOCK_HELP,
848                                   NULL);
849         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
850
851
852         button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
853         gtk_tooltips_set_tip (tooltips, button,
854                               "Build a display filter which cuts this stream from the capture", NULL);
855         g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
856                        follow_info);
857
858         button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
859         window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
860         gtk_tooltips_set_tip (tooltips, button,
861                               "Close the dialog and keep the current display filter", NULL);
862         gtk_widget_grab_default(button);
863
864         button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
865         g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
866                          (gpointer)HELP_FOLLOW_STREAM_DIALOG);
867
868         /* Tuck away the follow_info object into the window */
869         g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
870
871         follow_load_text(follow_info);
872         remember_follow_info(follow_info);
873
874
875         g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
876         g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
877
878         /* Make sure this widget gets destroyed if we quit the main loop,
879            so that if we exit, we clean up any temporary files we have
880            for "Follow TCP Stream" windows. */
881         gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
882
883         gtk_widget_show_all(streamwindow);
884         window_present(streamwindow);
885 }
886
887 /* The destroy call back has the responsibility of
888  * unlinking the temporary file
889  * and freeing the filter_out_filter */
890 static void
891 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
892 {
893         follow_info_t *follow_info;
894         follow_record_t *follow_record;
895         GList *cur;
896         int i;
897
898         follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
899
900         switch(follow_info->follow_type) {
901
902         case FOLLOW_TCP :
903                 i = ws_unlink(follow_info->data_out_filename);
904                 if(i != 0) {
905                         g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", follow_info->data_out_filename, strerror(errno), errno);
906                 }
907                 break;
908
909         case FOLLOW_UDP :
910                 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
911                         if(cur->data) {
912                                 follow_record = cur->data;
913                                 if(follow_record->data)
914                                         g_byte_array_free(follow_record->data,
915                                                           TRUE);
916
917                                 g_free(follow_record);
918                         }
919
920                 g_list_free(follow_info->payload);
921                 break;
922
923         case FOLLOW_SSL :
924                 /* free decrypted data list*/
925                 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
926                         if (cur->data)
927                                 {
928                                         g_free(cur->data);
929                                         cur->data = NULL;
930                                 }
931                 g_list_free (follow_info->payload);
932                 break;
933         }
934
935         g_free(follow_info->filter_out_filter);
936         g_free((gpointer)follow_info->client_ip.data);
937         forget_follow_info(follow_info);
938         g_free(follow_info);
939 }
940
941 frs_return_t
942 follow_show(follow_info_t *follow_info,
943             gboolean (*print_line)(char *, size_t, gboolean, void *),
944             char *buffer, size_t nchars, gboolean is_server, void *arg,
945             guint32 *global_pos, guint32 *server_packet_count,
946             guint32 *client_packet_count)
947 {
948         gchar initbuf[256];
949         guint32 current_pos;
950         static const gchar hexchars[16] = "0123456789abcdef";
951
952         switch (follow_info->show_type) {
953
954         case SHOW_EBCDIC:
955                 /* If our native arch is ASCII, call: */
956                 EBCDIC_to_ASCII(buffer, (guint) nchars);
957                 if (!(*print_line) (buffer, nchars, is_server, arg))
958                         return FRS_PRINT_ERROR;
959                 break;
960
961         case SHOW_ASCII:
962                 /* If our native arch is EBCDIC, call:
963                  * ASCII_TO_EBCDIC(buffer, nchars);
964                  */
965                 if (!(*print_line) (buffer, nchars, is_server, arg))
966                         return FRS_PRINT_ERROR;
967                 break;
968
969         case SHOW_RAW:
970                 /* Don't translate, no matter what the native arch
971                  * is.
972                  */
973                 if (!(*print_line) (buffer, nchars, is_server, arg))
974                         return FRS_PRINT_ERROR;
975                 break;
976
977         case SHOW_HEXDUMP:
978                 current_pos = 0;
979                 while (current_pos < nchars) {
980                         gchar hexbuf[256];
981                         int i;
982                         gchar *cur = hexbuf, *ascii_start;
983
984                         /* is_server indentation : put 78 spaces at the
985                          * beginning of the string */
986                         if (is_server && follow_info->show_stream == BOTH_HOSTS) {
987                                 memset(cur, ' ', 78);
988                                 cur += 78;
989                         }
990                         cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
991                         /* 49 is space consumed by hex chars */
992                         ascii_start = cur + 49;
993                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
994                                 *cur++ =
995                                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
996                                 *cur++ =
997                                         hexchars[buffer[current_pos + i] & 0x0f];
998                                 *cur++ = ' ';
999                                 if (i == 7)
1000                                         *cur++ = ' ';
1001                         }
1002                         /* Fill it up if column isn't complete */
1003                         while (cur < ascii_start)
1004                                 *cur++ = ' ';
1005
1006                         /* Now dump bytes as text */
1007                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1008                                 *cur++ =
1009                                         (isprint((guchar)buffer[current_pos + i]) ?
1010                                          buffer[current_pos + i] : '.' );
1011                                 if (i == 7) {
1012                                         *cur++ = ' ';
1013                                 }
1014                         }
1015                         current_pos += i;
1016                         (*global_pos) += i;
1017                         *cur++ = '\n';
1018                         *cur = 0;
1019                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1020                                 return FRS_PRINT_ERROR;
1021                 }
1022                 break;
1023
1024         case SHOW_CARRAY:
1025                 current_pos = 0;
1026                 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1027                            is_server ? 1 : 0,
1028                            is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1029                 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
1030                         return FRS_PRINT_ERROR;
1031
1032                 while (current_pos < nchars) {
1033                         gchar hexbuf[256];
1034                         int i, cur;
1035
1036                         cur = 0;
1037                         for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1038                                 /* Prepend entries with "0x" */
1039                                 hexbuf[cur++] = '0';
1040                                 hexbuf[cur++] = 'x';
1041                                 hexbuf[cur++] =
1042                                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1043                                 hexbuf[cur++] =
1044                                         hexchars[buffer[current_pos + i] & 0x0f];
1045
1046                                 /* Delimit array entries with a comma */
1047                                 if (current_pos + i + 1 < nchars)
1048                                         hexbuf[cur++] = ',';
1049
1050                                 hexbuf[cur++] = ' ';
1051                         }
1052
1053                         /* Terminate the array if we are at the end */
1054                         if (current_pos + i == nchars) {
1055                                 hexbuf[cur++] = '}';
1056                                 hexbuf[cur++] = ';';
1057                         }
1058
1059                         current_pos += i;
1060                         (*global_pos) += i;
1061                         hexbuf[cur++] = '\n';
1062                         hexbuf[cur] = 0;
1063                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1064                                 return FRS_PRINT_ERROR;
1065                 }
1066                 break;
1067         }
1068
1069         return FRS_OK;
1070 }