enable postcript printing if selected in prefs,
[obnox/wireshark/wip.git] / gtk / follow_dlg.c
1 /* follow_dlg.c
2  *
3  * $Id: follow_dlg.c,v 1.64 2004/05/27 23:09:09 ulfl Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@ethereal.com>
7  * Copyright 2000 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <gtk/gtk.h>
29
30 #include <stdio.h>
31 #include <string.h>
32
33 #ifdef HAVE_IO_H
34 #include <io.h>                 /* open/close on win32 */
35 #endif
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #include <ctype.h>
42
43 #include "color.h"
44 #include "color_utils.h"
45 #include "file.h"
46 #include "follow_dlg.h"
47 #include "follow.h"
48 #include "dlg_utils.h"
49 #include "keys.h"
50 #include "globals.h"
51 #include "gtkglobals.h"
52 #include "main.h"
53 #include "alert_box.h"
54 #include "simple_dialog.h"
55 #include "packet-ipv6.h"
56 #include "prefs.h"
57 #include <epan/resolv.h>
58 #include "util.h"
59 #include "ui_util.h"
60 #include <epan/epan_dissect.h>
61 #include <epan/filesystem.h>
62 #include "compat_macros.h"
63 #include "ipproto.h"
64 #include "tap_menu.h"
65 #include "print_mswin.h"
66
67 /* Show Stream */
68 typedef enum {
69         FROM_CLIENT,
70         FROM_SERVER,
71         BOTH_HOSTS
72 } show_stream_t;
73
74 /* Show Type */
75 typedef enum {
76         SHOW_ASCII,
77         SHOW_EBCDIC,
78         SHOW_HEXDUMP,
79         SHOW_CARRAY
80 } show_type_t;
81
82 typedef struct {
83         show_stream_t   show_stream;
84         show_type_t     show_type;
85         char            data_out_filename[128 + 1];
86         GtkWidget       *text;
87         GtkWidget       *ascii_bt;
88         GtkWidget       *ebcdic_bt;
89         GtkWidget       *hexdump_bt;
90         GtkWidget       *carray_bt;
91         GtkWidget       *follow_save_as_w;
92         gboolean        is_ipv6;
93         char            *filter_out_filter;
94         GtkWidget       *filter_te;
95         GtkWidget       *streamwindow;
96 } follow_info_t;
97
98 static void follow_destroy_cb(GtkWidget * win, gpointer data);
99 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
100 static void follow_load_text(follow_info_t *follow_info);
101 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
102 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
103 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
104 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
105 static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
106 static void follow_stream_om_both(GtkWidget * w, gpointer data);
107 static void follow_stream_om_client(GtkWidget * w, gpointer data);
108 static void follow_stream_om_server(GtkWidget * w, gpointer data);
109
110
111 /* With MSVC and a libethereal.dll, we need a special declaration. */
112 ETH_VAR_IMPORT FILE *data_out_file;
113
114 #define E_FOLLOW_INFO_KEY "follow_info_key"
115
116 /* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
117    so we can redraw them all if the colors or font changes. */
118 static GList *follow_infos;
119
120 /* Add a "follow_info_t" structure to the list. */
121 static void
122 remember_follow_info(follow_info_t *follow_info)
123 {
124   follow_infos = g_list_append(follow_infos, follow_info);
125 }
126
127 /* Remove a "follow_info_t" structure from the list. */
128 static void
129 forget_follow_info(follow_info_t *follow_info)
130 {
131   follow_infos = g_list_remove(follow_infos, follow_info);
132 }
133
134 static void
135 follow_redraw(gpointer data, gpointer user_data _U_)
136 {
137         follow_load_text((follow_info_t *)data);
138 }
139
140 /* Redraw the text in all "Follow TCP Stream" windows. */
141 void
142 follow_redraw_all(void)
143 {
144         g_list_foreach(follow_infos, follow_redraw, NULL);
145 }
146
147 /* Follow the TCP stream, if any, to which the last packet that we called
148    a dissection routine on belongs (this might be the most recently
149    selected packet, or it might be the last packet in the file). */
150 void
151 follow_stream_cb(GtkWidget * w, gpointer data _U_)
152 {
153         GtkWidget       *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
154         GtkWidget       *hbox, *button_hbox, *button, *radio_bt;
155     GtkWidget   *stream_fr, *stream_vb;
156         GtkWidget       *stream_om, *stream_menu, *stream_mi;
157         GtkTooltips *tooltips;
158         int                 tmp_fd;
159         gchar           *follow_filter;
160         const gchar     *previous_filter;
161     int             filter_out_filter_len;
162         const char      *hostname0, *hostname1;
163         char            *port0, *port1;
164         char            string[128];
165         follow_tcp_stats_t stats;
166         follow_info_t   *follow_info;
167
168         /* we got tcp so we can follow */
169         if (cfile.edt->pi.ipproto != 6) {
170                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
171                               "Error following stream.  Please make\n"
172                               "sure you have a TCP packet selected.");
173                 return;
174         }
175
176         follow_info = g_new0(follow_info_t, 1);
177
178         /* Create a temporary file into which to dump the reassembled data
179            from the TCP stream, and set "data_out_file" to refer to it, so
180            that the TCP code will write to it.
181
182            XXX - it might be nicer to just have the TCP code directly
183            append stuff to the text widget for the TCP stream window,
184            if we can arrange that said window not pop up until we're
185            done. */
186         tmp_fd = create_tempfile(follow_info->data_out_filename,
187                         sizeof follow_info->data_out_filename, "follow");
188
189         if (tmp_fd == -1) {
190             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
191                           "Could not create temporary file %s: %s",
192                           follow_info->data_out_filename, strerror(errno));
193             g_free(follow_info);
194             return;
195         }
196
197         data_out_file = fdopen(tmp_fd, "wb");
198         if (data_out_file == NULL) {
199             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
200                           "Could not create temporary file %s: %s",
201                           follow_info->data_out_filename, strerror(errno));
202             close(tmp_fd);
203             unlink(follow_info->data_out_filename);
204             g_free(follow_info);
205             return;
206         }
207
208         /* Create a new filter that matches all packets in the TCP stream,
209            and set the display filter entry accordingly */
210         reset_tcp_reassembly();
211         follow_filter = build_follow_filter(&cfile.edt->pi);
212
213         /* Set the display filter entry accordingly */
214         filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
215
216         /* needed in follow_filter_out_stream(), is there a better way? */
217         follow_info->filter_te = filter_te;
218
219         /* save previous filter, const since we're not supposed to alter */
220         previous_filter =
221             (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
222
223         /* allocate our new filter. API claims g_malloc terminates program on failure */
224         /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
225         filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
226         follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
227
228         /* append the negation */
229         if(strlen(previous_filter)) {
230             g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
231             "%s and !(%s)", previous_filter, follow_filter);
232         } else {
233             g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
234             "!(%s)", follow_filter);
235         }
236
237
238         gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
239
240         /* Run the display filter so it goes in effect - even if it's the
241            same as the previous display filter. */
242         main_filter_packets(&cfile, follow_filter, TRUE);
243
244         /* Free the filter string, as we're done with it. */
245         g_free(follow_filter);
246
247         /* The data_out_file should now be full of the streams information */
248         fclose(data_out_file);
249
250         /* The data_out_filename file now has all the text that was in the session */
251         streamwindow = dlg_window_new("Follow TCP stream");
252
253         /* needed in follow_filter_out_stream(), is there a better way? */
254         follow_info->streamwindow = streamwindow;
255
256         gtk_widget_set_name(streamwindow, "TCP stream window");
257     gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
258         gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
259
260         /* setup the container */
261     tooltips = gtk_tooltips_new ();
262
263         vbox = gtk_vbox_new(FALSE, 6);
264         gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
265
266     /* content frame */
267         if (incomplete_tcp_stream) {
268         stream_fr = gtk_frame_new("Stream Content (incomplete)");
269         } else {
270         stream_fr = gtk_frame_new("Stream Content");
271         }
272         gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
273         gtk_widget_show(stream_fr);
274
275     stream_vb = gtk_vbox_new(FALSE, 6);
276         gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
277         gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
278
279         /* create a scrolled window for the text */
280         txt_scrollw = scrolled_window_new(NULL, NULL);
281 #if GTK_MAJOR_VERSION >= 2
282     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
283                                    GTK_SHADOW_IN);
284 #endif
285         gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
286
287         /* create a text box */
288 #if GTK_MAJOR_VERSION < 2
289         text = gtk_text_new(NULL, NULL);
290         gtk_text_set_editable(GTK_TEXT(text), FALSE);
291 #else
292         text = gtk_text_view_new();
293         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
294 #endif
295         gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
296         follow_info->text = text;
297
298
299         /* stream hbox */
300         hbox = gtk_hbox_new(FALSE, 1);
301         gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
302
303         /* Create Save As Button */
304     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
305         SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
306     gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
307         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
308
309         /* Create Print Button */
310     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
311         SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
312     gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
313         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
314
315         /* Stream to show */
316         follow_tcp_stats(&stats);
317
318         if (stats.is_ipv6) {
319           struct e_in6_addr ipaddr;
320           memcpy(&ipaddr, stats.ip_address[0], 16);
321           hostname0 = get_hostname6(&ipaddr);
322           memcpy(&ipaddr, stats.ip_address[0], 16);
323           hostname1 = get_hostname6(&ipaddr);
324         } else {
325           guint32 ipaddr;
326           memcpy(&ipaddr, stats.ip_address[0], 4);
327           hostname0 = get_hostname(ipaddr);
328           memcpy(&ipaddr, stats.ip_address[1], 4);
329           hostname1 = get_hostname(ipaddr);
330         }
331
332         port0 = get_tcp_port(stats.tcp_port[0]);
333         port1 = get_tcp_port(stats.tcp_port[1]);
334
335         follow_info->is_ipv6 = stats.is_ipv6;
336
337         stream_om = gtk_option_menu_new();
338         stream_menu = gtk_menu_new();
339
340         /* Both Hosts */
341         g_snprintf(string, sizeof(string),
342                  "Entire conversation (%u bytes)",
343                  stats.bytes_written[0] + stats.bytes_written[1]);
344         stream_mi = gtk_menu_item_new_with_label(string);
345         SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
346                        follow_info);
347         gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
348         gtk_widget_show(stream_mi);
349         follow_info->show_stream = BOTH_HOSTS;
350
351         /* Host 0 --> Host 1 */
352         g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
353                  hostname0, port0, hostname1, port1,
354                  stats.bytes_written[0]);
355         stream_mi = gtk_menu_item_new_with_label(string);
356         SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
357                        follow_info);
358         gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
359         gtk_widget_show(stream_mi);
360
361         /* Host 1 --> Host 0 */
362         g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
363                  hostname1, port1, hostname0, port0,
364                  stats.bytes_written[1]);
365         stream_mi = gtk_menu_item_new_with_label(string);
366         SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
367                        follow_info);
368         gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
369         gtk_widget_show(stream_mi);
370
371         gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
372         /* Set history to 0th item, i.e., the first item. */
373         gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
374     gtk_tooltips_set_tip (tooltips, stream_om,
375         "Select the stream direction to display", NULL);
376         gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
377
378         /* ASCII radio button */
379         radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
380         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
381         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
382         SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
383                        follow_info);
384         follow_info->ascii_bt = radio_bt;
385         follow_info->show_type = SHOW_ASCII;
386
387         /* EBCDIC radio button */
388         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
389                                             (GTK_RADIO_BUTTON(radio_bt)),
390                                             "EBCDIC");
391         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
392         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
393         SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
394                        follow_info);
395         follow_info->ebcdic_bt = radio_bt;
396
397         /* HEX DUMP radio button */
398         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
399                                             (GTK_RADIO_BUTTON(radio_bt)),
400                                             "Hex Dump");
401         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
402         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
403         SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
404                        follow_info);
405         follow_info->hexdump_bt = radio_bt;
406
407         /* C Array radio button */
408         radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
409                                             (GTK_RADIO_BUTTON(radio_bt)),
410                                             "C Arrays");
411         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
412         gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
413         SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
414                        follow_info);
415         follow_info->carray_bt = radio_bt;
416
417         /* button hbox */
418         button_hbox = gtk_hbutton_box_new();
419         gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
420     gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
421     gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
422
423         /* Create exclude stream button */
424         button = gtk_button_new_with_label("Filter out this stream");
425         SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
426     gtk_tooltips_set_tip (tooltips, button,
427         "Build a display filter which cuts this stream from the capture", NULL);
428         gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
429
430         /* Create Close Button */
431     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
432     gtk_tooltips_set_tip (tooltips, button,
433         "Close the dialog and keep the current display filter", NULL);
434         gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
435     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
436
437     window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
438
439         /* Tuck away the follow_info object into the window */
440         OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
441
442         follow_load_text(follow_info);
443         remember_follow_info(follow_info);
444
445         data_out_file = NULL;
446
447     SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
448         SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
449
450         /* Make sure this widget gets destroyed if we quit the main loop,
451            so that if we exit, we clean up any temporary files we have
452            for "Follow TCP Stream" windows. */
453         gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
454
455         gtk_widget_show_all(streamwindow);
456         window_present(streamwindow);
457 }
458
459 /* The destroy call back has the responsibility of
460  * unlinking the temporary file
461  * and freeing the filter_out_filter */
462 static void
463 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
464 {
465         follow_info_t   *follow_info;
466     int i;
467
468         follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
469         i = unlink(follow_info->data_out_filename);
470     if(i != 0) {
471         g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)", 
472             follow_info->data_out_filename, strerror(errno), errno);        
473     }
474         g_free(follow_info->filter_out_filter);
475         forget_follow_info(follow_info);
476         g_free(follow_info);
477 }
478
479 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
480  * 3 different functions here? */
481 static void
482 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
483 {
484         follow_info_t   *follow_info = data;
485         follow_info->show_stream = BOTH_HOSTS;
486         follow_load_text(follow_info);
487 }
488
489 static void
490 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
491 {
492         follow_info_t   *follow_info = data;
493         follow_info->show_stream = FROM_CLIENT;
494         follow_load_text(follow_info);
495 }
496
497 static void
498 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
499 {
500         follow_info_t   *follow_info = data;
501         follow_info->show_stream = FROM_SERVER;
502         follow_load_text(follow_info);
503 }
504
505
506 /* Handles the ASCII/EBCDIC toggling */
507 static void
508 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
509 {
510     follow_info_t       *follow_info = data;
511
512         if (GTK_TOGGLE_BUTTON(follow_info->ebcdic_bt)->active)
513                 follow_info->show_type = SHOW_EBCDIC;
514         else if (GTK_TOGGLE_BUTTON(follow_info->hexdump_bt)->active)
515                 follow_info->show_type = SHOW_HEXDUMP;
516         else if (GTK_TOGGLE_BUTTON(follow_info->carray_bt)->active)
517                 follow_info->show_type = SHOW_CARRAY;
518         else if (GTK_TOGGLE_BUTTON(follow_info->ascii_bt)->active)
519                 follow_info->show_type = SHOW_ASCII;
520         else
521                 g_assert_not_reached();
522
523         follow_load_text(follow_info);
524 }
525
526 #define FLT_BUF_SIZE 1024
527
528 typedef enum {
529         FRS_OK,
530         FRS_OPEN_ERROR,
531         FRS_READ_ERROR,
532         FRS_PRINT_ERROR
533 } frs_return_t;
534
535 static frs_return_t
536 follow_read_stream(follow_info_t *follow_info,
537                    gboolean (*print_line) (char *, int, gboolean, void *, print_format_e),
538                    void *arg, print_format_e format)
539 {
540     tcp_stream_chunk    sc;
541     int                 bcount, iplen;
542     guint8              client_addr[MAX_IPADDR_LEN];
543     guint16             client_port = 0;
544     gboolean            is_server;
545     guint32             current_pos, global_client_pos = 0, global_server_pos = 0;
546     guint32             *global_pos;
547     gboolean            skip;
548     gchar               initbuf[256];
549     guint32             server_packet_count = 0;
550     guint32             client_packet_count = 0;
551     char                buffer[FLT_BUF_SIZE];
552     size_t              nchars;
553
554     iplen = (follow_info->is_ipv6) ? 16 : 4;
555
556     data_out_file = fopen(follow_info->data_out_filename, "rb");
557     if (data_out_file == NULL) {
558         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
559                       "Could not open temporary file %s: %s", follow_info->data_out_filename,
560                       strerror(errno));
561         return FRS_OPEN_ERROR;
562     }
563
564     while (fread(&sc, 1, sizeof(sc), data_out_file)) {
565         if (client_port == 0) {
566             memcpy(client_addr, sc.src_addr, iplen);
567             client_port = sc.src_port;
568         }
569         skip = FALSE;
570         if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
571             client_port == sc.src_port) {
572             is_server = FALSE;
573             global_pos = &global_client_pos;
574             if (follow_info->show_stream == FROM_SERVER) {
575                 skip = TRUE;
576             }
577         }
578         else {
579             is_server = TRUE;
580             global_pos = &global_server_pos;
581             if (follow_info->show_stream == FROM_CLIENT) {
582                 skip = TRUE;
583             }
584         }
585
586         while (sc.dlen > 0) {
587             bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
588             nchars = fread(buffer, 1, bcount, data_out_file);
589             if (nchars == 0)
590                 break;
591             sc.dlen -= bcount;
592             if (!skip) {
593                 switch (follow_info->show_type) {
594
595                 case SHOW_EBCDIC:
596                     /* If our native arch is ASCII, call: */
597                     EBCDIC_to_ASCII(buffer, nchars);
598                     if (!(*print_line) (buffer, nchars, is_server, arg, format))
599                         goto print_error;
600                     break;
601
602                 case SHOW_ASCII:
603                     /* If our native arch is EBCDIC, call:
604                      * ASCII_TO_EBCDIC(buffer, nchars);
605                      */
606                     if (!(*print_line) (buffer, nchars, is_server, arg, format))
607                         goto print_error;
608                     break;
609
610                 case SHOW_HEXDUMP:
611                     current_pos = 0;
612                     while (current_pos < nchars) {
613                         gchar hexbuf[256];
614                         gchar hexchars[] = "0123456789abcdef";
615                         int i, cur;
616
617                         /* is_server indentation : put 63 spaces at the
618                          * beginning of the string */
619                         g_snprintf(hexbuf, sizeof(hexbuf),
620                 (is_server && follow_info->show_stream == BOTH_HOSTS) ?
621                                 "                                 "
622                                 "                                             %08X  " :
623                                 "%08X  ", *global_pos);
624                         cur = strlen(hexbuf);
625                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
626                             hexbuf[cur++] =
627                                 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
628                             hexbuf[cur++] =
629                                 hexchars[buffer[current_pos + i] & 0x0f];
630                             if (i == 7) {
631                                 hexbuf[cur++] = ' ';
632                                 hexbuf[cur++] = ' ';
633                             } else if (i != 15)
634                                 hexbuf[cur++] = ' ';
635                         }
636                         /* Fill it up if column isn't complete */
637                         if (i < 16) {
638                             int j;
639
640                             for (j = i; j < 16; j++) {
641                                 if (j == 7)
642                                     hexbuf[cur++] = ' ';
643                                 hexbuf[cur++] = ' ';
644                                 hexbuf[cur++] = ' ';
645                                 hexbuf[cur++] = ' ';
646                             }
647                         } else
648                             hexbuf[cur++] = ' ';
649
650                         /* Now dump bytes as text */
651                         for (i = 0; i < 16 && current_pos + i < nchars; i++) {
652                             hexbuf[cur++] =
653                                 (isprint((guchar)buffer[current_pos + i]) ?
654                                 buffer[current_pos + i] : '.' );
655                             if (i == 7) {
656                                 hexbuf[cur++] = ' ';
657                             }
658                         }
659                         current_pos += i;
660                         (*global_pos) += i;
661                         hexbuf[cur++] = '\n';
662                         hexbuf[cur] = 0;
663                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg, format))
664                             goto print_error;
665                     }
666                     break;
667
668                 case SHOW_CARRAY:
669                     current_pos = 0;
670                     g_snprintf(initbuf, 256, "char peer%d_%d[] = {\n", is_server ? 1 : 0,
671                             is_server ? server_packet_count++ : client_packet_count++);
672                     if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg, format))
673                         goto print_error;
674                     while (current_pos < nchars) {
675                         gchar hexbuf[256];
676                         gchar hexchars[] = "0123456789abcdef";
677                         int i, cur;
678
679                         cur = 0;
680                         for (i = 0; i < 8 && current_pos + i < nchars; i++) {
681                           /* Prepend entries with "0x" */
682                           hexbuf[cur++] = '0';
683                           hexbuf[cur++] = 'x';
684                             hexbuf[cur++] =
685                                 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
686                             hexbuf[cur++] =
687                                 hexchars[buffer[current_pos + i] & 0x0f];
688
689                             /* Delimit array entries with a comma */
690                             if (current_pos + i + 1 < nchars)
691                               hexbuf[cur++] = ',';
692
693                             hexbuf[cur++] = ' ';
694                         }
695
696                         /* Terminate the array if we are at the end */
697                         if (current_pos + i == nchars) {
698                             hexbuf[cur++] = '}';
699                             hexbuf[cur++] = ';';
700                         }
701
702                         current_pos += i;
703                         (*global_pos) += i;
704                         hexbuf[cur++] = '\n';
705                         hexbuf[cur] = 0;
706                         if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg, format))
707                             goto print_error;
708                     }
709                     break;
710                 }
711             }
712         }
713     }
714     if (ferror(data_out_file)) {
715         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
716                       "Error reading temporary file %s: %s", follow_info->data_out_filename,
717                       strerror(errno));
718         fclose(data_out_file);
719         data_out_file = NULL;
720         return FRS_READ_ERROR;
721     }
722
723         fclose(data_out_file);
724         data_out_file = NULL;
725     return FRS_OK;
726
727 print_error:
728     fclose(data_out_file);
729     data_out_file = NULL;
730     return FRS_PRINT_ERROR;
731 }
732
733 /*
734  * XXX - for text printing, we probably want to wrap lines at 80 characters;
735  * (PostScript printing is doing this already), and perhaps put some kind of 
736  * dingbat (to use the technical term) to indicate a wrapped line, along the 
737  * lines of what's done when displaying this in a window, as per Warren Young's 
738  * suggestion.
739  */
740 static gboolean
741 follow_print_text(char *buffer, int nchars, gboolean is_server _U_, void *arg, print_format_e format)
742 {
743     FILE *fh = arg;
744     int i;
745     char *str;
746
747     /* convert non printable characters */
748     for (i = 0; i < nchars; i++) {
749         if (buffer[i] == '\n' || buffer[i] == '\r')
750             continue;
751         if (! isprint(buffer[i])) {
752             buffer[i] = '.';
753         }
754     }
755
756     /* convert unterminated char array to a zero terminated string */
757     str = g_malloc(nchars + 1);
758     memcpy(str, buffer, nchars);
759     str[nchars] = 0;
760     print_line(fh, /*indent*/ 0, format, str);
761     g_free(str);
762
763     return TRUE;
764 }
765
766 static void
767 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
768 {
769     follow_info_t       *follow_info = data;
770
771     /* Lock out user from messing with us. (ie. don't free our data!) */
772     gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
773
774     /* Set the display filter. */
775     gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
776
777     /* Run the display filter so it goes in effect. */
778     main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
779
780     /* we force a subsequent close */
781     window_destroy(follow_info->streamwindow);
782
783     return;
784 }
785
786 static void
787 follow_print_stream(GtkWidget * w _U_, gpointer data)
788 {
789     FILE                *fh;
790     gboolean            to_file;
791     char                *print_dest;
792     follow_info_t       *follow_info = data;
793 #ifdef _WIN32
794     gboolean win_printer = FALSE;
795 #endif
796
797
798     switch (prefs.pr_dest) {
799     case PR_DEST_CMD:
800 #ifdef _WIN32
801         win_printer = TRUE;
802         /*XXX should use temp file stuff in util routines */
803         print_dest = g_strdup(tmpnam(NULL));
804         to_file = TRUE;
805 #else
806         print_dest = prefs.pr_cmd;
807         to_file = FALSE;
808 #endif
809         break;
810     case PR_DEST_FILE:
811         print_dest = prefs.pr_file;
812         to_file = TRUE;
813         break;
814     default:                    /* "Can't happen" */
815         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
816             "Couldn't figure out where to send the print "
817             "job. Check your preferences.");
818         return;
819     }
820
821     fh = open_print_dest(to_file, print_dest);
822     if (fh == NULL) {
823         if (to_file) {
824             open_failure_alert_box(prefs.pr_file, errno, TRUE);
825         } else {
826             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
827                       "Couldn't run print command %s.", prefs.pr_cmd);
828         }
829         return;
830     }
831
832     print_preamble(fh, prefs.pr_format, cfile.filename);
833     if (ferror(fh))
834         goto print_error;
835
836     switch (follow_read_stream(follow_info, follow_print_text, fh, prefs.pr_format)) {
837     case FRS_OK:
838         break;
839     case FRS_OPEN_ERROR:
840     case FRS_READ_ERROR:
841             /* XXX - cancel printing? */
842             close_print_dest(to_file, fh);
843             return;
844     case FRS_PRINT_ERROR:
845             goto print_error;
846     }
847
848     print_finale(fh, prefs.pr_format);
849     if (ferror(fh))
850             goto print_error;
851
852     if (!close_print_dest(to_file, fh)) {
853         if (to_file) {
854             write_failure_alert_box(prefs.pr_file, errno);
855         } else {
856                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
857                           "Error closing print destination.");
858         }
859     }
860 #ifdef _WIN32
861     if (win_printer) {
862         print_mswin(print_dest);
863
864         /* trash temp file */
865         remove(print_dest);
866     }
867 #endif
868     return;
869
870 print_error:
871     if (to_file) {
872         write_failure_alert_box(prefs.pr_file, errno);
873     } else {
874         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
875           "Error writing to print command: %s", strerror(errno));
876     }
877     /* XXX - cancel printing? */
878     close_print_dest(to_file, fh);
879
880 #ifdef _WIN32
881     if (win_printer) {
882         /* trash temp file */
883         remove(print_dest);
884     }
885 #endif
886 }
887
888 static gboolean
889 follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server,
890                        void *arg, print_format_e format _U_)
891 {
892     GtkWidget *text = arg;
893     GdkColor   fg, bg;
894 #if GTK_MAJOR_VERSION >= 2
895     GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
896     GtkTextIter    iter;
897     GtkTextTag    *tag;
898     gsize          outbytes;
899     gchar         *convbuf;
900 #endif
901
902 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
903     /* While our isprint() hack is in place, we
904      * have to use convert some chars to '.' in order
905      * to be able to see the data we *should* see
906      * in the GtkText widget.
907      */
908     int i;
909
910     for (i = 0; i < nchars; i++) {
911         if (buffer[i] == '\n' || buffer[i] == '\r')
912             continue;
913         if (! isprint(buffer[i])) {
914             buffer[i] = '.';
915         }
916     }
917 #endif
918
919     if (is_server) {
920         color_t_to_gdkcolor(&fg, &prefs.st_server_fg);
921         color_t_to_gdkcolor(&bg, &prefs.st_server_bg);
922     } else {
923         color_t_to_gdkcolor(&fg, &prefs.st_client_fg);
924         color_t_to_gdkcolor(&bg, &prefs.st_client_bg);
925     }
926 #if GTK_MAJOR_VERSION < 2
927     gtk_text_insert(GTK_TEXT(text), m_r_font, &fg, &bg, buffer, nchars);
928 #else
929     gtk_text_buffer_get_end_iter(buf, &iter);
930     tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &fg,
931                                      "background-gdk", &bg, "font-desc",
932                                      m_r_font, NULL);
933     convbuf = g_locale_to_utf8(buffer, nchars, NULL, &outbytes, NULL);
934     gtk_text_buffer_insert_with_tags(buf, &iter, convbuf, outbytes, tag,
935                                      NULL);
936     g_free(convbuf);
937 #endif
938     return TRUE;
939 }
940
941 static void
942 follow_load_text(follow_info_t *follow_info)
943 {
944 #if GTK_MAJOR_VERSION < 2
945     int bytes_already;
946 #else
947     GtkTextBuffer *buf;
948
949     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
950 #endif
951
952     /* Delete any info already in text box */
953 #if GTK_MAJOR_VERSION < 2
954     bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
955     if (bytes_already > 0) {
956         gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
957         gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
958     }
959
960     /* stop the updates while we fill the text box */
961     gtk_text_freeze(GTK_TEXT(follow_info->text));
962 #else
963     gtk_text_buffer_set_text(buf, "", -1);
964 #endif
965     follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text, PR_FMT_TEXT);
966 #if GTK_MAJOR_VERSION < 2
967     gtk_text_thaw(GTK_TEXT(follow_info->text));
968 #endif
969 }
970
971
972 /*
973  * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
974  * any, so that if somebody tries to do "Save"
975  * while there's already a "Save TCP Follow Stream" window up, we just pop
976  * up the existing one, rather than creating a new one.
977  */
978 static void
979 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
980 {
981     GtkWidget           *new_win;
982     follow_info_t       *follow_info = data;
983
984     if (follow_info->follow_save_as_w != NULL) {
985         /* There's already a dialog box; reactivate it. */
986         reactivate_window(follow_info->follow_save_as_w);
987         return;
988     }
989
990     new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
991                                  FILE_SELECTION_SAVE);
992     follow_info->follow_save_as_w = new_win;
993
994     /* Tuck away the follow_info object into the window */
995     OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
996
997     /* If we've opened a file, start out by showing the files in the directory
998        in which that file resided. */
999     if (last_open_dir)
1000         file_selection_set_current_folder(new_win, last_open_dir);
1001
1002     SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
1003
1004 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1005     if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
1006     {
1007         follow_save_as_ok_cb(new_win, new_win);
1008     } else {
1009         window_destroy(new_win);
1010     }
1011 #else
1012     /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1013        pointer to the file selection box widget */
1014     SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button, 
1015         "clicked", follow_save_as_ok_cb, new_win);
1016
1017     window_set_cancel_button(new_win, 
1018         GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
1019
1020     gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
1021
1022     SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
1023
1024     gtk_widget_show_all(new_win);
1025     window_present(new_win);
1026 #endif
1027 }
1028
1029
1030 static void
1031 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
1032 {
1033     gchar               *to_name;
1034     follow_info_t       *follow_info;
1035     FILE                *fh;
1036     gchar               *dirname;
1037
1038 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1039     to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1040 #else
1041     to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1042 #endif
1043
1044     /* Perhaps the user specified a directory instead of a file.
1045        Check whether they did. */
1046     if (test_for_directory(to_name) == EISDIR) {
1047         /* It's a directory - set the file selection box to display that
1048            directory, and leave the selection box displayed. */
1049         set_last_open_dir(to_name);
1050         g_free(to_name);
1051         file_selection_set_current_folder(fs, last_open_dir);
1052         return;
1053     }
1054
1055     fh = fopen(to_name, "wb");
1056     if (fh == NULL) {
1057         open_failure_alert_box(to_name, errno, TRUE);
1058         g_free(to_name);
1059         return;
1060     }
1061
1062     gtk_widget_hide(GTK_WIDGET(fs));
1063     follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1064     window_destroy(GTK_WIDGET(fs));
1065
1066     switch (follow_read_stream(follow_info, follow_print_text, fh, PR_FMT_TEXT)) {
1067
1068     case FRS_OK:
1069         if (fclose(fh) == EOF)
1070             write_failure_alert_box(to_name, errno);
1071         break;
1072
1073     case FRS_OPEN_ERROR:
1074     case FRS_READ_ERROR:
1075         fclose(fh);
1076         break;
1077
1078     case FRS_PRINT_ERROR:
1079         write_failure_alert_box(to_name, errno);
1080         fclose(fh);
1081         break;
1082     }
1083
1084     /* Save the directory name for future file dialogs. */
1085     dirname = get_dirname(to_name);  /* Overwrites to_name */
1086     set_last_open_dir(dirname);
1087     g_free(to_name);
1088 }
1089
1090 static void
1091 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1092 {
1093         follow_info_t   *follow_info = data;
1094
1095         /* Note that we no longer have a dialog box. */
1096         follow_info->follow_save_as_w = NULL;
1097 }