remove some warnings
[obnox/wireshark/wip.git] / gtk / follow_dlg.c
index 9b2fc5e5eaad367a869cb0873724954457cbb9fa..2ca7e6ff7bbcadb1950974890e2a21dad71c3729 100644 (file)
@@ -2,8 +2,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 2000 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -18,7 +18,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -30,9 +30,6 @@
 #include <stdio.h>
 #include <string.h>
 
-#ifdef HAVE_IO_H
-#include <io.h>                        /* open/close on win32 */
-#endif
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 
 #include "isprint.h"
 
+#include "file_util.h"
 #include "color.h"
 #include "colors.h"
 #include "file.h"
 #include "follow_dlg.h"
 #include <epan/follow.h>
 #include "dlg_utils.h"
+#include "file_dlg.h"
 #include "keys.h"
 #include "globals.h"
 #include "main.h"
 #include <epan/prefs.h>
 #include <epan/addr_resolv.h>
 #include <epan/charsets.h>
-#include "util.h"
-#include "ui_util.h"
+#include "tempfile.h"
+#include "gui_utils.h"
 #include <epan/epan_dissect.h>
 #include <epan/filesystem.h>
 #include "compat_macros.h"
 #include <epan/ipproto.h>
-#include "stat_menu.h"
 #include "print_mswin.h"
 #include "font_utils.h"
+#include "help_dlg.h"
+
+/* This is backwards-compatibility code for old versions of GTK+ (2.2.1 and
+ * earlier).  It defines the new wrap behavior (unknown in earlier versions)
+ * as the old (slightly buggy) wrap behavior.
+ */
+#ifndef GTK_WRAP_WORD_CHAR
+#define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
+#endif
 
 /* Show Stream */
 typedef enum {
@@ -94,6 +101,9 @@ typedef struct {
        GtkWidget       *carray_bt;
        GtkWidget       *raw_bt;
        GtkWidget       *follow_save_as_w;
+#if GTK_MAJOR_VERSION >= 2
+       GtkWidget       *find_dlg_w;
+#endif
        gboolean        is_ipv6;
        char            *filter_out_filter;
        GtkWidget       *filter_te;
@@ -104,6 +114,11 @@ static void follow_destroy_cb(GtkWidget * win, gpointer data);
 static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
 static void follow_load_text(follow_info_t *follow_info);
 static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
+#if GTK_MAJOR_VERSION >= 2
+static void follow_find_cb(GtkWidget * w, gpointer data);
+static void follow_find_button_cb(GtkWidget * w _U_, gpointer parent_w);
+static void follow_find_destroy_cb(GtkWidget * win _U_, gpointer data);
+#endif
 static void follow_print_stream(GtkWidget * w, gpointer parent_w);
 static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
 static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
@@ -113,8 +128,8 @@ static void follow_stream_om_client(GtkWidget * w, gpointer data);
 static void follow_stream_om_server(GtkWidget * w, gpointer data);
 
 
-/* With MSVC and a libethereal.dll, we need a special declaration. */
-ETH_VAR_IMPORT FILE *data_out_file;
+/* With MSVC and a libwireshark.dll, we need a special declaration. */
+WS_VAR_IMPORT FILE *data_out_file;
 
 #define E_FOLLOW_INFO_KEY "follow_info_key"
 
@@ -156,22 +171,23 @@ void
 follow_stream_cb(GtkWidget * w, gpointer data _U_)
 {
        GtkWidget       *streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
-       GtkWidget       *hbox, *button_hbox, *button, *radio_bt;
-    GtkWidget   *stream_fr, *stream_vb;
+       GtkWidget       *hbox, *bbox, *button, *radio_bt;
+       GtkWidget       *stream_fr, *stream_vb;
        GtkWidget       *stream_om, *stream_menu, *stream_mi;
-       GtkTooltips *tooltips;
-       int                 tmp_fd;
+       GtkTooltips     *tooltips;
+       int             tmp_fd;
        gchar           *follow_filter;
        const gchar     *previous_filter;
-    int                    filter_out_filter_len;
+       int             filter_out_filter_len;
        const char      *hostname0, *hostname1;
        char            *port0, *port1;
        char            string[128];
        follow_tcp_stats_t stats;
        follow_info_t   *follow_info;
+       tcp_stream_chunk sc;
 
        /* we got tcp so we can follow */
-       if (cfile.edt->pi.ipproto != 6) {
+       if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
                simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
                              "Error following stream.  Please make\n"
                              "sure you have a TCP packet selected.");
@@ -199,12 +215,12 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
            return;
        }
 
-       data_out_file = fdopen(tmp_fd, "wb");
+       data_out_file = fdopen(tmp_fd, "w+b");
        if (data_out_file == NULL) {
            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
                          "Could not create temporary file %s: %s",
                          follow_info->data_out_filename, strerror(errno));
-           close(tmp_fd);
+           eth_close(tmp_fd);
            unlink(follow_info->data_out_filename);
            g_free(follow_info);
            return;
@@ -249,11 +265,8 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
        /* Free the filter string, as we're done with it. */
        g_free(follow_filter);
 
-       /* The data_out_file should now be full of the streams information */
-       fclose(data_out_file);
-
        /* The data_out_filename file now has all the text that was in the session */
-       streamwindow = dlg_window_new("Follow TCP stream");
+       streamwindow = dlg_window_new("Follow TCP Stream");
 
        /* needed in follow_filter_out_stream(), is there a better way? */
        follow_info->streamwindow = streamwindow;
@@ -268,7 +281,7 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
        vbox = gtk_vbox_new(FALSE, 6);
        gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
 
-    /* content frame */
+       /* content frame */
        if (incomplete_tcp_stream) {
                stream_fr = gtk_frame_new("Stream Content (incomplete)");
        } else {
@@ -296,6 +309,7 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
 #else
         text = gtk_text_view_new();
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+       gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
 #endif
        gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
        follow_info->text = text;
@@ -305,6 +319,14 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
        hbox = gtk_hbox_new(FALSE, 1);
        gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
 
+#if GTK_MAJOR_VERSION >= 2
+       /* Create Find Button */
+       button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_FIND);
+       SIGNAL_CONNECT(button, "clicked", follow_find_cb, follow_info);
+       gtk_tooltips_set_tip (tooltips, button, "Find text in the displayed content", NULL);
+       gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+#endif
+
        /* Create Save As Button */
        button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
        SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
@@ -324,7 +346,7 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
          struct e_in6_addr ipaddr;
          memcpy(&ipaddr, stats.ip_address[0], 16);
          hostname0 = get_hostname6(&ipaddr);
-         memcpy(&ipaddr, stats.ip_address[0], 16);
+         memcpy(&ipaddr, stats.ip_address[1], 16);
          hostname1 = get_hostname6(&ipaddr);
        } else {
          guint32 ipaddr;
@@ -353,10 +375,28 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
        gtk_widget_show(stream_mi);
        follow_info->show_stream = BOTH_HOSTS;
 
+       /* Go back to the top of the file and read the first tcp_stream_chunk
+        * to ensure that the IP addresses and port numbers in the drop-down
+        * list are tied to the correct lines displayed by follow_read_stream()
+        * later on (which also reads from this file).  Close the file when
+        * we're done.
+        */
+
+       rewind(data_out_file);
+       fread(&sc, 1, sizeof(sc), data_out_file);
+       fclose(data_out_file);
+
        /* Host 0 --> Host 1 */
-       g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
-                hostname0, port0, hostname1, port1,
-                stats.bytes_written[0]);
+       if(sc.src_port == strtol(port0, NULL, 10)) {
+               g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+                          hostname0, port0, hostname1, port1,
+                          stats.bytes_written[0]);
+       } else {
+               g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+                          hostname1, port1, hostname0, port0,
+                          stats.bytes_written[0]);
+       }
+
        stream_mi = gtk_menu_item_new_with_label(string);
        SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
                        follow_info);
@@ -364,9 +404,16 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
        gtk_widget_show(stream_mi);
 
        /* Host 1 --> Host 0 */
-       g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
-                hostname1, port1, hostname0, port0,
-                stats.bytes_written[1]);
+       if(sc.src_port == strtol(port0, NULL, 10)) {
+               g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+                          hostname1, port1, hostname0, port0,
+                          stats.bytes_written[1]);
+       } else {
+               g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+                          hostname0, port0, hostname1, port1,
+                          stats.bytes_written[1]);
+       }
+
        stream_mi = gtk_menu_item_new_with_label(string);
        SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
                        follow_info);
@@ -435,27 +482,30 @@ follow_stream_cb(GtkWidget * w, gpointer data _U_)
                        follow_info);
        follow_info->raw_bt = radio_bt;
 
-       /* button hbox */
-       button_hbox = gtk_hbutton_box_new();
-       gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
-       gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
-       gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
+    /* Button row: (help), filter out, close button */
+    if(topic_available(HELP_FILESET_DIALOG)) {
+      bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+    } else {
+      bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM, GTK_STOCK_CLOSE, NULL);
+    }
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
 
-       /* Create exclude stream button */
-       button = gtk_button_new_with_label("Filter out this stream");
-       SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
+
+    button = OBJECT_GET_DATA(bbox, WIRESHARK_STOCK_FILTER_OUT_STREAM);
        gtk_tooltips_set_tip (tooltips, button,
         "Build a display filter which cuts this stream from the capture", NULL);
-       gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+       SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
 
-       /* Create Close Button */
-       button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
+    button = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
+       window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
        gtk_tooltips_set_tip (tooltips, button,
            "Close the dialog and keep the current display filter", NULL);
-       gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
-       GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+    gtk_widget_grab_default(button);
 
-       window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
+    if(topic_available(HELP_FILESET_DIALOG)) {
+      button = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
+      SIGNAL_CONNECT(button, "clicked", topic_cb, HELP_FOLLOW_TCP_STREAM_DIALOG);
+    }
 
        /* Tuck away the follow_info object into the window */
        OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
@@ -596,13 +646,13 @@ follow_read_stream(follow_info_t *follow_info,
     gchar               initbuf[256];
     guint32             server_packet_count = 0;
     guint32             client_packet_count = 0;
-    char                buffer[FLT_BUF_SIZE];
+    char                buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
     size_t              nchars;
     static const gchar hexchars[16] = "0123456789abcdef";
 
     iplen = (follow_info->is_ipv6) ? 16 : 4;
 
-    data_out_file = fopen(follow_info->data_out_filename, "rb");
+    data_out_file = eth_fopen(follow_info->data_out_filename, "rb");
     if (data_out_file == NULL) {
        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
                      "Could not open temporary file %s: %s", follow_info->data_out_filename,
@@ -843,6 +893,128 @@ follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
     return;
 }
 
+#if GTK_MAJOR_VERSION >= 2
+static void
+follow_find_cb(GtkWidget * w _U_, gpointer data)
+{
+    follow_info_t              *follow_info = data;
+    GtkTooltips                *tooltips;
+    GtkWidget          *find_dlg_w, *main_vb, *buttons_row, *find_lb;
+    GtkWidget          *find_hb, *find_text_box, *find_bt, *cancel_bt;
+
+    tooltips = gtk_tooltips_new();
+
+    if (follow_info->find_dlg_w != NULL) {
+           /* There's already a dialog box; reactivate it. */
+           reactivate_window(follow_info->find_dlg_w);
+           return;
+    }
+
+    /* Create the find box */
+    find_dlg_w = dlg_window_new("Wireshark: Find text");
+    gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
+                                GTK_WINDOW(follow_info->streamwindow));
+    gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
+    follow_info->find_dlg_w = find_dlg_w;
+
+    SIGNAL_CONNECT(find_dlg_w, "destroy", follow_find_destroy_cb, follow_info);
+    SIGNAL_CONNECT(find_dlg_w, "delete_event", window_delete_event_cb, NULL);
+
+    /* Main vertical box */
+    main_vb = gtk_vbox_new(FALSE, 3);
+    gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+    gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
+
+    /* Horizontal box for find label, entry field and up/down radio buttons*/
+    find_hb = gtk_hbox_new(FALSE, 3);
+    gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
+    gtk_widget_show(find_hb);
+
+    /* Find label */
+    find_lb = gtk_label_new("Find text:");
+    gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
+    gtk_widget_show(find_lb);
+
+    /* Find field */
+    find_text_box = gtk_entry_new();
+    gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
+    gtk_tooltips_set_tip(tooltips, find_text_box, "Text to search for (case sensitive)", NULL);
+    gtk_widget_show(find_text_box);
+
+    /* Buttons row */
+    buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL, NULL);
+    gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
+    find_bt = OBJECT_GET_DATA(buttons_row, GTK_STOCK_FIND);
+    cancel_bt = OBJECT_GET_DATA(buttons_row, GTK_STOCK_CANCEL);
+
+    SIGNAL_CONNECT(find_bt, "clicked", follow_find_button_cb, follow_info);
+    OBJECT_SET_DATA(find_bt, "find_string", find_text_box);
+    window_set_cancel_button(find_dlg_w, cancel_bt, window_cancel_button_cb);
+
+    /* Hitting return in the find field "clicks" the find button */
+    dlg_set_activate(find_text_box, find_bt);
+
+    /* Show the dialog */
+    gtk_widget_show_all(find_dlg_w);
+    window_present(find_dlg_w);
+}
+
+static void
+follow_find_button_cb(GtkWidget * w, gpointer data)
+{
+    gboolean           found;
+    const gchar                *find_string;
+    follow_info_t      *follow_info = data;
+    GtkTextBuffer      *buffer;
+    GtkTextIter                iter, match_start, match_end;
+    GtkTextMark                *last_pos_mark;
+    GtkWidget          *find_string_w;
+
+    /* Get the text the user typed into the find field */
+    find_string_w = (GtkWidget *)OBJECT_GET_DATA(w, "find_string");
+    find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
+
+    /* Get the buffer associated with the follow stream */
+    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+    gtk_text_buffer_get_start_iter(buffer, &iter);
+
+    /* Look for the search string in the buffer */
+    last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
+    if(last_pos_mark)
+           gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
+
+    found = gtk_text_iter_forward_search(&iter, find_string, 0, &match_start,
+                                        &match_end,
+                                        NULL);
+
+    if(found) {
+           gtk_text_buffer_select_range(buffer, &match_start, &match_end);
+           last_pos_mark = gtk_text_buffer_create_mark (buffer, "last_position",
+                                                        &match_end, FALSE);
+           gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
+    } else {
+           /* We didn't find a match */
+           simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+                         "%sFind text has reached the end of the followed "
+                         "stream%s\n\nThe next search will start from the "
+                         "beginning", simple_dialog_primary_start(),
+                         simple_dialog_primary_end());
+           if(last_pos_mark)
+                   gtk_text_buffer_delete_mark(buffer, last_pos_mark);
+    }
+       
+}
+
+static void
+follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+       follow_info_t   *follow_info = data;
+
+       /* Note that we no longer have a dialog box. */
+       follow_info->find_dlg_w = NULL;
+}
+#endif /* GTK_MAJOR_VERSION >= 2 */
+
 static void
 follow_print_stream(GtkWidget * w _U_, gpointer data)
 {
@@ -932,7 +1104,7 @@ follow_print_stream(GtkWidget * w _U_, gpointer data)
         print_mswin(print_dest);
 
         /* trash temp file */
-        remove(print_dest);
+        eth_remove(print_dest);
     }
 #endif
     return;
@@ -950,7 +1122,7 @@ print_error:
 #ifdef _WIN32
     if (win_printer) {
         /* trash temp file */
-        remove(print_dest);
+        eth_remove(print_dest);
     }
 #endif
 }
@@ -975,6 +1147,7 @@ follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
 #endif
 
 #if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
+    gboolean line_break = FALSE;
     /* While our isprint() hack is in place, we
      * have to use convert some chars to '.' in order
      * to be able to see the data we *should* see
@@ -984,11 +1157,21 @@ follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
 
     for (i = 0; i < nchars; i++) {
         if (buffer[i] == '\n' || buffer[i] == '\r')
+        {
+            line_break = TRUE;
             continue;
+        }
         if (! isprint(buffer[i])) {
             buffer[i] = '.';
         }
     }
+
+    /* XXX - workaround for bug 852
+     * Force a line break so that the text view 
+     * doesn't blow up on excessive long lines.
+     */
+    if (line_break == FALSE)
+        buffer[i] = '\n';
 #endif
 
 #if GTK_MAJOR_VERSION < 2
@@ -1075,7 +1258,7 @@ follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
        return;
     }
 
-    new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
+    new_win = file_selection_new("Wireshark: Save TCP Follow Stream As",
                                  FILE_SELECTION_SAVE);
     follow_info->follow_save_as_w = new_win;
 
@@ -1139,10 +1322,10 @@ follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
     follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
     if (follow_info->show_type == SHOW_RAW) {
         /* Write the data out as raw binary data */
-        fh = fopen(to_name, "wb");
+        fh = eth_fopen(to_name, "wb");
     } else {
         /* Write it out as text */
-        fh = fopen(to_name, "w");
+        fh = eth_fopen(to_name, "w");
     }
     if (fh == NULL) {
         open_failure_alert_box(to_name, errno, TRUE);