Rename "ws_version_info.h", also .c
[metze/wireshark/wip.git] / ui / gtk / follow_stream.c
1 /* follow_stream.c
2  * Common routines for following data streams
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21  * USA.
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <gtk/gtk.h>
29
30 #include <epan/addr_resolv.h>
31 #include <epan/follow.h>
32 #include <epan/epan_dissect.h>
33 #include <wsutil/filesystem.h>
34 #include <epan/prefs.h>
35 #include <epan/charsets.h>
36 #include <epan/tap.h>
37
38 #include <epan/print.h>
39
40 #include <ui/alert_box.h>
41 #include <ui/last_open_dir.h>
42 #include <ui/simple_dialog.h>
43
44 #include <wsutil/file_util.h>
45 #include <version_info.h>
46
47 #include "gtkglobals.h"
48 #include "ui/gtk/keys.h"
49 #include "ui/gtk/color_utils.h"
50 #include "ui/gtk/stock_icons.h"
51 #include "ui/gtk/dlg_utils.h"
52 #include "ui/gtk/follow_stream.h"
53 #include "ui/gtk/font_utils.h"
54 #include "ui/gtk/file_dlg.h"
55 #include "ui/gtk/gui_utils.h"
56 #include "ui/gtk/help_dlg.h"
57 #include "ui/gtk/main.h"
58 #include "ui/gtk/old-gtk-compat.h"
59
60 #include <wsutil/utf8_entities.h>
61 #ifdef _WIN32
62 #include "wsutil/tempfile.h"
63 #include "ui/win32/print_win32.h"
64 #endif
65
66 /* static variable declarations to speed up the performance
67  * of follow_load_text and follow_add_to_gtk_text
68  */
69 static GdkColor server_fg, server_bg;
70 static GdkColor client_fg, client_bg;
71 static GtkTextTag *server_tag, *client_tag;
72
73 static void follow_find_destroy_cb(GtkWidget * win _U_, gpointer data);
74 static void follow_find_button_cb(GtkWidget * w, gpointer data);
75 static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
76
77 static void follow_stream(const gchar *title, follow_info_t *follow_info,
78           gchar *both_directions_string, gchar *server_to_client_string, gchar *client_to_server_string);
79 static frs_return_t follow_show(follow_info_t *follow_info,
80             follow_print_line_func follow_print,
81             char *buffer, size_t nchars, gboolean is_from_server, void *arg,
82             guint32 *global_pos, guint32 *server_packet_count,
83             guint32 *client_packet_count);
84
85 static GList *follow_infos = NULL;
86
87 /*
88  * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
89  * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
90  * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
91  * the "print_line()" routine from "print.c", and as that routine might
92  * genuinely expect to be handed a line (if, for example, it's using
93  * some OS or desktop environment's printing API, and that API expects
94  * to be handed lines), "follow_print_text()" should probably accumulate
95  * lines in a buffer and hand them "print_line()".  (If there's a
96  * complete line in a buffer - i.e., there's nothing of the line in
97  * the previous buffer or the next buffer - it can just hand that to
98  * "print_line()" after filtering out non-printables, as an
99  * optimization.)
100  *
101  * This might or might not be the reason why C arrays display
102  * correctly but get extra blank lines very other line when printed.
103  */
104 static frs_return_t
105 follow_common_read_stream(follow_info_t *follow_info,
106     follow_print_line_func follow_print,
107     void *arg)
108 {
109     guint32 global_client_pos = 0, global_server_pos = 0;
110     guint32 server_packet_count = 0;
111     guint32 client_packet_count = 0;
112     guint32 *global_pos;
113     gboolean skip;
114     GList* cur;
115     frs_return_t frs_return;
116     follow_record_t *follow_record;
117     GByteArray *buffer = g_byte_array_new();
118
119
120     for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
121         follow_record = (follow_record_t *)cur->data;
122         skip = FALSE;
123         if (!follow_record->is_server) {
124             global_pos = &global_client_pos;
125             if(follow_info->show_stream == FROM_SERVER) {
126                 skip = TRUE;
127             }
128         } else {
129             global_pos = &global_server_pos;
130             if (follow_info->show_stream == FROM_CLIENT) {
131                 skip = TRUE;
132             }
133         }
134
135         if (!skip) {
136             g_byte_array_set_size(buffer, 0);
137             g_byte_array_append(buffer, follow_record->data->data,
138                                      follow_record->data->len);
139
140             frs_return = follow_show(follow_info, follow_print,
141                                      buffer->data,
142                                      follow_record->data->len,
143                                      follow_record->is_server, arg,
144                                      global_pos,
145                                      &server_packet_count,
146                                      &client_packet_count);
147             if(frs_return == FRS_PRINT_ERROR) {
148                 g_byte_array_free(buffer, TRUE);
149                 return frs_return;
150             }
151         }
152     }
153
154     g_byte_array_free(buffer, TRUE);
155     return FRS_OK;
156 }
157
158 static void follow_stream_cb(register_follow_t* follower, follow_read_stream_func read_stream_func, GtkWidget * w _U_, gpointer data _U_)
159 {
160     GtkWidget   *filter_cm;
161     GtkWidget   *filter_te;
162     gchar       *follow_filter;
163     const gchar *previous_filter;
164     int         filter_out_filter_len;
165     const char  *hostname0, *hostname1;
166     char        *port0, *port1;
167     gchar       *server_to_client_string = NULL;
168     gchar       *client_to_server_string = NULL;
169     gchar       *both_directions_string = NULL;
170     follow_info_t  *follow_info;
171     gtk_follow_info_t *gtk_follow_info;
172     GString *msg;
173     gboolean is_follow = FALSE;
174     guint32 ignore_stream;
175     char  stream_window_title[256];
176
177     is_follow = proto_is_frame_protocol(cfile.edt->pi.layers, proto_get_protocol_filter_name(get_follow_proto_id(follower)));
178
179     if (!is_follow) {
180         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
181                       "Error following stream.  Please make\n"
182                       "sure you have a %s packet selected.", proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower))));
183         return;
184     }
185
186     gtk_follow_info = g_new0(gtk_follow_info_t, 1);
187     follow_info = g_new0(follow_info_t, 1);
188     gtk_follow_info->read_stream = read_stream_func;
189     follow_info->gui_data = gtk_follow_info;
190
191     /* Create a new filter that matches all packets in the TCP stream,
192        and set the display filter entry accordingly */
193     follow_filter = get_follow_conv_func(follower)(&cfile.edt->pi, &ignore_stream);
194     if (!follow_filter) {
195         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
196                       "Error creating filter for this stream.\n"
197                       "A transport or network layer header is needed");
198         g_free(gtk_follow_info);
199         g_free(follow_info);
200         return;
201     }
202
203     /* Set the display filter entry accordingly */
204     filter_cm = (GtkWidget *)g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
205     filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
206
207     /* needed in follow_filter_out_stream(), is there a better way? */
208     gtk_follow_info->filter_te = filter_te;
209
210     /* save previous filter, const since we're not supposed to alter */
211     previous_filter =
212         (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
213
214     /* allocate our new filter. API claims g_malloc terminates program on failure */
215     /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
216     filter_out_filter_len = (int)(strlen(follow_filter) + strlen(previous_filter) + 16);
217     follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
218
219     /* append the negation */
220     if(strlen(previous_filter)) {
221         g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
222             "%s and !(%s)", previous_filter, follow_filter);
223     } else {
224         g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
225             "!(%s)", follow_filter);
226     }
227
228     /* data will be passed via tap callback*/
229     msg = register_tap_listener(get_follow_tap_string(follower), follow_info, follow_filter,
230                                 0, NULL, get_follow_tap_handler(follower), NULL);
231     if (msg) {
232         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
233                       "Can't register %s tap: %s\n",
234                       get_follow_tap_string(follower), msg->str);
235         g_free(gtk_follow_info);
236         g_free(follow_info->filter_out_filter);
237         g_free(follow_info);
238         g_free(follow_filter);
239         return;
240     }
241
242     gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
243
244     /* Run the display filter so it goes in effect - even if it's the
245        same as the previous display filter. */
246     main_filter_packets(&cfile, follow_filter, TRUE);
247
248     remove_tap_listener(follow_info);
249
250     hostname0 = address_to_name(&follow_info->client_ip);
251     hostname1 = address_to_name(&follow_info->server_ip);
252
253     port0 = get_follow_port_to_display(follower)(NULL, follow_info->client_port);
254     port1 = get_follow_port_to_display(follower)(NULL, follow_info->server_port);
255
256     /* Both Stream Directions */
257     both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]);
258
259     server_to_client_string =
260         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
261                         hostname0, port0,
262                         hostname1, port1,
263                         follow_info->bytes_written[0]);
264
265     client_to_server_string =
266         g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
267                         hostname1, port1,
268                         hostname0, port0,
269                         follow_info->bytes_written[1]);
270
271     g_snprintf(stream_window_title, 256, "Follow %s Stream (%s)",
272                     proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower))), follow_filter);
273     follow_stream(stream_window_title, follow_info, both_directions_string,
274                   server_to_client_string, client_to_server_string);
275
276     /* Free the filter string, as we're done with it. */
277     g_free(follow_filter);
278
279     wmem_free(NULL, port0);
280     wmem_free(NULL, port1);
281     g_free(both_directions_string);
282     g_free(server_to_client_string);
283     g_free(client_to_server_string);
284
285 }
286
287 static gboolean
288 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_from_server,
289                void *arg)
290 {
291     GtkWidget *text = (GtkWidget *)arg;
292     GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
293     GtkTextIter    iter;
294
295     /*
296      * have to convert non printable ASCII chars to '.' in order
297      * to be able to see the data we *should* see
298      * in the GtkText widget.
299      */
300     size_t i;
301
302     for (i = 0; i < nchars; i++) {
303         if (buffer[i] == '\n' || buffer[i] == '\r')
304             continue;
305         if (! g_ascii_isprint(buffer[i])) {
306             buffer[i] = '.';
307         }
308     }
309
310     gtk_text_buffer_get_end_iter(buf, &iter);
311     if (is_from_server) {
312         gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
313                          server_tag, NULL);
314     } else {
315         gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
316                          client_tag, NULL);
317     }
318     return TRUE;
319 }
320
321 /*
322  * XXX - for text printing, we probably want to wrap lines at 80 characters;
323  * (PostScript printing is doing this already), and perhaps put some kind of
324  * dingbat (to use the technical term) to indicate a wrapped line, along the
325  * lines of what's done when displaying this in a window, as per Warren Young's
326  * suggestion.
327  */
328 static gboolean
329 follow_print_text(char *buffer, size_t nchars, gboolean is_from_server _U_,
330           void *arg)
331 {
332     print_stream_t *stream = (print_stream_t *)arg;
333     size_t i;
334     char *str;
335
336     /* convert non printable characters */
337     for (i = 0; i < nchars; i++) {
338         if (buffer[i] == '\n' || buffer[i] == '\r')
339             continue;
340         if (! g_ascii_isprint(buffer[i])) {
341             buffer[i] = '.';
342         }
343     }
344
345     /* convert unterminated char array to a zero terminated string */
346     str = (char *)g_malloc(nchars + 1);
347     memcpy(str, buffer, nchars);
348     str[nchars] = 0;
349     print_line(stream, /*indent*/ 0, str);
350     g_free(str);
351
352     return TRUE;
353 }
354
355 static gboolean
356 follow_write_raw(char *buffer, size_t nchars, gboolean is_from_server _U_, void *arg)
357 {
358     FILE *fh = (FILE *)arg;
359     size_t nwritten;
360
361     nwritten = fwrite(buffer, 1, nchars, fh);
362     if (nwritten != nchars)
363         return FALSE;
364
365     return TRUE;
366 }
367
368 static void
369 follow_load_text(follow_info_t *follow_info)
370 {
371     GtkTextBuffer *buf;
372     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
373
374     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtk_follow_info->text));
375
376     /* prepare colors one time for repeated use by follow_add_to_gtk_text */
377     color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
378     color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
379     color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
380     color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
381
382     /* prepare tags one time for repeated use by follow_add_to_gtk_text */
383     server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
384                                         &server_fg, "background-gdk",
385                                         &server_bg, "font-desc",
386                                         user_font_get_regular(), NULL);
387     client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
388                                         &client_fg, "background-gdk",
389                                         &client_bg, "font-desc",
390                                         user_font_get_regular(), NULL);
391
392     /* Delete any info already in text box */
393     gtk_text_buffer_set_text(buf, "", -1);
394
395     gtk_follow_info->read_stream(follow_info, follow_add_to_gtk_text,
396                gtk_follow_info->text);
397 }
398
399 /* Handles the display style toggling */
400 static void
401 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
402 {
403     follow_info_t    *follow_info = (follow_info_t *)data;
404     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
405
406     /*
407      * A radio button toggles when it goes on and when it goes
408      * off, so when you click a radio button two signals are
409      * delivered.  We only want to reprocess the display once,
410      * so we do it only when the button goes on.
411      */
412     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
413         if (w == gtk_follow_info->ebcdic_bt)
414             gtk_follow_info->show_type = SHOW_EBCDIC;
415         else if (w == gtk_follow_info->hexdump_bt)
416             gtk_follow_info->show_type = SHOW_HEXDUMP;
417         else if (w == gtk_follow_info->carray_bt)
418             gtk_follow_info->show_type = SHOW_CARRAY;
419         else if (w == gtk_follow_info->ascii_bt)
420             gtk_follow_info->show_type = SHOW_ASCII;
421         else if (w == gtk_follow_info->raw_bt)
422             gtk_follow_info->show_type = SHOW_RAW;
423         follow_load_text(follow_info);
424     }
425 }
426
427 static void
428 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
429 {
430     follow_info_t    *follow_info = (follow_info_t *)data;
431     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
432
433     /* Lock out user from messing with us. (ie. don't free our data!) */
434     gtk_widget_set_sensitive(gtk_follow_info->streamwindow, FALSE);
435
436     /* Set the display filter. */
437     gtk_entry_set_text(GTK_ENTRY(gtk_follow_info->filter_te),
438                follow_info->filter_out_filter);
439
440     /* Run the display filter so it goes in effect. */
441     main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
442
443     /* we force a subsequent close */
444     window_destroy(gtk_follow_info->streamwindow);
445
446     return;
447 }
448
449 static void
450 follow_find_cb(GtkWidget * w _U_, gpointer data)
451 {
452     follow_info_t       *follow_info = (follow_info_t *)data;
453     gtk_follow_info_t   *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
454     GtkWidget           *find_dlg_w, *main_vb, *buttons_row, *find_lb;
455     GtkWidget           *find_hb, *find_text_box, *find_bt, *cancel_bt;
456
457     if (gtk_follow_info->find_dlg_w != NULL) {
458         /* There's already a dialog box; reactivate it. */
459         reactivate_window(gtk_follow_info->find_dlg_w);
460         return;
461     }
462
463     /* Create the find box */
464     find_dlg_w = dlg_window_new("Wireshark: Find text");
465     gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
466                                 GTK_WINDOW(gtk_follow_info->streamwindow));
467     gtk_widget_set_size_request(find_dlg_w, 225, -1);
468     gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
469     gtk_follow_info->find_dlg_w = find_dlg_w;
470
471     g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
472                     follow_info);
473     g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
474                     NULL);
475
476     /* Main vertical box */
477     main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
478     gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
479     gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
480
481     /* Horizontal box for find label, entry field and up/down radio
482        buttons */
483     find_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
484     gtk_box_pack_start(GTK_BOX (main_vb), find_hb, TRUE, TRUE, 0);
485     gtk_widget_show(find_hb);
486
487     /* Find label */
488     find_lb = gtk_label_new("Find text:");
489     gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
490     gtk_widget_show(find_lb);
491
492     /* Find field */
493     find_text_box = gtk_entry_new();
494     gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
495     gtk_widget_set_tooltip_text(find_text_box, "Text to search for (case sensitive)");
496     gtk_widget_show(find_text_box);
497
498     /* Buttons row */
499     buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
500                                     NULL);
501     gtk_box_pack_start(GTK_BOX(main_vb), buttons_row, TRUE, TRUE, 0);
502     find_bt   = (GtkWidget *)g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
503     cancel_bt = (GtkWidget *)g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
504
505     g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
506     g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
507     window_set_cancel_button(find_dlg_w, cancel_bt,
508                              window_cancel_button_cb);
509
510     /* Hitting return in the find field "clicks" the find button */
511     dlg_set_activate(find_text_box, find_bt);
512
513     /* Show the dialog */
514     gtk_widget_show_all(find_dlg_w);
515     window_present(find_dlg_w);
516 }
517
518 static void
519 follow_find_button_cb(GtkWidget * w, gpointer data)
520 {
521     gboolean        found;
522     const gchar     *find_string;
523     follow_info_t   *follow_info = (follow_info_t *)data;
524     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
525     GtkTextBuffer   *buffer;
526     GtkTextIter     iter, match_start, match_end;
527     GtkTextMark     *last_pos_mark;
528     GtkWidget       *find_string_w;
529
530     /* Get the text the user typed into the find field */
531     find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
532     find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
533
534     /* Get the buffer associated with the follow stream */
535     buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtk_follow_info->text));
536     gtk_text_buffer_get_start_iter(buffer, &iter);
537
538     /* Look for the search string in the buffer */
539     last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
540     if(last_pos_mark)
541         gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
542
543     found = gtk_text_iter_forward_search(&iter, find_string, (GtkTextSearchFlags)0,
544                                          &match_start,
545                                          &match_end,
546                                          NULL);
547
548     if(found) {
549         gtk_text_buffer_select_range(buffer, &match_start, &match_end);
550         last_pos_mark = gtk_text_buffer_create_mark (buffer,
551                                                      "last_position",
552                                                      &match_end, FALSE);
553         gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(gtk_follow_info->text), last_pos_mark);
554     } else {
555         /* We didn't find a match */
556         simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
557                       "%sFind text has reached the end of the followed "
558                       "stream%s\n\nThe next search will start from the "
559                       "beginning", simple_dialog_primary_start(),
560                       simple_dialog_primary_end());
561         if(last_pos_mark)
562             gtk_text_buffer_delete_mark(buffer, last_pos_mark);
563     }
564
565 }
566
567 static void
568 follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
569 {
570     follow_info_t     *follow_info = (follow_info_t *)data;
571     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
572
573     /* Note that we no longer have a dialog box. */
574     gtk_follow_info->find_dlg_w = NULL;
575 }
576
577 static void
578 follow_print_stream(GtkWidget * w _U_, gpointer data)
579 {
580     print_stream_t  *stream;
581     gboolean        to_file;
582     const char      *print_dest;
583     follow_info_t   *follow_info =(follow_info_t *) data;
584     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
585 #ifdef _WIN32
586     gboolean        win_printer = FALSE;
587     int             tmp_fd;
588     char            *tmp_namebuf;
589 #endif
590
591     switch (prefs.pr_dest) {
592     case PR_DEST_CMD:
593 #ifdef _WIN32
594         win_printer = TRUE;
595         /* (The code for creating a temp filename is adapted from print_dlg.c).   */
596         /* We currently don't have a function in util.h to create just a tempfile */
597         /* name, so simply create a tempfile using the "official" function,       */
598         /* then delete this file again. After this, the name MUST be available.   */
599         /* */
600         /* Don't use tmpnam() or such, as this will fail under some ACL           */
601         /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358  */
602         /* Also: tmpnam is "insecure" and should not be used.                     */
603         tmp_fd = create_tempfile(&tmp_namebuf, "wshprint", NULL);
604         if(tmp_fd == -1) {
605             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
606                           "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
607             return;
608         }
609         ws_close(tmp_fd);
610         ws_unlink(tmp_namebuf);
611         print_dest = tmp_namebuf;
612         to_file = TRUE;
613 #else
614         print_dest = prefs.pr_cmd;
615         to_file = FALSE;
616 #endif
617         break;
618     case PR_DEST_FILE:
619         print_dest = prefs.pr_file;
620         to_file = TRUE;
621         break;
622     default:            /* "Can't happen" */
623         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
624                       "Couldn't figure out where to send the print "
625                       "job. Check your preferences.");
626         return;
627     }
628
629     switch (prefs.pr_format) {
630
631     case PR_FMT_TEXT:
632         stream = print_stream_text_new(to_file, print_dest);
633         break;
634
635     case PR_FMT_PS:
636         stream = print_stream_ps_new(to_file, print_dest);
637         break;
638
639     default:
640         g_assert_not_reached();
641         stream = NULL;
642     }
643     if (stream == NULL) {
644         if (to_file) {
645             open_failure_alert_box(print_dest, errno, TRUE);
646         } else {
647             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
648                           "Couldn't run print command %s.",
649                           prefs.pr_cmd);
650         }
651         return;
652     }
653
654     if (!print_preamble(stream, cfile.filename, get_ws_vcs_version_info()))
655         goto print_error;
656
657     switch (gtk_follow_info->read_stream(follow_info, follow_print_text, stream)) {
658     case FRS_OK:
659         break;
660     case FRS_OPEN_ERROR:
661     case FRS_READ_ERROR:
662         /* XXX - cancel printing? */
663         destroy_print_stream(stream);
664         return;
665     case FRS_PRINT_ERROR:
666         goto print_error;
667     }
668
669     if (!print_finale(stream))
670         goto print_error;
671
672     if (!destroy_print_stream(stream)) {
673         if (to_file) {
674             write_failure_alert_box(print_dest, errno);
675         } else {
676             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
677                           "Error closing print destination.");
678         }
679     }
680 #ifdef _WIN32
681     if (win_printer) {
682         print_mswin(print_dest);
683
684         /* trash temp file */
685         ws_remove(print_dest);
686     }
687 #endif
688     return;
689
690  print_error:
691     if (to_file) {
692         write_failure_alert_box(print_dest, errno);
693     } else {
694         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
695                       "Error writing to print command: %s",
696                       g_strerror(errno));
697     }
698     /* XXX - cancel printing? */
699     destroy_print_stream(stream);
700
701 #ifdef _WIN32
702     if (win_printer) {
703         /* trash temp file */
704         ws_remove(print_dest);
705     }
706 #endif
707 }
708
709 static char *
710 gtk_follow_save_as_file(GtkWidget *caller)
711 {
712     GtkWidget   *new_win;
713     char        *pathname;
714
715     new_win = file_selection_new("Wireshark: Save Follow Stream As",
716                                  GTK_WINDOW(caller),
717                                  FILE_SELECTION_SAVE);
718     pathname = file_selection_run(new_win);
719     if (pathname == NULL) {
720         /* User cancelled or closed the dialog. */
721         return NULL;
722     }
723
724     /* We've crosed the Rubicon; get rid of the dialog box. */
725     window_destroy(new_win);
726
727     return pathname;
728 }
729
730 static gboolean
731 follow_save_as_ok_cb(gchar *to_name, follow_info_t *follow_info)
732 {
733     FILE        *fh;
734     print_stream_t    *stream;
735     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
736
737     if (gtk_follow_info->show_type == SHOW_RAW) {
738         /* Write the data out as raw binary data */
739         fh = ws_fopen(to_name, "wb");
740     } else {
741         /* Write it out as text */
742         fh = ws_fopen(to_name, "w");
743     }
744     if (fh == NULL) {
745         open_failure_alert_box(to_name, errno, TRUE);
746         return FALSE;
747     }
748
749     if (gtk_follow_info->show_type == SHOW_RAW) {
750         switch (gtk_follow_info->read_stream(follow_info, follow_write_raw, fh)) {
751         case FRS_OK:
752             if (fclose(fh) == EOF) {
753                 write_failure_alert_box(to_name, errno);
754                 return FALSE;
755             }
756             break;
757
758         case FRS_OPEN_ERROR:
759         case FRS_READ_ERROR:
760             fclose(fh);
761             return FALSE;
762
763         case FRS_PRINT_ERROR:
764             write_failure_alert_box(to_name, errno);
765             fclose(fh);
766             return FALSE;
767         }
768     } else {
769         stream = print_stream_text_stdio_new(fh);
770         switch (gtk_follow_info->read_stream(follow_info, follow_print_text, stream)) {
771         case FRS_OK:
772             if (!destroy_print_stream(stream)) {
773                 write_failure_alert_box(to_name, errno);
774                 return FALSE;
775             }
776             break;
777
778         case FRS_OPEN_ERROR:
779         case FRS_READ_ERROR:
780             destroy_print_stream(stream);
781             return FALSE;
782
783         case FRS_PRINT_ERROR:
784             write_failure_alert_box(to_name, errno);
785             destroy_print_stream(stream);
786             return FALSE;
787         }
788     }
789
790     return TRUE;
791 }
792
793 static void
794 follow_save_as_cmd_cb(GtkWidget *w, gpointer data)
795 {
796     GtkWidget       *caller = gtk_widget_get_toplevel(w);
797     follow_info_t   *follow_info = (follow_info_t *)data;
798     char            *pathname;
799
800     /*
801      * Loop until the user either selects a file or gives up.
802      */
803     for (;;) {
804         pathname = gtk_follow_save_as_file(caller);
805         if (pathname == NULL) {
806             /* User gave up. */
807             break;
808         }
809         if (follow_save_as_ok_cb(pathname, follow_info)) {
810             /* We succeeded. */
811             g_free(pathname);
812             break;
813         }
814         /* Dump failed; let the user select another file or give up. */
815         g_free(pathname);
816     }
817 }
818
819 static void
820 follow_stream_direction_changed(GtkWidget *w, gpointer data)
821 {
822     follow_info_t *follow_info = (follow_info_t *)data;
823
824     switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
825
826     case 0 :
827         follow_info->show_stream = BOTH_HOSTS;
828         follow_load_text(follow_info);
829         break;
830     case 1 :
831         follow_info->show_stream = FROM_SERVER;
832         follow_load_text(follow_info);
833         break;
834     case 2 :
835         follow_info->show_stream = FROM_CLIENT;
836         follow_load_text(follow_info);
837         break;
838     }
839 }
840
841 /* Add a "follow_info_t" structure to the list. */
842 static void
843 remember_follow_info(follow_info_t *follow_info)
844 {
845     follow_infos = g_list_append(follow_infos, follow_info);
846 }
847
848 #define IS_SHOW_TYPE(x) (gtk_follow_info->show_type == x ? 1 : 0)
849 /* Remove a "follow_info_t" structure from the list. */
850 static void
851 forget_follow_info(follow_info_t *follow_info)
852 {
853     follow_infos = g_list_remove(follow_infos, follow_info);
854 }
855
856 static void
857 follow_stream(const gchar *title, follow_info_t *follow_info,
858           gchar *both_directions_string,
859           gchar *server_to_client_string, gchar *client_to_server_string)
860 {
861     GtkWidget    *streamwindow, *vbox, *txt_scrollw, *text;
862     GtkWidget    *hbox, *bbox, *button, *radio_bt;
863     GtkWidget    *stream_fr, *stream_vb, *direction_hbox;
864     GtkWidget    *stream_cmb;
865     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
866
867     gtk_follow_info->show_type = SHOW_RAW;
868
869     streamwindow = dlg_window_new(title);
870
871     /* needed in follow_filter_out_stream(), is there a better way? */
872     gtk_follow_info->streamwindow = streamwindow;
873
874     gtk_widget_set_name(streamwindow, title);
875     gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
876     gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
877
878     /* setup the container */
879     vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 6, FALSE);
880     gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
881
882     /* content frame */
883     stream_fr = gtk_frame_new("Stream Content");
884     gtk_box_pack_start(GTK_BOX (vbox), stream_fr, TRUE, TRUE, 0);
885     gtk_widget_show(stream_fr);
886
887     stream_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 6, FALSE);
888     gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
889     gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
890
891     /* create a scrolled window for the text */
892     txt_scrollw = scrolled_window_new(NULL, NULL);
893     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw), GTK_SHADOW_IN);
894     gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
895
896     /* create a text box */
897     text = gtk_text_view_new();
898     gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
899     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
900
901     gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
902     gtk_follow_info->text = text;
903
904     /* direction hbox */
905     direction_hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
906     gtk_box_pack_start(GTK_BOX(stream_vb), direction_hbox, FALSE, FALSE, 0);
907
908     stream_cmb = gtk_combo_box_text_new();
909
910     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb), both_directions_string);
911     follow_info->show_stream = BOTH_HOSTS;
912
913     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb), client_to_server_string);
914
915     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb), server_to_client_string);
916
917     gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect  */
918                                                             /*  so callback not triggered     */
919
920     g_signal_connect(stream_cmb, "changed", G_CALLBACK(follow_stream_direction_changed), follow_info);
921
922     gtk_widget_set_tooltip_text(stream_cmb, "Select the stream direction to display");
923     gtk_box_pack_start(GTK_BOX(direction_hbox), stream_cmb, TRUE, TRUE, 0);
924
925     /* stream hbox */
926     hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1, FALSE);
927     gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
928
929     /* Create Find Button */
930     button = ws_gtk_button_new_from_stock(GTK_STOCK_FIND);
931     g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
932     gtk_widget_set_tooltip_text(button, "Find text in the displayed content");
933     gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
934
935     /* Create Save As Button */
936     button = ws_gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
937     g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
938     gtk_widget_set_tooltip_text(button, "Save the content as currently displayed");
939     gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
940
941     /* Create Print Button */
942     button = ws_gtk_button_new_from_stock(GTK_STOCK_PRINT);
943     g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
944     gtk_widget_set_tooltip_text(button, "Print the content as currently displayed");
945     gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
946
947     /* ASCII radio button */
948     radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
949     gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"ASCII\" format");
950     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), IS_SHOW_TYPE(SHOW_ASCII));
951     gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
952     g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb), follow_info);
953     gtk_follow_info->ascii_bt = radio_bt;
954
955     /* EBCDIC radio button */
956     radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt)),
957                                                 "EBCDIC");
958     gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"EBCDIC\" format");
959     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), IS_SHOW_TYPE(SHOW_EBCDIC));
960     gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
961     g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb), follow_info);
962     gtk_follow_info->ebcdic_bt = radio_bt;
963
964     /* HEX DUMP radio button */
965     radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt)),
966                                                "Hex Dump");
967     gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"Hexdump\" format");
968     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), IS_SHOW_TYPE(SHOW_HEXDUMP));
969     gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
970     g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),follow_info);
971     gtk_follow_info->hexdump_bt = radio_bt;
972
973     /* C Array radio button */
974     radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt)),
975                                                "C Arrays");
976     gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"C Array\" format");
977     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), IS_SHOW_TYPE(SHOW_CARRAY));
978     gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
979     g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb), follow_info);
980     gtk_follow_info->carray_bt = radio_bt;
981
982     /* Raw radio button */
983     radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_bt)),
984                                                  "Raw");
985     gtk_widget_set_tooltip_text(radio_bt,
986                                     "Stream data output in \"Raw\" (binary) format. "
987                                     "As this contains non printable characters, "
988                                     "the screen output will be in ASCII format");
989     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), IS_SHOW_TYPE(SHOW_RAW));
990     gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
991     g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb), follow_info);
992     gtk_follow_info->raw_bt = radio_bt;
993
994     /* Button row: help, filter out, close button */
995     bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM, GTK_STOCK_CLOSE, GTK_STOCK_HELP,
996                               NULL);
997     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
998
999
1000     button = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
1001     gtk_widget_set_tooltip_text(button, "Build a display filter which cuts this stream from the capture");
1002     g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream), follow_info);
1003
1004     button = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1005     window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
1006     gtk_widget_set_tooltip_text(button, "Close the dialog and keep the current display filter");
1007     gtk_widget_grab_default(button);
1008
1009     button = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1010     g_signal_connect(button, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_FOLLOW_STREAM_DIALOG);
1011
1012     /* Tuck away the follow_info object into the window */
1013     g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
1014
1015     follow_load_text(follow_info);
1016     remember_follow_info(follow_info);
1017
1018
1019     g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1020     g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
1021
1022     /* Make sure this widget gets destroyed if we quit the main loop,
1023        so that if we exit, we clean up any temporary files we have
1024        for "Follow TCP Stream" windows.
1025        gtk_quit_add_destroy is deprecated and should not be used in newly-written code.
1026        This function is going to be removed in GTK+ 3.0
1027        gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
1028        */
1029
1030     gtk_widget_show_all(streamwindow);
1031     window_present(streamwindow);
1032 }
1033
1034 /* The destroy call back has the responsibility of
1035  * unlinking the temporary file
1036  * and freeing the filter_out_filter */
1037 static void
1038 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
1039 {
1040     follow_info_t *follow_info;
1041     gtk_follow_info_t *gtk_follow_info;
1042
1043     follow_info = (follow_info_t *)g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
1044     gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
1045
1046     forget_follow_info(follow_info);
1047     g_free(gtk_follow_info);
1048     follow_info_free(follow_info);
1049     gtk_widget_destroy(w);
1050 }
1051
1052 static frs_return_t
1053 follow_show(follow_info_t *follow_info,
1054             follow_print_line_func follow_print,
1055             char *buffer, size_t nchars, gboolean is_from_server, void *arg,
1056             guint32 *global_pos, guint32 *server_packet_count,
1057             guint32 *client_packet_count)
1058 {
1059     gchar initbuf[256];
1060     guint32 current_pos;
1061     static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1062     gtk_follow_info_t *gtk_follow_info = (gtk_follow_info_t *)follow_info->gui_data;
1063
1064     switch (gtk_follow_info->show_type) {
1065
1066     case SHOW_EBCDIC:
1067         /* If our native arch is ASCII, call: */
1068         EBCDIC_to_ASCII(buffer, (guint) nchars);
1069         if (!follow_print(buffer, nchars, is_from_server, arg))
1070             return FRS_PRINT_ERROR;
1071         break;
1072
1073     case SHOW_ASCII:
1074         /* If our native arch is EBCDIC, call:
1075          * ASCII_TO_EBCDIC(buffer, nchars);
1076          */
1077         if (!follow_print(buffer, nchars, is_from_server, arg))
1078             return FRS_PRINT_ERROR;
1079         break;
1080
1081     case SHOW_RAW:
1082         /* Don't translate, no matter what the native arch
1083          * is.
1084          */
1085         if (!follow_print(buffer, nchars, is_from_server, arg))
1086             return FRS_PRINT_ERROR;
1087         break;
1088
1089     case SHOW_HEXDUMP:
1090         current_pos = 0;
1091         while (current_pos < nchars) {
1092             gchar hexbuf[256];
1093             int i;
1094             gchar *cur = hexbuf, *ascii_start;
1095
1096             /* is_from_server indentation : put 4 spaces at the
1097              * beginning of the string */
1098             /* XXX - We might want to prepend each line with "C" or "S" instead. */
1099             if (is_from_server && follow_info->show_stream == BOTH_HOSTS) {
1100                 memset(cur, ' ', 4);
1101                 cur += 4;
1102             }
1103             cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
1104             /* 49 is space consumed by hex chars */
1105             ascii_start = cur + 49;
1106             for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1107                 *cur++ =
1108                     hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1109                 *cur++ =
1110                     hexchars[buffer[current_pos + i] & 0x0f];
1111                 *cur++ = ' ';
1112                 if (i == 7)
1113                     *cur++ = ' ';
1114             }
1115             /* Fill it up if column isn't complete */
1116             while (cur < ascii_start)
1117                 *cur++ = ' ';
1118
1119             /* Now dump bytes as text */
1120             for (i = 0; i < 16 && current_pos + i < nchars; i++) {
1121                 *cur++ =
1122                     (g_ascii_isprint(buffer[current_pos + i]) ?
1123                      buffer[current_pos + i] : '.' );
1124                 if (i == 7) {
1125                     *cur++ = ' ';
1126                 }
1127             }
1128             current_pos += i;
1129             (*global_pos) += i;
1130             *cur++ = '\n';
1131             *cur = 0;
1132             if (!follow_print(hexbuf, strlen(hexbuf), is_from_server, arg))
1133                 return FRS_PRINT_ERROR;
1134         }
1135         break;
1136
1137     case SHOW_CARRAY:
1138         current_pos = 0;
1139         g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
1140                is_from_server ? 1 : 0,
1141                is_from_server ? (*server_packet_count)++ : (*client_packet_count)++);
1142         if (!follow_print(initbuf, strlen(initbuf), is_from_server, arg))
1143             return FRS_PRINT_ERROR;
1144
1145         while (current_pos < nchars) {
1146             gchar hexbuf[256];
1147             int i, cur;
1148
1149             cur = 0;
1150             for (i = 0; i < 8 && current_pos + i < nchars; i++) {
1151                 /* Prepend entries with "0x" */
1152                 hexbuf[cur++] = '0';
1153                 hexbuf[cur++] = 'x';
1154                 hexbuf[cur++] = hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
1155                 hexbuf[cur++] = hexchars[buffer[current_pos + i] & 0x0f];
1156
1157                 /* Delimit array entries with a comma */
1158                 if (current_pos + i + 1 < nchars)
1159                     hexbuf[cur++] = ',';
1160
1161                 hexbuf[cur++] = ' ';
1162             }
1163
1164             /* Terminate the array if we are at the end */
1165             if (current_pos + i == nchars) {
1166                 hexbuf[cur++] = '}';
1167                 hexbuf[cur++] = ';';
1168             }
1169
1170             current_pos += i;
1171             (*global_pos) += i;
1172             hexbuf[cur++] = '\n';
1173             hexbuf[cur] = 0;
1174             if (!follow_print(hexbuf, strlen(hexbuf), is_from_server, arg))
1175                 return FRS_PRINT_ERROR;
1176         }
1177         break;
1178
1179     case SHOW_YAML:
1180     case SHOW_UTF8:
1181     case SHOW_UTF16:
1182         g_assert_not_reached();
1183         break;
1184     }
1185
1186     return FRS_OK;
1187 }
1188
1189 /* Follow the TCP stream, if any, to which the last packet that we called
1190    a dissection routine on belongs (this might be the most recently
1191    selected packet, or it might be the last packet in the file). */
1192 void
1193 follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_)
1194 {
1195     register_follow_t* follower = get_follow_by_name("TCP");
1196
1197     follow_stream_cb(follower, follow_common_read_stream, w, data);
1198 }
1199
1200 /* Follow the UDP stream, if any, to which the last packet that we called
1201    a dissection routine on belongs (this might be the most recently
1202    selected packet, or it might be the last packet in the file). */
1203 void
1204 follow_udp_stream_cb(GtkWidget * w _U_, gpointer data _U_)
1205 {
1206     register_follow_t* follower = get_follow_by_name("UDP");
1207
1208     follow_stream_cb(follower, follow_common_read_stream, w, data);
1209 }
1210
1211 /* Follow the HTTP stream, if any, to which the last packet that we called
1212    a dissection routine on belongs (this might be the most recently
1213    selected packet, or it might be the last packet in the file). */
1214 void
1215 follow_http_stream_cb(GtkWidget * w _U_, gpointer data _U_)
1216 {
1217     register_follow_t* follower = get_follow_by_name("HTTP");
1218
1219     follow_stream_cb(follower, follow_common_read_stream, w, data);
1220 }
1221
1222 /* Follow the SSL stream, if any, to which the last packet that we called
1223    a dissection routine on belongs (this might be the most recently
1224    selected packet, or it might be the last packet in the file). */
1225 void
1226 follow_ssl_stream_cb(GtkWidget * w _U_, gpointer data _U_)
1227 {
1228     register_follow_t* follower = get_follow_by_name("SSL");
1229
1230     follow_stream_cb(follower, follow_common_read_stream, w, data);
1231 }
1232
1233 static void
1234 follow_redraw(gpointer data, gpointer user_data _U_)
1235 {
1236     follow_load_text((follow_info_t *)data);
1237 }
1238
1239 /* Redraw the text in all "Follow Stream" windows. */
1240 void
1241 follow_stream_redraw_all(void)
1242 {
1243     g_list_foreach(follow_infos, follow_redraw, NULL);
1244 }
1245
1246 /*
1247  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1248  *
1249  * Local variables:
1250  * c-basic-offset: 4
1251  * tab-width: 8
1252  * indent-tabs-mode: nil
1253  * End:
1254  *
1255  * vi: set shiftwidth=4 tabstop=8 expandtab:
1256  * :indentSize=4:tabSize=8:noTabs=true:
1257  */