follow_stream: Don't load follow text twice when initially building the window;
[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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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         gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect  */
778                                                                 /*  so callback not triggered     */
779
780         g_signal_connect(stream_cmb, "changed",
781                          G_CALLBACK(follow_stream_direction_changed),
782                          follow_info);
783
784         gtk_tooltips_set_tip (tooltips, stream_cmb,
785                               "Select the stream direction to display", NULL);
786         gtk_box_pack_start(GTK_BOX(hbox), stream_cmb, FALSE, FALSE, 0);
787
788         /* ASCII radio button */
789         radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
790         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
791         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
792                 IS_SHOW_TYPE(SHOW_ASCII));
793         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
794         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
795                        follow_info);
796         follow_info->ascii_bt = radio_bt;
797
798         /* EBCDIC radio button */
799         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
800                                                    (GTK_RADIO_BUTTON(radio_bt)),
801                                                    "EBCDIC");
802         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
803         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
804                 IS_SHOW_TYPE(SHOW_EBCDIC));
805         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
806         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
807                        follow_info);
808         follow_info->ebcdic_bt = radio_bt;
809
810         /* HEX DUMP radio button */
811         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
812                                                    (GTK_RADIO_BUTTON(radio_bt)),
813                                                    "Hex Dump");
814         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
815         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
816                 IS_SHOW_TYPE(SHOW_HEXDUMP));
817         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
818         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
819                        follow_info);
820         follow_info->hexdump_bt = radio_bt;
821
822         /* C Array radio button */
823         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
824                                                    (GTK_RADIO_BUTTON(radio_bt)),
825                                                    "C Arrays");
826         gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
827         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
828                 IS_SHOW_TYPE(SHOW_CARRAY));
829         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
830         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
831                        follow_info);
832         follow_info->carray_bt = radio_bt;
833
834         /* Raw radio button */
835         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
836                                                    (GTK_RADIO_BUTTON(radio_bt)),
837                                                    "Raw");
838         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);
839         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
840                 IS_SHOW_TYPE(SHOW_RAW));
841         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
842         g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
843                        follow_info);
844         follow_info->raw_bt = radio_bt;
845
846         /* Button row: help, filter out, close button */
847         bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
848                                   GTK_STOCK_CLOSE, GTK_STOCK_HELP,
849                                   NULL);
850         gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
851
852
853         button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
854         gtk_tooltips_set_tip (tooltips, button,
855                               "Build a display filter which cuts this stream from the capture", NULL);
856         g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
857                        follow_info);
858
859         button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
860         window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
861         gtk_tooltips_set_tip (tooltips, button,
862                               "Close the dialog and keep the current display filter", NULL);
863         gtk_widget_grab_default(button);
864
865         button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
866         g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
867                          (gpointer)HELP_FOLLOW_STREAM_DIALOG);
868
869         /* Tuck away the follow_info object into the window */
870         g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
871
872         follow_load_text(follow_info);
873         remember_follow_info(follow_info);
874
875
876         g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
877         g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
878
879         /* Make sure this widget gets destroyed if we quit the main loop,
880            so that if we exit, we clean up any temporary files we have
881            for "Follow TCP Stream" windows. */
882         gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
883
884         gtk_widget_show_all(streamwindow);
885         window_present(streamwindow);
886 }
887
888 /* The destroy call back has the responsibility of
889  * unlinking the temporary file
890  * and freeing the filter_out_filter */
891 static void
892 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
893 {
894         follow_info_t *follow_info;
895         follow_record_t *follow_record;
896         GList *cur;
897         int i;
898
899         follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
900
901         switch(follow_info->follow_type) {
902
903         case FOLLOW_TCP :
904                 i = ws_unlink(follow_info->data_out_filename);
905                 if(i != 0) {
906                         g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", follow_info->data_out_filename, strerror(errno), errno);
907                 }
908                 break;
909
910         case FOLLOW_UDP :
911                 for(cur = follow_info->payload; cur; cur = g_list_next(cur))
912                         if(cur->data) {
913                                 follow_record = cur->data;
914                                 if(follow_record->data)
915                                         g_byte_array_free(follow_record->data,
916                                                           TRUE);
917
918                                 g_free(follow_record);
919                         }
920
921                 g_list_free(follow_info->payload);
922                 break;
923
924         case FOLLOW_SSL :
925                 /* free decrypted data list*/
926                 for (cur = follow_info->payload; cur; cur = g_list_next(cur))
927                         if (cur->data)
928                                 {
929                                         g_free(cur->data);
930                                         cur->data = NULL;
931                                 }
932                 g_list_free (follow_info->payload);
933                 break;
934         }
935
936         g_free(follow_info->filter_out_filter);
937         g_free((gpointer)follow_info->client_ip.data);
938         forget_follow_info(follow_info);
939         g_free(follow_info);
940 }
941
942 frs_return_t
943 follow_show(follow_info_t *follow_info,
944             gboolean (*print_line)(char *, size_t, gboolean, void *),
945             char *buffer, size_t nchars, gboolean is_server, void *arg,
946             guint32 *global_pos, guint32 *server_packet_count,
947             guint32 *client_packet_count)
948 {
949         gchar initbuf[256];
950         guint32 current_pos;
951         static const gchar hexchars[16] = "0123456789abcdef";
952
953         switch (follow_info->show_type) {
954
955         case SHOW_EBCDIC:
956                 /* If our native arch is ASCII, call: */
957                 EBCDIC_to_ASCII(buffer, (guint) nchars);
958                 if (!(*print_line) (buffer, nchars, is_server, arg))
959                         return FRS_PRINT_ERROR;
960                 break;
961
962         case SHOW_ASCII:
963                 /* If our native arch is EBCDIC, call:
964                  * ASCII_TO_EBCDIC(buffer, nchars);
965                  */
966                 if (!(*print_line) (buffer, nchars, is_server, arg))
967                         return FRS_PRINT_ERROR;
968                 break;
969
970         case SHOW_RAW:
971                 /* Don't translate, no matter what the native arch
972                  * is.
973                  */
974                 if (!(*print_line) (buffer, nchars, is_server, arg))
975                         return FRS_PRINT_ERROR;
976                 break;
977
978         case SHOW_HEXDUMP:
979                 current_pos = 0;
980                 while (current_pos < nchars) {
981                         gchar hexbuf[256];
982                         int i;
983                         gchar *cur = hexbuf, *ascii_start;
984
985                         /* is_server indentation : put 78 spaces at the
986                          * beginning of the string */
987                         if (is_server && follow_info->show_stream == BOTH_HOSTS) {
988                                 memset(cur, ' ', 78);
989                                 cur += 78;
990                         }
991                         cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
992                         /* 49 is space consumed by hex chars */
993                         ascii_start = cur + 49;
994                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
995                                 *cur++ =
996                                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
997                                 *cur++ =
998                                         hexchars[buffer[current_pos + i] & 0x0f];
999                                 *cur++ = ' ';
1000                                 if (i == 7)
1001                                         *cur++ = ' ';
1002                         }
1003                         /* Fill it up if column isn't complete */
1004                         while (cur < ascii_start)
1005                                 *cur++ = ' ';
1006
1007                         /* Now dump bytes as text */
1008                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1009                                 *cur++ =
1010                                         (isprint((guchar)buffer[current_pos + i]) ?
1011                                          buffer[current_pos + i] : '.' );
1012                                 if (i == 7) {
1013                                         *cur++ = ' ';
1014                                 }
1015                         }
1016                         current_pos += i;
1017                         (*global_pos) += i;
1018                         *cur++ = '\n';
1019                         *cur = 0;
1020                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1021                                 return FRS_PRINT_ERROR;
1022                 }
1023                 break;
1024
1025         case SHOW_CARRAY:
1026                 current_pos = 0;
1027                 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1028                            is_server ? 1 : 0,
1029                            is_server ? (*server_packet_count)++ : (*client_packet_count)++);
1030                 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
1031                         return FRS_PRINT_ERROR;
1032
1033                 while (current_pos < nchars) {
1034                         gchar hexbuf[256];
1035                         int i, cur;
1036
1037                         cur = 0;
1038                         for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1039                                 /* Prepend entries with "0x" */
1040                                 hexbuf[cur++] = '0';
1041                                 hexbuf[cur++] = 'x';
1042                                 hexbuf[cur++] =
1043                                         hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1044                                 hexbuf[cur++] =
1045                                         hexchars[buffer[current_pos + i] & 0x0f];
1046
1047                                 /* Delimit array entries with a comma */
1048                                 if (current_pos + i + 1 < nchars)
1049                                         hexbuf[cur++] = ',';
1050
1051                                 hexbuf[cur++] = ' ';
1052                         }
1053
1054                         /* Terminate the array if we are at the end */
1055                         if (current_pos + i == nchars) {
1056                                 hexbuf[cur++] = '}';
1057                                 hexbuf[cur++] = ';';
1058                         }
1059
1060                         current_pos += i;
1061                         (*global_pos) += i;
1062                         hexbuf[cur++] = '\n';
1063                         hexbuf[cur] = 0;
1064                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
1065                                 return FRS_PRINT_ERROR;
1066                 }
1067                 break;
1068         }
1069
1070         return FRS_OK;
1071 }