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