5 * Ethereal - Network traffic analyzer
6 * By Gerald Combs <gerald@ethereal.com>
7 * Copyright 2000 Gerald Combs
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.
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.
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.
42 #include "file_util.h"
46 #include "follow_dlg.h"
47 #include <epan/follow.h>
48 #include "dlg_utils.h"
52 #include "alert_box.h"
53 #include "simple_dialog.h"
54 #include <epan/dissectors/packet-ipv6.h>
55 #include <epan/prefs.h>
56 #include <epan/addr_resolv.h>
57 #include <epan/charsets.h>
59 #include "gui_utils.h"
60 #include <epan/epan_dissect.h>
61 #include <epan/filesystem.h>
62 #include "compat_macros.h"
63 #include <epan/ipproto.h>
64 #include "print_mswin.h"
65 #include "font_utils.h"
68 /* This is backwards-compatibility code for old versions of GTK+ (2.2.1 and
69 * earlier). It defines the new wrap behavior (unknown in earlier versions)
70 * as the old (slightly buggy) wrap behavior.
72 #ifndef GTK_WRAP_WORD_CHAR
73 #define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
93 show_stream_t show_stream;
94 show_type_t show_type;
95 char data_out_filename[128 + 1];
99 GtkWidget *hexdump_bt;
100 GtkWidget *carray_bt;
102 GtkWidget *follow_save_as_w;
104 char *filter_out_filter;
105 GtkWidget *filter_te;
106 GtkWidget *streamwindow;
109 static void follow_destroy_cb(GtkWidget * win, gpointer data);
110 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
111 static void follow_load_text(follow_info_t *follow_info);
112 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
113 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
114 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
115 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
116 static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
117 static void follow_stream_om_both(GtkWidget * w, gpointer data);
118 static void follow_stream_om_client(GtkWidget * w, gpointer data);
119 static void follow_stream_om_server(GtkWidget * w, gpointer data);
122 /* With MSVC and a libethereal.dll, we need a special declaration. */
123 ETH_VAR_IMPORT FILE *data_out_file;
125 #define E_FOLLOW_INFO_KEY "follow_info_key"
127 /* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
128 so we can redraw them all if the colors or font changes. */
129 static GList *follow_infos;
131 /* Add a "follow_info_t" structure to the list. */
133 remember_follow_info(follow_info_t *follow_info)
135 follow_infos = g_list_append(follow_infos, follow_info);
138 /* Remove a "follow_info_t" structure from the list. */
140 forget_follow_info(follow_info_t *follow_info)
142 follow_infos = g_list_remove(follow_infos, follow_info);
146 follow_redraw(gpointer data, gpointer user_data _U_)
148 follow_load_text((follow_info_t *)data);
151 /* Redraw the text in all "Follow TCP Stream" windows. */
153 follow_redraw_all(void)
155 g_list_foreach(follow_infos, follow_redraw, NULL);
158 /* Follow the TCP stream, if any, to which the last packet that we called
159 a dissection routine on belongs (this might be the most recently
160 selected packet, or it might be the last packet in the file). */
162 follow_stream_cb(GtkWidget * w, gpointer data _U_)
164 GtkWidget *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
165 GtkWidget *hbox, *bbox, *button, *radio_bt;
166 GtkWidget *stream_fr, *stream_vb;
167 GtkWidget *stream_om, *stream_menu, *stream_mi;
168 GtkTooltips *tooltips;
170 gchar *follow_filter;
171 const gchar *previous_filter;
172 int filter_out_filter_len;
173 const char *hostname0, *hostname1;
176 follow_tcp_stats_t stats;
177 follow_info_t *follow_info;
179 /* we got tcp so we can follow */
180 if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
181 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
182 "Error following stream. Please make\n"
183 "sure you have a TCP packet selected.");
187 follow_info = g_new0(follow_info_t, 1);
189 /* Create a temporary file into which to dump the reassembled data
190 from the TCP stream, and set "data_out_file" to refer to it, so
191 that the TCP code will write to it.
193 XXX - it might be nicer to just have the TCP code directly
194 append stuff to the text widget for the TCP stream window,
195 if we can arrange that said window not pop up until we're
197 tmp_fd = create_tempfile(follow_info->data_out_filename,
198 sizeof follow_info->data_out_filename, "follow");
201 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
202 "Could not create temporary file %s: %s",
203 follow_info->data_out_filename, strerror(errno));
208 data_out_file = fdopen(tmp_fd, "wb");
209 if (data_out_file == NULL) {
210 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
211 "Could not create temporary file %s: %s",
212 follow_info->data_out_filename, strerror(errno));
214 unlink(follow_info->data_out_filename);
219 /* Create a new filter that matches all packets in the TCP stream,
220 and set the display filter entry accordingly */
221 reset_tcp_reassembly();
222 follow_filter = build_follow_filter(&cfile.edt->pi);
224 /* Set the display filter entry accordingly */
225 filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
227 /* needed in follow_filter_out_stream(), is there a better way? */
228 follow_info->filter_te = filter_te;
230 /* save previous filter, const since we're not supposed to alter */
232 (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
234 /* allocate our new filter. API claims g_malloc terminates program on failure */
235 /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
236 filter_out_filter_len = strlen(follow_filter) + strlen(previous_filter) + 16;
237 follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
239 /* append the negation */
240 if(strlen(previous_filter)) {
241 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
242 "%s and !(%s)", previous_filter, follow_filter);
244 g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
245 "!(%s)", follow_filter);
249 gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
251 /* Run the display filter so it goes in effect - even if it's the
252 same as the previous display filter. */
253 main_filter_packets(&cfile, follow_filter, TRUE);
255 /* Free the filter string, as we're done with it. */
256 g_free(follow_filter);
258 /* The data_out_file should now be full of the streams information */
259 fclose(data_out_file);
261 /* The data_out_filename file now has all the text that was in the session */
262 streamwindow = dlg_window_new("Follow TCP Stream");
264 /* needed in follow_filter_out_stream(), is there a better way? */
265 follow_info->streamwindow = streamwindow;
267 gtk_widget_set_name(streamwindow, "TCP stream window");
268 gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
269 gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
271 /* setup the container */
272 tooltips = gtk_tooltips_new ();
274 vbox = gtk_vbox_new(FALSE, 6);
275 gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
278 if (incomplete_tcp_stream) {
279 stream_fr = gtk_frame_new("Stream Content (incomplete)");
281 stream_fr = gtk_frame_new("Stream Content");
283 gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
284 gtk_widget_show(stream_fr);
286 stream_vb = gtk_vbox_new(FALSE, 6);
287 gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
288 gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
290 /* create a scrolled window for the text */
291 txt_scrollw = scrolled_window_new(NULL, NULL);
292 #if GTK_MAJOR_VERSION >= 2
293 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
296 gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
298 /* create a text box */
299 #if GTK_MAJOR_VERSION < 2
300 text = gtk_text_new(NULL, NULL);
301 gtk_text_set_editable(GTK_TEXT(text), FALSE);
303 text = gtk_text_view_new();
304 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
305 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
307 gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
308 follow_info->text = text;
312 hbox = gtk_hbox_new(FALSE, 1);
313 gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
315 /* Create Save As Button */
316 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
317 SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
318 gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
319 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
321 /* Create Print Button */
322 button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_PRINT);
323 SIGNAL_CONNECT(button, "clicked", follow_print_stream, follow_info);
324 gtk_tooltips_set_tip (tooltips, button, "Print the content as currently displayed", NULL);
325 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
328 follow_tcp_stats(&stats);
331 struct e_in6_addr ipaddr;
332 memcpy(&ipaddr, stats.ip_address[0], 16);
333 hostname0 = get_hostname6(&ipaddr);
334 memcpy(&ipaddr, stats.ip_address[0], 16);
335 hostname1 = get_hostname6(&ipaddr);
338 memcpy(&ipaddr, stats.ip_address[0], 4);
339 hostname0 = get_hostname(ipaddr);
340 memcpy(&ipaddr, stats.ip_address[1], 4);
341 hostname1 = get_hostname(ipaddr);
344 port0 = get_tcp_port(stats.tcp_port[0]);
345 port1 = get_tcp_port(stats.tcp_port[1]);
347 follow_info->is_ipv6 = stats.is_ipv6;
349 stream_om = gtk_option_menu_new();
350 stream_menu = gtk_menu_new();
352 /* Both Stream Directions */
353 g_snprintf(string, sizeof(string),
354 "Entire conversation (%u bytes)",
355 stats.bytes_written[0] + stats.bytes_written[1]);
356 stream_mi = gtk_menu_item_new_with_label(string);
357 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
359 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
360 gtk_widget_show(stream_mi);
361 follow_info->show_stream = BOTH_HOSTS;
363 /* Host 0 --> Host 1 */
364 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
365 hostname0, port0, hostname1, port1,
366 stats.bytes_written[0]);
367 stream_mi = gtk_menu_item_new_with_label(string);
368 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
370 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
371 gtk_widget_show(stream_mi);
373 /* Host 1 --> Host 0 */
374 g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
375 hostname1, port1, hostname0, port0,
376 stats.bytes_written[1]);
377 stream_mi = gtk_menu_item_new_with_label(string);
378 SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
380 gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
381 gtk_widget_show(stream_mi);
383 gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
384 /* Set history to 0th item, i.e., the first item. */
385 gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
386 gtk_tooltips_set_tip (tooltips, stream_om,
387 "Select the stream direction to display", NULL);
388 gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
390 /* ASCII radio button */
391 radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
392 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
393 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
394 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
395 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
397 follow_info->ascii_bt = radio_bt;
398 follow_info->show_type = SHOW_ASCII;
400 /* EBCDIC radio button */
401 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
402 (GTK_RADIO_BUTTON(radio_bt)),
404 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"EBCDIC\" format", NULL);
405 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
406 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
407 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
409 follow_info->ebcdic_bt = radio_bt;
411 /* HEX DUMP radio button */
412 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
413 (GTK_RADIO_BUTTON(radio_bt)),
415 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
416 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
417 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
418 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
420 follow_info->hexdump_bt = radio_bt;
422 /* C Array radio button */
423 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
424 (GTK_RADIO_BUTTON(radio_bt)),
426 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
427 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
428 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
429 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
431 follow_info->carray_bt = radio_bt;
433 /* Raw radio button */
434 radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
435 (GTK_RADIO_BUTTON(radio_bt)),
437 gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Raw\" (binary) format. "
438 "As this contains non printable characters, the screen output will be in ASCII format", NULL);
439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
440 gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
441 SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
443 follow_info->raw_bt = radio_bt;
445 /* Button row: (help), filter out, close button */
446 if(topic_available(HELP_FILESET_DIALOG)) {
447 bbox = dlg_button_row_new(ETHEREAL_STOCK_FILTER_OUT_STREAM, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
449 bbox = dlg_button_row_new(ETHEREAL_STOCK_FILTER_OUT_STREAM, GTK_STOCK_CLOSE, NULL);
451 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
454 button = OBJECT_GET_DATA(bbox, ETHEREAL_STOCK_FILTER_OUT_STREAM);
455 gtk_tooltips_set_tip (tooltips, button,
456 "Build a display filter which cuts this stream from the capture", NULL);
457 SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
459 button = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
460 window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
461 gtk_tooltips_set_tip (tooltips, button,
462 "Close the dialog and keep the current display filter", NULL);
463 gtk_widget_grab_default(button);
465 if(topic_available(HELP_FILESET_DIALOG)) {
466 button = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
467 SIGNAL_CONNECT(button, "clicked", topic_cb, HELP_FOLLOW_TCP_STREAM_DIALOG);
470 /* Tuck away the follow_info object into the window */
471 OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
473 follow_load_text(follow_info);
474 remember_follow_info(follow_info);
476 data_out_file = NULL;
478 SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
479 SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
481 /* Make sure this widget gets destroyed if we quit the main loop,
482 so that if we exit, we clean up any temporary files we have
483 for "Follow TCP Stream" windows. */
484 gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
486 gtk_widget_show_all(streamwindow);
487 window_present(streamwindow);
490 /* The destroy call back has the responsibility of
491 * unlinking the temporary file
492 * and freeing the filter_out_filter */
494 follow_destroy_cb(GtkWidget *w, gpointer data _U_)
496 follow_info_t *follow_info;
499 follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
500 i = unlink(follow_info->data_out_filename);
502 g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
503 follow_info->data_out_filename, strerror(errno), errno);
505 g_free(follow_info->filter_out_filter);
506 forget_follow_info(follow_info);
510 /* XXX - can I emulate follow_charset_toggle_cb() instead of having
511 * 3 different functions here?
512 * That might not be a bad idea, as it might mean we only reload
513 * the window once, not twice - see follow_charset_toggle_cb()
514 * for an explanation. */
516 follow_stream_om_both(GtkWidget *w _U_, gpointer data)
518 follow_info_t *follow_info = data;
519 follow_info->show_stream = BOTH_HOSTS;
520 follow_load_text(follow_info);
524 follow_stream_om_client(GtkWidget *w _U_, gpointer data)
526 follow_info_t *follow_info = data;
527 follow_info->show_stream = FROM_CLIENT;
528 follow_load_text(follow_info);
532 follow_stream_om_server(GtkWidget *w _U_, gpointer data)
534 follow_info_t *follow_info = data;
535 follow_info->show_stream = FROM_SERVER;
536 follow_load_text(follow_info);
540 /* Handles the display style toggling */
542 follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
544 follow_info_t *follow_info = data;
547 * A radio button toggles when it goes on and when it goes
548 * off, so when you click a radio button two signals are
549 * delivered. We only want to reprocess the display once,
550 * so we do it only when the button goes on.
552 if (GTK_TOGGLE_BUTTON(w)->active) {
553 if (w == follow_info->ebcdic_bt)
554 follow_info->show_type = SHOW_EBCDIC;
555 else if (w == follow_info->hexdump_bt)
556 follow_info->show_type = SHOW_HEXDUMP;
557 else if (w == follow_info->carray_bt)
558 follow_info->show_type = SHOW_CARRAY;
559 else if (w == follow_info->ascii_bt)
560 follow_info->show_type = SHOW_ASCII;
561 else if (w == follow_info->raw_bt)
562 follow_info->show_type = SHOW_RAW;
563 follow_load_text(follow_info);
567 #define FLT_BUF_SIZE 1024
577 * XXX - the routine pointed to by "print_line" doesn't get handed lines,
578 * it gets handed bufferfuls. That's fine for "follow_write_raw()"
579 * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
580 * the "print_line()" routine from "print.c", and as that routine might
581 * genuinely expect to be handed a line (if, for example, it's using
582 * some OS or desktop environment's printing API, and that API expects
583 * to be handed lines), "follow_print_text()" should probably accumulate
584 * lines in a buffer and hand them "print_line()". (If there's a
585 * complete line in a buffer - i.e., there's nothing of the line in
586 * the previous buffer or the next buffer - it can just hand that to
587 * "print_line()" after filtering out non-printables, as an
590 * This might or might not be the reason why C arrays display
591 * correctly but get extra blank lines very other line when printed.
594 follow_read_stream(follow_info_t *follow_info,
595 gboolean (*print_line) (char *, size_t, gboolean, void *),
600 guint8 client_addr[MAX_IPADDR_LEN];
601 guint16 client_port = 0;
603 guint32 current_pos, global_client_pos = 0, global_server_pos = 0;
607 guint32 server_packet_count = 0;
608 guint32 client_packet_count = 0;
609 char buffer[FLT_BUF_SIZE];
611 static const gchar hexchars[16] = "0123456789abcdef";
613 iplen = (follow_info->is_ipv6) ? 16 : 4;
615 data_out_file = eth_fopen(follow_info->data_out_filename, "rb");
616 if (data_out_file == NULL) {
617 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
618 "Could not open temporary file %s: %s", follow_info->data_out_filename,
620 return FRS_OPEN_ERROR;
623 while (fread(&sc, 1, sizeof(sc), data_out_file)) {
624 if (client_port == 0) {
625 memcpy(client_addr, sc.src_addr, iplen);
626 client_port = sc.src_port;
629 if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
630 client_port == sc.src_port) {
632 global_pos = &global_client_pos;
633 if (follow_info->show_stream == FROM_SERVER) {
639 global_pos = &global_server_pos;
640 if (follow_info->show_stream == FROM_CLIENT) {
645 while (sc.dlen > 0) {
646 bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
647 nchars = fread(buffer, 1, bcount, data_out_file);
653 switch (follow_info->show_type) {
656 /* If our native arch is ASCII, call: */
657 EBCDIC_to_ASCII(buffer, nchars);
658 if (!(*print_line) (buffer, nchars, is_server, arg))
663 /* If our native arch is EBCDIC, call:
664 * ASCII_TO_EBCDIC(buffer, nchars);
666 if (!(*print_line) (buffer, nchars, is_server, arg))
671 /* Don't translate, no matter what the native arch
674 if (!(*print_line) (buffer, nchars, is_server, arg))
680 while (current_pos < nchars) {
683 gchar *cur = hexbuf, *ascii_start;
685 /* is_server indentation : put 78 spaces at the
686 * beginning of the string */
687 if (is_server && follow_info->show_stream == BOTH_HOSTS) {
688 memset(cur, ' ', 78);
691 cur += g_snprintf(cur, 20, "%08X ", *global_pos);
692 /* 49 is space consumed by hex chars */
693 ascii_start = cur + 49;
694 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
696 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
698 hexchars[buffer[current_pos + i] & 0x0f];
703 /* Fill it up if column isn't complete */
704 while (cur < ascii_start)
707 /* Now dump bytes as text */
708 for (i = 0; i < 16 && current_pos + i < nchars; i++) {
710 (isprint((guchar)buffer[current_pos + i]) ?
711 buffer[current_pos + i] : '.' );
720 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
727 g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
729 is_server ? server_packet_count++ : client_packet_count++);
730 if (!(*print_line) (initbuf, strlen(initbuf), is_server, arg))
732 while (current_pos < nchars) {
737 for (i = 0; i < 8 && current_pos + i < nchars; i++) {
738 /* Prepend entries with "0x" */
742 hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
744 hexchars[buffer[current_pos + i] & 0x0f];
746 /* Delimit array entries with a comma */
747 if (current_pos + i + 1 < nchars)
753 /* Terminate the array if we are at the end */
754 if (current_pos + i == nchars) {
761 hexbuf[cur++] = '\n';
763 if (!(*print_line) (hexbuf, strlen(hexbuf), is_server, arg))
771 if (ferror(data_out_file)) {
772 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
773 "Error reading temporary file %s: %s", follow_info->data_out_filename,
775 fclose(data_out_file);
776 data_out_file = NULL;
777 return FRS_READ_ERROR;
780 fclose(data_out_file);
781 data_out_file = NULL;
785 fclose(data_out_file);
786 data_out_file = NULL;
787 return FRS_PRINT_ERROR;
791 * XXX - for text printing, we probably want to wrap lines at 80 characters;
792 * (PostScript printing is doing this already), and perhaps put some kind of
793 * dingbat (to use the technical term) to indicate a wrapped line, along the
794 * lines of what's done when displaying this in a window, as per Warren Young's
798 follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
800 print_stream_t *stream = arg;
804 /* convert non printable characters */
805 for (i = 0; i < nchars; i++) {
806 if (buffer[i] == '\n' || buffer[i] == '\r')
808 if (! isprint((guchar)buffer[i])) {
813 /* convert unterminated char array to a zero terminated string */
814 str = g_malloc(nchars + 1);
815 memcpy(str, buffer, nchars);
817 print_line(stream, /*indent*/ 0, str);
824 follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
829 nwritten = fwrite(buffer, 1, nchars, fh);
830 if (nwritten != nchars)
837 follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
839 follow_info_t *follow_info = data;
841 /* Lock out user from messing with us. (ie. don't free our data!) */
842 gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
844 /* Set the display filter. */
845 gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
847 /* Run the display filter so it goes in effect. */
848 main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
850 /* we force a subsequent close */
851 window_destroy(follow_info->streamwindow);
857 follow_print_stream(GtkWidget * w _U_, gpointer data)
859 print_stream_t *stream;
862 follow_info_t *follow_info = data;
864 gboolean win_printer = FALSE;
867 switch (prefs.pr_dest) {
871 /*XXX should use temp file stuff in util routines */
872 print_dest = g_strdup(tmpnam(NULL));
875 print_dest = prefs.pr_cmd;
880 print_dest = prefs.pr_file;
883 default: /* "Can't happen" */
884 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
885 "Couldn't figure out where to send the print "
886 "job. Check your preferences.");
890 switch (prefs.pr_format) {
893 stream = print_stream_text_new(to_file, print_dest);
897 stream = print_stream_ps_new(to_file, print_dest);
901 g_assert_not_reached();
904 if (stream == NULL) {
906 open_failure_alert_box(prefs.pr_file, errno, TRUE);
908 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
909 "Couldn't run print command %s.", prefs.pr_cmd);
914 if (!print_preamble(stream, cfile.filename))
917 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
922 /* XXX - cancel printing? */
923 destroy_print_stream(stream);
925 case FRS_PRINT_ERROR:
929 if (!print_finale(stream))
932 if (!destroy_print_stream(stream)) {
934 write_failure_alert_box(prefs.pr_file, errno);
936 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
937 "Error closing print destination.");
942 print_mswin(print_dest);
944 /* trash temp file */
945 eth_remove(print_dest);
952 write_failure_alert_box(prefs.pr_file, errno);
954 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
955 "Error writing to print command: %s", strerror(errno));
957 /* XXX - cancel printing? */
958 destroy_print_stream(stream);
962 /* trash temp file */
963 eth_remove(print_dest);
968 /* static variable declarations to speed up the performance
969 * of follow_load_text and follow_add_to_gtk_text
971 static GdkColor server_fg, server_bg;
972 static GdkColor client_fg, client_bg;
973 #if GTK_MAJOR_VERSION >= 2
974 static GtkTextTag *server_tag, *client_tag;
978 follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
981 GtkWidget *text = arg;
982 #if GTK_MAJOR_VERSION >= 2
983 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
987 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
988 gboolean line_break = FALSE;
989 /* While our isprint() hack is in place, we
990 * have to use convert some chars to '.' in order
991 * to be able to see the data we *should* see
992 * in the GtkText widget.
996 for (i = 0; i < nchars; i++) {
997 if (buffer[i] == '\n' || buffer[i] == '\r')
1002 if (! isprint(buffer[i])) {
1007 /* XXX - workaround for bug 852
1008 * Force a line break so that the text view
1009 * doesn't blow up on excessive long lines.
1011 if (line_break == FALSE)
1015 #if GTK_MAJOR_VERSION < 2
1017 gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &server_fg,
1018 &server_bg, buffer, nchars);
1020 gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &client_fg,
1021 &client_bg, buffer, nchars);
1024 gtk_text_buffer_get_end_iter(buf, &iter);
1026 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars,
1029 gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars,
1037 follow_load_text(follow_info_t *follow_info)
1039 #if GTK_MAJOR_VERSION < 2
1044 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
1047 /* prepare colors one time for repeated use by follow_add_to_gtk_text */
1048 color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
1049 color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
1050 color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
1051 color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
1053 /* Delete any info already in text box */
1054 #if GTK_MAJOR_VERSION < 2
1055 bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
1056 if (bytes_already > 0) {
1057 gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
1058 gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
1061 /* stop the updates while we fill the text box */
1062 gtk_text_freeze(GTK_TEXT(follow_info->text));
1064 /* prepare tags one time for repeated use by follow_add_to_gtk_text */
1065 server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &server_fg,
1066 "background-gdk", &server_bg, "font-desc",
1067 user_font_get_regular(), NULL);
1068 client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &client_fg,
1069 "background-gdk", &client_bg, "font-desc",
1070 user_font_get_regular(), NULL);
1072 gtk_text_buffer_set_text(buf, "", -1);
1074 follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
1075 #if GTK_MAJOR_VERSION < 2
1076 gtk_text_thaw(GTK_TEXT(follow_info->text));
1082 * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
1083 * any, so that if somebody tries to do "Save"
1084 * while there's already a "Save TCP Follow Stream" window up, we just pop
1085 * up the existing one, rather than creating a new one.
1088 follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
1091 follow_info_t *follow_info = data;
1093 if (follow_info->follow_save_as_w != NULL) {
1094 /* There's already a dialog box; reactivate it. */
1095 reactivate_window(follow_info->follow_save_as_w);
1099 new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
1100 FILE_SELECTION_SAVE);
1101 follow_info->follow_save_as_w = new_win;
1103 /* Tuck away the follow_info object into the window */
1104 OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
1106 SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
1108 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1109 if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
1111 follow_save_as_ok_cb(new_win, new_win);
1113 window_destroy(new_win);
1116 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1117 pointer to the file selection box widget */
1118 SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
1119 "clicked", follow_save_as_ok_cb, new_win);
1121 window_set_cancel_button(new_win,
1122 GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
1124 gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
1126 SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
1128 gtk_widget_show_all(new_win);
1129 window_present(new_win);
1135 follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
1138 follow_info_t *follow_info;
1140 print_stream_t *stream = NULL;
1143 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1144 to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1146 to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
1149 /* Perhaps the user specified a directory instead of a file.
1150 Check whether they did. */
1151 if (test_for_directory(to_name) == EISDIR) {
1152 /* It's a directory - set the file selection box to display that
1153 directory, and leave the selection box displayed. */
1154 set_last_open_dir(to_name);
1156 file_selection_set_current_folder(fs, get_last_open_dir());
1160 follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
1161 if (follow_info->show_type == SHOW_RAW) {
1162 /* Write the data out as raw binary data */
1163 fh = eth_fopen(to_name, "wb");
1165 /* Write it out as text */
1166 fh = eth_fopen(to_name, "w");
1169 open_failure_alert_box(to_name, errno, TRUE);
1174 gtk_widget_hide(GTK_WIDGET(fs));
1175 window_destroy(GTK_WIDGET(fs));
1177 if (follow_info->show_type == SHOW_RAW) {
1178 switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
1180 if (fclose(fh) == EOF)
1181 write_failure_alert_box(to_name, errno);
1184 case FRS_OPEN_ERROR:
1185 case FRS_READ_ERROR:
1189 case FRS_PRINT_ERROR:
1190 write_failure_alert_box(to_name, errno);
1195 stream = print_stream_text_stdio_new(fh);
1196 switch (follow_read_stream(follow_info, follow_print_text, stream)) {
1198 if (!destroy_print_stream(stream))
1199 write_failure_alert_box(to_name, errno);
1202 case FRS_OPEN_ERROR:
1203 case FRS_READ_ERROR:
1204 destroy_print_stream(stream);
1207 case FRS_PRINT_ERROR:
1208 write_failure_alert_box(to_name, errno);
1209 destroy_print_stream(stream);
1214 /* Save the directory name for future file dialogs. */
1215 dirname = get_dirname(to_name); /* Overwrites to_name */
1216 set_last_open_dir(dirname);
1221 follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
1223 follow_info_t *follow_info = data;
1225 /* Note that we no longer have a dialog box. */
1226 follow_info->follow_save_as_w = NULL;