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