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