From Thomas ERSFELD (GSoC13)
authorAlexis La Goutte <alexis.lagoutte@gmail.com>
Mon, 9 Sep 2013 19:30:30 +0000 (19:30 -0000)
committerAlexis La Goutte <alexis.lagoutte@gmail.com>
Mon, 9 Sep 2013 19:30:30 +0000 (19:30 -0000)
Add TCP/UDP/SSL Follow feature to QtShark

Known issue :
* Duplicate code with GTK (function need follow_info_t struct but in GTK there is some GWidget variable in struct)
* Sometimes TCP Follow fail...

svn path=/trunk/; revision=51883

20 files changed:
ui/CMakeLists.txt
ui/Makefile.common
ui/follow.c [new file with mode: 0644]
ui/follow.h [new file with mode: 0644]
ui/gtk/follow_ssl.c
ui/gtk/follow_stream.h
ui/gtk/follow_tcp.c
ui/gtk/follow_udp.c
ui/qt/CMakeLists.txt
ui/qt/Makefile.common
ui/qt/QtShark.pro
ui/qt/follow_stream_dialog.cpp [new file with mode: 0644]
ui/qt/follow_stream_dialog.h [new file with mode: 0644]
ui/qt/follow_stream_dialog.ui [new file with mode: 0644]
ui/qt/main_window.cpp
ui/qt/main_window.h
ui/qt/main_window.ui
ui/qt/main_window_slots.cpp
ui/qt/packet_list.cpp
ui/qt/packet_list.h

index 81c39dc96689987dd292d1ebd72cb9b112d63088..ff15e2eb99dae2294011f74d1e5e6d6834156b1f 100644 (file)
@@ -28,6 +28,7 @@ set(COMMON_UI_SRC
        export_object_dicom.c
        export_object_http.c
        export_object_smb.c
+       follow.c
        help_url.c
        packet_list_utils.c
        iface_lists.c
index 6246cdceb3b681e06b2c919504110ff22bc57c49..fc4ceb88252dfa178298ba0ca48263219e7134f4 100644 (file)
@@ -49,6 +49,7 @@ WIRESHARK_UI_SRC = \
        export_object_dicom.c   \
        export_object_http.c    \
        export_object_smb.c     \
+       follow.c                \
        iface_lists.c           \
        help_url.c              \
        packet_list_utils.c     \
@@ -70,6 +71,7 @@ noinst_HEADERS = \
        export_object.h         \
        last_open_dir.h         \
        file_dialog.h           \
+       follow.h                \
        help_url.h              \
        packet_list_utils.h     \
        iface_lists.h           \
diff --git a/ui/follow.c b/ui/follow.c
new file mode 100644 (file)
index 0000000..fe08c02
--- /dev/null
@@ -0,0 +1,125 @@
+/* follow.c
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/filesystem.h>
+#include <epan/dfilter/dfilter.h>
+
+#include "ui/follow.h"
+
+#ifdef HAVE_LIBZ
+char *
+sgetline(char *str, int *next)
+{
+    char *end;
+
+    end = strstr(str, "\r\n");
+    if (!end) {
+        *next = (int)strlen(str);
+        return NULL;
+    }
+    *end = '\0';
+    *next = (int)(end-str+2);
+    return str;
+}
+
+gboolean
+parse_http_header(char *data, size_t len, size_t *content_start)
+{
+    char *tmp, *copy, *line;
+    size_t pos = 0;
+    int next_line;
+    gboolean is_gzipped;
+
+    /* XXX handle case where only partial header is passed in here.
+     * we should pass something back to indicate whether header is complete.
+     * (if not, is_gzipped is may still be unknown)
+     */
+
+    /*
+     * In order to parse header, we duplicate data and tokenize lines.
+     * We aren't interested in actual data, so use g_strndup instead of memcpy
+     * to (possibly) copy fewer bytes (e.g., if a nul byte exists in data)
+     * This also ensures that we have a terminated string for futher processing.
+     */
+    tmp = copy = g_strndup(data, len);
+    if (!tmp) {
+        *content_start = 0;
+        return FALSE;
+    }
+
+    /* skip HTTP... line*/
+    /*line = */sgetline(tmp, &next_line);
+
+    tmp += next_line;
+    pos += next_line;
+
+    is_gzipped = FALSE;
+
+    *content_start = -1;
+    while ((line = sgetline(tmp, &next_line))) {
+        char *key, *val, *c;
+
+        tmp += next_line;
+        pos += next_line;
+
+        if (strlen(line) == 0) {
+            /* end of header*/
+            break;
+        }
+
+        c = strchr(line, ':');
+        if (!c) break;
+
+        key = line;
+        *c = '\0';
+        val = c+2;
+
+        if (!strcmp(key, "Content-Encoding") && strstr(val, "gzip")) {
+            is_gzipped = TRUE;
+        }
+    }
+    *content_start = pos;
+    g_free(copy);
+    return is_gzipped;
+}
+#endif
+
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/follow.h b/ui/follow.h
new file mode 100644 (file)
index 0000000..ec026f9
--- /dev/null
@@ -0,0 +1,128 @@
+/* follow.h
+ * Common routines for following data streams (qt/gtk)
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ */
+
+#ifndef __FOLLOW__H__
+#define __FOLLOW__H__
+
+#include "glib.h"
+
+#include <epan/follow.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/charsets.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/ipproto.h>
+#include <epan/charsets.h>
+
+#include "config.h"
+#include "globals.h"
+#include "file.h"
+
+#include "version_info.h"
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#ifdef SSL_PLUGIN
+#include "packet-ssl-utils.h"
+#else
+#include <epan/dissectors/packet-ssl-utils.h>
+#endif
+
+#ifndef QT_CORE_LIB
+#include <gtk/gtk.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct {
+    gboolean   is_from_server;
+    StringInfo data;
+} SslDecryptedRecord;
+
+/* Type of follow we are doing */
+typedef enum {
+    FOLLOW_TCP,
+    FOLLOW_SSL,
+    FOLLOW_UDP
+} follow_type_t;
+
+/* Show Stream */
+typedef enum {
+    FROM_CLIENT,
+    FROM_SERVER,
+    BOTH_HOSTS
+} show_stream_t;
+
+/* Show Type */
+typedef enum {
+    SHOW_ASCII,
+    SHOW_EBCDIC,
+    SHOW_HEXDUMP,
+    SHOW_CARRAY,
+    SHOW_RAW
+} show_type_t;
+
+typedef enum {
+    FRS_OK,
+    FRS_OPEN_ERROR,
+    FRS_READ_ERROR,
+    FRS_PRINT_ERROR
+} frs_return_t;
+
+typedef struct {
+    gboolean is_server;
+    GByteArray *data;
+} follow_record_t;
+
+char *
+sgetline(char *str, int *next);
+
+gboolean
+parse_http_header(char *data, size_t len, size_t *content_start);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
index 42a79a6497ac6a75c68eb8a3e318753bbeefdf5e..8ef803bc7096f4268d8a29baaebcb53fe8018e90 100644 (file)
@@ -57,6 +57,7 @@
 #include "ui/gtk/keys.h"
 #include "ui/gtk/gui_utils.h"
 #include "ui/gtk/font_utils.h"
+#include "ui/follow.h"
 #include "ui/gtk/follow_ssl.h"
 #include "ui/gtk/follow_stream.h"
 
 #include <epan/dissectors/packet-ssl-utils.h>
 #endif
 
-
-typedef struct {
-    gboolean   is_from_server;
-    StringInfo data;
-} SslDecryptedRecord;
-
 static int
 ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ssl)
 {
@@ -130,7 +125,6 @@ ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_
 
     return 0;
 }
-
 /* Follow the SSL stream, if any, to which the last packet that we called
    a dissection routine on belongs (this might be the most recently
    selected packet, or it might be the last packet in the file). */
index c55abf8abc2f927ffbeafe4ea21f62581afda728..e88ba23e1c80628907e84d0ed6cede7509aa264c 100644 (file)
 #define __FOLLOW_STREAM_H__
 
 #include <gtk/gtk.h>
-
-/* Type of follow we are doing */
-typedef enum {
-    FOLLOW_TCP,
-    FOLLOW_SSL,
-    FOLLOW_UDP
-} follow_type_t;
-
-/* Show Stream */
-typedef enum {
-    FROM_CLIENT,
-    FROM_SERVER,
-    BOTH_HOSTS
-} show_stream_t;
-
-/* Show Type */
-typedef enum {
-    SHOW_ASCII,
-    SHOW_EBCDIC,
-    SHOW_HEXDUMP,
-    SHOW_CARRAY,
-    SHOW_RAW
-} show_type_t;
-
-typedef enum {
-    FRS_OK,
-    FRS_OPEN_ERROR,
-    FRS_READ_ERROR,
-    FRS_PRINT_ERROR
-} frs_return_t;
-
-typedef struct {
-    gboolean is_server;
-    GByteArray *data;
-} follow_record_t;
+#include <ui/follow.h>
 
 typedef struct {
     follow_type_t   follow_type;
index c188e0134cb4ce9a6a7ae7aa671c8ae4fcc8c069..8ca8be452a9bbb0e077fd0da5c14616954b0338f 100644 (file)
@@ -316,84 +316,6 @@ follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_)
     data_out_file = NULL;
 }
 
-#ifdef HAVE_LIBZ
-static char *
-sgetline(char *str, int *next)
-{
-  char *end;
-
-  end = strstr(str, "\r\n");
-  if (!end) {
-    *next = (int)strlen(str);
-    return NULL;
-  }
-  *end = '\0';
-  *next = (int)(end-str+2);
-  return str;
-}
-
-static gboolean
-parse_http_header(char *data, size_t len, size_t *content_start)
-{
-  char *tmp, *copy, *line;
-  size_t pos = 0;
-  int next_line;
-  gboolean is_gzipped;
-
-  /* XXX handle case where only partial header is passed in here.
-   * we should pass something back to indicate whether header is complete.
-   * (if not, is_gzipped is may still be unknown)
-   */
-
-  /*
-   * In order to parse header, we duplicate data and tokenize lines.
-   * We aren't interested in actual data, so use g_strndup instead of memcpy
-   * to (possibly) copy fewer bytes (e.g., if a nul byte exists in data)
-   * This also ensures that we have a terminated string for futher processing.
-   */
-  tmp = copy = g_strndup(data, len);
-  if (!tmp) {
-      *content_start = 0;
-      return FALSE;
-  }
-
-  /* skip HTTP... line*/
-  /*line = */sgetline(tmp, &next_line);
-
-  tmp += next_line;
-  pos += next_line;
-
-  is_gzipped = FALSE;
-
-  *content_start = -1;
-  while ((line = sgetline(tmp, &next_line))) {
-    char *key, *val, *c;
-
-    tmp += next_line;
-    pos += next_line;
-
-    if (strlen(line) == 0) {
-      /* end of header*/
-      break;
-    }
-
-    c = strchr(line, ':');
-    if (!c) break;
-
-    key = line;
-    *c = '\0';
-    val = c+2;
-
-    if (!strcmp(key, "Content-Encoding") && strstr(val, "gzip")) {
-      is_gzipped = TRUE;
-    }
-  }
-  *content_start = pos;
-  g_free(copy);
-  return is_gzipped;
-}
-#endif
-
 #define FLT_BUF_SIZE 1024
 
 /*
index 37061de5682703ac84a954dc329d81232bca549e..93a5106b7e04262eb58ae3a8a125c5d1ebc2244e 100644 (file)
@@ -39,6 +39,7 @@
 #include <ui/utf8_entities.h>
 
 #include "gtkglobals.h"
+#include "ui/follow.h"
 #include "ui/gtk/follow_stream.h"
 #include "ui/gtk/keys.h"
 #include "ui/gtk/main.h"
@@ -76,7 +77,6 @@ udp_queue_packet_data(void *tapdata, packet_info *pinfo,
     return 0;
 }
 
-
 /* Follow the UDP stream, if any, to which the last packet that we called
    a dissection routine on belongs (this might be the most recently
    selected packet, or it might be the last packet in the file). */
index 879b71a1f7a3d8dd7993da6ae3968e9978eca8e9..daee6e00383d92e1ecc3403f380ad94f26c5d15f 100644 (file)
@@ -44,6 +44,7 @@ set(QTSHARK_H_SRC
        export_object_dialog.h
        file_set_dialog.h
        filter_expressions_preferences_frame.h
+       follow_stream_dialog.h
        font_color_preferences_frame.h
        import_text_dialog.h
        interface_tree.h
@@ -104,6 +105,7 @@ set(CLEAN_FILES
        export_object_dialog.cpp
        file_set_dialog.cpp
        filter_expressions_preferences_frame.cpp
+       follow_stream_dialog.cpp
        font_color_preferences_frame.cpp
        import_text_dialog.cpp
        interface_tree.cpp
@@ -152,6 +154,7 @@ set(QTSHARK_UI
        export_object_dialog.ui
        file_set_dialog.ui
        filter_expressions_preferences_frame.ui
+       follow_stream_dialog.ui
        font_color_preferences_frame.ui
        import_text_dialog.ui
        layout_preferences_frame.ui
index 0781e8f5261dccfa041862853f309e0a7d64be76..d294285fb1d65dc7e2257bf08e4647af068606ba 100644 (file)
@@ -35,8 +35,9 @@ NODIST_GENERATED_HEADER_FILES = \
        ui_column_preferences_frame.h \
        ui_export_object_dialog.h \
        ui_file_set_dialog.h \
-       ui_font_color_preferences_frame.h \
        ui_filter_expressions_preferences_frame.h \
+       ui_follow_stream_dialog.h \
+       ui_font_color_preferences_frame.h \
        ui_import_text_dialog.h \
        ui_layout_preferences_frame.h \
        ui_main_welcome.h \
@@ -50,8 +51,8 @@ NODIST_GENERATED_HEADER_FILES = \
        ui_print_dialog.h \
        ui_profile_dialog.h \
        ui_search_frame.h \
-       ui_summary_dialog.h \
        ui_splash_overlay.h \
+       ui_summary_dialog.h \
        ui_tcp_stream_dialog.h \
        ui_time_shift_dialog.h \
        ui_uat_dialog.h
@@ -109,6 +110,7 @@ MOC_HDRS = \
        export_object_dialog.h \
        file_set_dialog.h \
        filter_expressions_preferences_frame.h \
+       follow_stream_dialog.h \
        font_color_preferences_frame.h \
        import_text_dialog.h \
        interface_tree.h \
@@ -134,9 +136,9 @@ MOC_HDRS = \
        related_packet_delegate.h \
        search_frame.h \
        simple_dialog_qt.h \
-       summary_dialog.h \
        sparkline_delegate.h \
        splash_overlay.h \
+       summary_dialog.h \
        syntax_line_edit.h \
        tcp_stream_dialog.h \
        time_shift_dialog.h \
@@ -153,6 +155,7 @@ UI_FILES = \
        export_object_dialog.ui         \
        file_set_dialog.ui              \
        filter_expressions_preferences_frame.ui \
+       follow_stream_dialog.ui \
        font_color_preferences_frame.ui \
        import_text_dialog.ui           \
        layout_preferences_frame.ui     \
@@ -167,8 +170,8 @@ UI_FILES = \
        print_dialog.ui                 \
        profile_dialog.ui               \
        search_frame.ui                 \
-       summary_dialog.ui               \
        splash_overlay.ui               \
+       summary_dialog.ui               \
        tcp_stream_dialog.ui            \
        time_shift_dialog.ui            \
        uat_dialog.ui
@@ -239,6 +242,7 @@ WIRESHARK_QT_SRC = \
        export_object_dialog.cpp \
        file_set_dialog.cpp \
        filter_expressions_preferences_frame.cpp \
+       follow_stream_dialog.cpp \
        font_color_preferences_frame.cpp \
        import_text_dialog.cpp \
        interface_tree.cpp \
index 40cca770162a552b6534ba5ed21fc7193ea42f4a..9f1fb142eec41cdc55e30aa103f8a06d65415bab 100644 (file)
@@ -210,6 +210,7 @@ FORMS += \
     export_object_dialog.ui \
     file_set_dialog.ui \
     filter_expressions_preferences_frame.ui \
+    follow_stream_dialog.ui \
     font_color_preferences_frame.ui \
     import_text_dialog.ui \
     layout_preferences_frame.ui \
@@ -230,7 +231,6 @@ FORMS += \
     uat_dialog.ui \
     tcp_stream_dialog.ui
 
-
 win32 { ## These should be in config.pri ??
     !isEmpty(PORTAUDIO_DIR) {
         PA_OBJECTS = \
@@ -260,6 +260,7 @@ HEADERS += $$HEADERS_WS_C \
     export_dissection_dialog.h \
     export_object_dialog.h \
     filter_expressions_preferences_frame.h \
+    follow_stream_dialog.h \
     font_color_preferences_frame.h \
     layout_preferences_frame.h \
     main_window_preferences_frame.h \
@@ -551,6 +552,7 @@ SOURCES += \
     export_object_dialog.cpp \
     file_set_dialog.cpp \
     filter_expressions_preferences_frame.cpp \
+    follow_stream_dialog.cpp \
     font_color_preferences_frame.cpp \
     import_text_dialog.cpp \
     interface_tree.cpp \
diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp
new file mode 100644 (file)
index 0000000..516dd3e
--- /dev/null
@@ -0,0 +1,1192 @@
+/* follow_stream_dialog.cpp
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "follow_stream_dialog.h"
+#include "ui_follow_stream_dialog.h"
+
+#include "main_window.h"
+#include "wireshark_application.h"
+
+#include "ui/follow.h"
+
+FollowStreamDialog::FollowStreamDialog(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::FollowStreamDialog)
+{
+    follow_info = NULL;
+    ui->setupUi(this);
+
+    this->setFixedSize(this->size());
+
+    connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(HelpButton()));
+
+    bFilterOut = ui->buttonBox->addButton(tr("Filter out this stream"), QDialogButtonBox::ActionRole);
+    connect(bFilterOut, SIGNAL(clicked()), this, SLOT(FilterOut()));
+
+
+    bFind = ui->buttonBox->addButton(tr("Find"), QDialogButtonBox::ActionRole);
+    connect(bFind, SIGNAL(clicked()), this, SLOT(FindText()));
+
+    bPrint = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole);
+    connect(bPrint, SIGNAL(clicked()), this, SLOT(Print()));
+
+    bSave = ui->buttonBox->addButton(tr("Save as..."), QDialogButtonBox::ActionRole);
+    connect(bSave, SIGNAL(clicked()), this, SLOT(SaveAs()));
+
+    save_as = false;
+}
+
+void FollowStreamDialog::Print()
+{
+#ifndef QT_NO_PRINTER
+    QPrinter printer(QPrinter::HighResolution);
+    QPrintDialog dialog(&printer, this);
+    if ( dialog.exec() == QDialog::Accepted)
+        ui->teStreamContent->print(&printer);
+#endif
+}
+
+void FollowStreamDialog::FindText()
+{
+    bool ok;
+    QString text = QInputDialog::getText(this, tr("Wireshark: Find text"),
+                                         tr("Find text:"), QLineEdit::Normal,
+                                         " ", &ok);
+    if (ok && !text.isEmpty())
+    {
+        ui->teStreamContent->moveCursor(QTextCursor::Start);
+        ui->teStreamContent->find(text);
+    }
+}
+
+void FollowStreamDialog::SaveAs()
+{
+    QString file_name = QFileDialog::getSaveFileName(this, "Wireshark: Save stream content as");
+    file.setFileName(file_name);
+    file.open( QIODevice::WriteOnly );
+    QTextStream out(&file);
+
+    save_as = true;
+
+    follow_read_stream();
+
+    if (follow_info->show_type != SHOW_RAW)
+    {
+        out << ui->teStreamContent->toPlainText();
+    }
+
+    save_as = false;
+
+    file.close();
+}
+
+void FollowStreamDialog::HelpButton()
+{
+    wsApp->helpTopicAction(HELP_FOLLOW_STREAM_DIALOG);
+}
+
+void FollowStreamDialog::FilterOut()
+{
+
+    QString filter = QString(follow_info->filter_out_filter);
+    emit updateFilter(filter, TRUE);
+
+    this->close();
+}
+
+void FollowStreamDialog::on_cbDirections_currentIndexChanged(int index)
+{
+    if (!follow_info)
+        return;
+
+    switch(index)
+    {
+    case 0 :
+        follow_info->show_stream = BOTH_HOSTS;
+        break;
+    case 1 :
+        follow_info->show_stream = FROM_SERVER;
+        break;
+    case 2 :
+        follow_info->show_stream = FROM_CLIENT;
+        break;
+    default:
+        return;
+    }
+
+    follow_read_stream();
+}
+
+void FollowStreamDialog::on_cbCharset_currentIndexChanged(int index)
+{
+    switch (index)
+    {
+    case 0:
+        follow_info->show_type = SHOW_ASCII;
+        break;
+
+    case 1:
+        follow_info->show_type = SHOW_EBCDIC;
+        break;
+
+    case 2:
+        follow_info->show_type = SHOW_CARRAY;
+        break;
+
+    case 3:
+        follow_info->show_type = SHOW_HEXDUMP;
+        break;
+
+    case 4:
+        follow_info->show_type = SHOW_RAW;
+        break;
+
+    default:
+        return;
+    }
+
+    follow_read_stream();
+}
+
+frs_return_t
+FollowStreamDialog::follow_read_stream()
+{
+    ui->teStreamContent->clear();
+
+    switch(follow_info->follow_type) {
+
+    case FOLLOW_TCP :
+        return follow_read_tcp_stream();
+
+    case FOLLOW_UDP :
+        return follow_read_udp_stream();
+
+    case FOLLOW_SSL :
+        return follow_read_ssl_stream();
+
+    default :
+        g_assert_not_reached();
+        return (frs_return_t)0;
+    }
+}
+
+//Copy from ui/gtk/follow_udp.c
+static int
+udp_queue_packet_data(void *tapdata, packet_info *pinfo,
+                      epan_dissect_t *edt _U_, const void *data)
+{
+
+    follow_record_t *follow_record;
+    follow_info_t *follow_info = (follow_info_t *)tapdata;
+    tvbuff_t *next_tvb = (tvbuff_t *)data;
+
+    follow_record = g_new(follow_record_t,1);
+
+    follow_record->data = g_byte_array_sized_new(tvb_length(next_tvb));
+    follow_record->data = g_byte_array_append(follow_record->data,
+                                              tvb_get_ptr(next_tvb, 0, -1),
+                                              tvb_length(next_tvb));
+
+    if (follow_info->client_port == 0) {
+        follow_info->client_port = pinfo->srcport;
+        COPY_ADDRESS(&follow_info->client_ip, &pinfo->src);
+    }
+
+    if (ADDRESSES_EQUAL(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport)
+        follow_record->is_server = FALSE;
+    else
+        follow_record->is_server = TRUE;
+
+    /* update stream counter */
+    follow_info->bytes_written[follow_record->is_server] += follow_record->data->len;
+
+    follow_info->payload = g_list_append(follow_info->payload, follow_record);
+    return 0;
+}
+
+//Copy from ui/gtk/follow_ssl.c
+static int
+ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ssl)
+{
+    follow_info_t *      follow_info = (follow_info_t*) tapdata;
+    SslDecryptedRecord * rec = NULL;
+    SslDataInfo *        appl_data = NULL;
+    int                  proto_ssl = GPOINTER_TO_INT(ssl);
+    SslPacketInfo *      pi = NULL;
+    show_stream_t        from = FROM_CLIENT;
+
+    /* Skip packets without decrypted payload data. */
+    pi = (SslPacketInfo*) p_get_proto_data(pinfo->fd, proto_ssl, 0);
+    if (!pi || !pi->appl_data) return 0;
+
+    /* Compute the packet's sender. */
+    if (follow_info->client_port == 0) {
+        follow_info->client_port = pinfo->srcport;
+        COPY_ADDRESS(&follow_info->client_ip, &pinfo->src);
+    }
+    if (ADDRESSES_EQUAL(&follow_info->client_ip, &pinfo->src) &&
+            follow_info->client_port == pinfo->srcport) {
+        from = FROM_CLIENT;
+    } else {
+        from = FROM_SERVER;
+    }
+
+    for (appl_data = pi->appl_data; appl_data != NULL; appl_data = appl_data->next) {
+
+        /* TCP segments that contain the end of two or more SSL PDUs will be
+           queued to SSL taps for each of those PDUs. Therefore a single
+           packet could be processed by this SSL tap listener multiple times.
+           The following test handles that scenario by treating the
+           follow_info->bytes_written[] values as the next expected
+           appl_data->seq. Any appl_data instances that fall below that have
+           already been processed and must be skipped. */
+        if (appl_data->seq < follow_info->bytes_written[from]) continue;
+
+        /* Allocate a SslDecryptedRecord to hold the current appl_data
+           instance's decrypted data. Even though it would be possible to
+           consolidate multiple appl_data instances into a single rec, it is
+           beneficial to use a one-to-one mapping. This affords the Follow
+           Stream dialog view modes (ASCII, EBCDIC, Hex Dump, C Arrays, Raw)
+           the opportunity to accurately reflect SSL PDU boundaries. Currently
+           the Hex Dump view does by starting a new line, and the C Arrays
+           view does by starting a new array declaration. */
+        rec = (SslDecryptedRecord*) g_malloc(sizeof(SslDecryptedRecord) + appl_data->plain_data.data_len);
+        rec->is_from_server = from == FROM_SERVER;
+        rec->data.data = (guchar*) (rec + 1);
+        rec->data.data_len = appl_data->plain_data.data_len;
+        memcpy(rec->data.data, appl_data->plain_data.data, appl_data->plain_data.data_len);
+
+        /* Append the record to the follow_info structure. */
+        follow_info->payload = g_list_append(follow_info->payload, rec);
+        follow_info->bytes_written[from] += rec->data.data_len;
+    }
+
+    return 0;
+}
+
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()".  (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+FollowStreamDialog::follow_read_ssl_stream()
+{
+    guint32      global_client_pos = 0, global_server_pos = 0;
+    guint32      server_packet_count = 0;
+    guint32      client_packet_count = 0;
+    guint32 *    global_pos;
+    GList *      cur;
+    frs_return_t frs_return;
+
+    for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
+        SslDecryptedRecord * rec = (SslDecryptedRecord*) cur->data;
+        gboolean             include_rec = FALSE;
+
+        if (rec->is_from_server) {
+            global_pos = &global_server_pos;
+            include_rec = (follow_info->show_stream == BOTH_HOSTS) ||
+                    (follow_info->show_stream == FROM_SERVER);
+        } else {
+            global_pos = &global_client_pos;
+            include_rec = (follow_info->show_stream == BOTH_HOSTS) ||
+                    (follow_info->show_stream == FROM_CLIENT);
+        }
+
+        if (include_rec) {
+            size_t nchars = rec->data.data_len;
+            gchar *buffer = (gchar *)g_memdup(rec->data.data, (guint) nchars);
+
+            frs_return = follow_show(buffer, nchars,
+                                     rec->is_from_server, global_pos,
+                                     &server_packet_count, &client_packet_count);
+            g_free(buffer);
+            if (frs_return == FRS_PRINT_ERROR)
+                return frs_return;
+        }
+    }
+
+    return FRS_OK;
+}
+
+void
+FollowStreamDialog::follow_stream()
+{
+    follow_stats_t stats;
+
+    follow_info->show_type = SHOW_RAW;
+    follow_info->show_stream = BOTH_HOSTS;
+
+    /* Stream to show */
+    follow_stats(&stats);
+
+    follow_info->is_ipv6 = stats.is_ipv6;
+
+    follow_read_stream();
+}
+
+
+
+
+void FollowStreamDialog::add_text(char *buffer, size_t nchars, gboolean is_from_server)
+{
+    size_t i;
+    QString buf;
+    gchar *str;
+
+    if (save_as == true)
+    {
+        //FILE *fh = (FILE *)arg;
+        size_t nwritten;
+        int FileDescriptor = file.handle();
+        FILE* fh = fdopen(dup(FileDescriptor), "wb");
+        nwritten = fwrite(buffer, 1, nchars, fh);
+        fclose(fh);
+        return;
+        if (nwritten != nchars)
+            return;
+    }
+
+    QColor tagserver_fg = QColor(prefs.st_server_fg.red>>8,
+                                 prefs.st_server_fg.green>>8,
+                                 prefs.st_server_fg.blue>>8);
+    QColor tagserver_bg = QColor(prefs.st_server_bg.red>>8,
+                                 prefs.st_server_bg.green>>8,
+                                 prefs.st_server_bg.blue>>8);
+
+    QColor tagclient_fg = QColor(prefs.st_client_fg.red>>8,
+                                 prefs.st_client_fg.green>>8,
+                                 prefs.st_client_fg.blue>>8);
+    QColor tagclient_bg = QColor(prefs.st_client_bg.red>>8,
+                                 prefs.st_client_bg.green>>8,
+                                 prefs.st_client_bg.blue>>8);
+
+    for (i = 0; i < nchars; i++) {
+        if (buffer[i] == '\n' || buffer[i] == '\r')
+            continue;
+        if (! isprint((guchar)buffer[i])) {
+            buffer[i] = '.';
+        }
+    }
+
+
+
+    /* convert unterminated char array to a zero terminated string */
+    str = (char *)g_malloc(nchars + 1);
+    memcpy(str, buffer, nchars);
+    str[nchars] = 0;
+    buf = QString(str);
+    g_free(str);
+
+
+
+    if (is_from_server)
+    {
+        ui->teStreamContent->setTextColor(tagserver_fg);
+        ui->teStreamContent->setTextBackgroundColor(tagserver_bg);
+        ui->teStreamContent->insertPlainText(buf);
+    }
+    else
+    {
+        ui->teStreamContent->setTextColor(tagclient_fg);
+        ui->teStreamContent->setTextBackgroundColor(tagclient_bg);
+        ui->teStreamContent->insertPlainText(buf);
+    }
+}
+
+
+frs_return_t
+FollowStreamDialog::follow_show(char *buffer, size_t nchars, gboolean is_from_server,
+                                guint32 *global_pos, guint32 *server_packet_count,
+                                guint32 *client_packet_count)
+{
+    gchar initbuf[256];
+    guint32 current_pos;
+    static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+    switch (follow_info->show_type) {
+
+    case SHOW_EBCDIC:
+        /* If our native arch is ASCII, call: */
+        EBCDIC_to_ASCII((guint8*)buffer, (guint) nchars);
+        add_text(buffer, nchars, is_from_server);
+        break;
+
+    case SHOW_ASCII:
+        /* If our native arch is EBCDIC, call:
+         * ASCII_TO_EBCDIC(buffer, nchars);
+         */
+        add_text(buffer, nchars, is_from_server);
+        break;
+
+    case SHOW_RAW:
+        /* Don't translate, no matter what the native arch
+         * is.
+         */
+        add_text(buffer, nchars, is_from_server);
+        break;
+
+    case SHOW_HEXDUMP:
+        current_pos = 0;
+        while (current_pos < nchars) {
+            gchar hexbuf[256];
+            int i;
+            gchar *cur = hexbuf, *ascii_start;
+
+            /* is_from_server indentation : put 4 spaces at the
+             * beginning of the string */
+            /* XXX - We might want to prepend each line with "C" or "S" instead. */
+            if (is_from_server && follow_info->show_stream == BOTH_HOSTS) {
+                memset(cur, ' ', 4);
+                cur += 4;
+            }
+            cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
+            /* 49 is space consumed by hex chars */
+            ascii_start = cur + 49;
+            for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+                *cur++ =
+                        hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+                *cur++ =
+                        hexchars[buffer[current_pos + i] & 0x0f];
+                *cur++ = ' ';
+                if (i == 7)
+                    *cur++ = ' ';
+            }
+            /* Fill it up if column isn't complete */
+            while (cur < ascii_start)
+                *cur++ = ' ';
+
+            /* Now dump bytes as text */
+            for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+                *cur++ =
+                        (isprint((guchar)buffer[current_pos + i]) ?
+                            buffer[current_pos + i] : '.' );
+                if (i == 7) {
+                    *cur++ = ' ';
+                }
+            }
+            current_pos += i;
+            (*global_pos) += i;
+            *cur++ = '\n';
+            *cur = 0;
+
+            add_text(hexbuf, strlen(hexbuf), is_from_server);
+        }
+        break;
+
+    case SHOW_CARRAY:
+        current_pos = 0;
+        g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
+                   is_from_server ? 1 : 0,
+                   is_from_server ? (*server_packet_count)++ : (*client_packet_count)++);
+        add_text(initbuf, strlen(initbuf), is_from_server);
+
+        while (current_pos < nchars) {
+            gchar hexbuf[256];
+            int i, cur;
+
+            cur = 0;
+            for (i = 0; i < 8 && current_pos + i < nchars; i++) {
+                /* Prepend entries with "0x" */
+                hexbuf[cur++] = '0';
+                hexbuf[cur++] = 'x';
+                hexbuf[cur++] =
+                        hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+                hexbuf[cur++] =
+                        hexchars[buffer[current_pos + i] & 0x0f];
+
+                /* Delimit array entries with a comma */
+                if (current_pos + i + 1 < nchars)
+                    hexbuf[cur++] = ',';
+
+                hexbuf[cur++] = ' ';
+            }
+
+            /* Terminate the array if we are at the end */
+            if (current_pos + i == nchars) {
+                hexbuf[cur++] = '}';
+                hexbuf[cur++] = ';';
+            }
+
+            current_pos += i;
+            (*global_pos) += i;
+            hexbuf[cur++] = '\n';
+            hexbuf[cur] = 0;
+            add_text(hexbuf, strlen(hexbuf), is_from_server);
+        }
+        break;
+    }
+
+    return FRS_OK;
+}
+
+
+
+bool FollowStreamDialog::Follow(QString previous_filter_, follow_type_t type)
+{
+    int                 tmp_fd;
+    gchar               *follow_filter;
+    const gchar         *previous_filter = previous_filter_.toStdString().c_str();
+    int                 filter_out_filter_len, previous_filter_len;
+    const char          *hostname0 = NULL, *hostname1 = NULL;
+    char                *port0 = NULL, *port1 = NULL;
+    gchar               *server_to_client_string = NULL;
+    gchar               *client_to_server_string = NULL;
+    gchar               *both_directions_string = NULL;
+    follow_stats_t      stats;
+    tcp_stream_chunk    sc;
+    size_t              nchars;
+    gchar               *data_out_filename;
+    GString *           msg;
+
+    if (cfile.edt == NULL)
+    {
+        QMessageBox::warning(this, tr("Error following stream."), tr("Capture file invalid."));
+        return false;
+    }
+
+    switch (type)
+    {
+    case FOLLOW_TCP:
+        if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
+            QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a TCP packet selected."));
+            return false;
+        }
+        break;
+    case FOLLOW_UDP:
+        if (cfile.edt->pi.ipproto != IP_PROTO_UDP) {
+            QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a UDP packet selected."));
+            return false;
+        }
+        break;
+    case FOLLOW_SSL:
+        /* we got ssl so we can follow */
+        if (!epan_dissect_packet_contains_field(cfile.edt, "ssl")) {
+            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                          "Error following stream.  Please make\n"
+                          "sure you have an SSL packet selected.");
+            return false;
+        }
+        break;
+    }
+
+    follow_info = g_new0(follow_info_t, 1);
+    follow_info->follow_type = type;
+
+    if (type == FOLLOW_TCP || type == FOLLOW_SSL)
+    {
+        /* Create a new filter that matches all packets in the TCP stream,
+           and set the display filter entry accordingly */
+        reset_tcp_reassembly();
+    }
+
+    follow_filter = build_follow_filter(&cfile.edt->pi);
+    if (!follow_filter) {
+        QMessageBox::warning(this,
+                             tr("Error creating filter for this stream."),
+                             tr("A transport or network layer header is needed."));
+        g_free(follow_info);
+        return false;
+    }
+
+    if (type == FOLLOW_TCP || type == FOLLOW_SSL)
+    {
+        /* Create a temporary file into which to dump the reassembled data
+           from the TCP stream, and set "data_out_file" to refer to it, so
+           that the TCP code will write to it.
+
+           XXX - it might be nicer to just have the TCP code directly
+           append stuff to the text widget for the TCP stream window,
+           if we can arrange that said window not pop up until we're
+           done. */
+        tmp_fd = create_tempfile(&data_out_filename, "follow");
+        follow_info->data_out_filename = g_strdup(data_out_filename);
+
+        if (tmp_fd == -1) {
+            QMessageBox::warning(this, "Error",
+                                 "Could not create temporary file %1: %2",
+                                 follow_info->data_out_filename, g_strerror(errno));
+            g_free(follow_info->data_out_filename);
+            g_free(follow_info);
+            g_free(follow_filter);
+            return false;
+        }
+
+        data_out_file = fdopen(tmp_fd, "w+b");
+        if (data_out_file == NULL) {
+            QMessageBox::warning(this, "Error",
+                                 "Could not create temporary file %1: %2",
+                                 follow_info->data_out_filename, g_strerror(errno));
+            //ws_close(tmp_fd);
+            ws_unlink(follow_info->data_out_filename);
+            g_free(follow_info->data_out_filename);
+            g_free(follow_info);
+            g_free(follow_filter);
+            return false;
+        }
+    }
+
+
+    /* allocate our new filter. API claims g_malloc terminates program on failure */
+    /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+    previous_filter_len = previous_filter?(int)strlen(previous_filter):0;
+    filter_out_filter_len = (int)(strlen(follow_filter) + strlen(previous_filter) + 16);
+    follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+    /* append the negation */
+    if(previous_filter_len) {
+        g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+                   "%s and !(%s)", previous_filter, follow_filter);
+    }
+    else
+    {
+        g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+                   "!(%s)", follow_filter);
+    }
+
+    switch (type)
+    {
+    case FOLLOW_TCP:
+
+        break;
+    case FOLLOW_UDP:
+        /* data will be passed via tap callback*/
+        msg = register_tap_listener("udp_follow", follow_info, follow_filter,
+                                    0, NULL, udp_queue_packet_data, NULL);
+        if (msg) {
+            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                          "Can't register udp_follow tap: %s\n",
+                          msg->str);
+            g_free(follow_info->filter_out_filter);
+            g_free(follow_info);
+            g_free(follow_filter);
+            return false;
+        }
+        break;
+    case FOLLOW_SSL:
+        /* we got ssl so we can follow */
+        msg = register_tap_listener("ssl", follow_info, follow_filter, 0,
+                                    NULL, ssl_queue_packet_data, NULL);
+        if (msg)
+        {
+            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                          "Can't register ssl tap: %s\n",msg->str);
+            g_free(follow_info->filter_out_filter);
+            g_free(follow_info);
+            g_free(follow_filter);
+            return false;
+        }
+        break;
+    }
+
+
+
+    /* Run the display filter so it goes in effect - even if it's the
+       same as the previous display filter. */
+    QString filter = QString(follow_filter);
+    emit updateFilter(filter, TRUE);
+
+    switch (type)
+    {
+    case FOLLOW_TCP:
+
+        break;
+    case FOLLOW_UDP:
+        remove_tap_listener(follow_info);
+        break;
+    case FOLLOW_SSL:
+        remove_tap_listener(follow_info);
+        break;
+    }
+
+
+    if (type == FOLLOW_TCP)
+    {
+        /* Check whether we got any data written to the file. */
+        if (empty_tcp_stream) {
+            QMessageBox::warning(this, "Error",
+                                 "The packets in the capture file for that stream have no data.");
+            //ws_close(tmp_fd);
+            ws_unlink(follow_info->data_out_filename);
+            g_free(follow_info->data_out_filename);
+            g_free(follow_info->filter_out_filter);
+            g_free(follow_info);
+            return false;
+        }
+
+        rewind(data_out_file);
+        nchars=fread(&sc, 1, sizeof(sc), data_out_file);
+        if (nchars != sizeof(sc)) {
+            if (ferror(data_out_file)) {
+                QMessageBox::warning(this, "Error",
+                                     QString(tr("Could not read from temporary file %1: %2"))
+                                     .arg(follow_info->data_out_filename)
+                                     .arg(g_strerror(errno)));
+            } else {
+                QMessageBox::warning(this, "Error",
+                                     QString(tr("Short read from temporary file %1: expected %2, got %3"))
+                                     .arg(follow_info->data_out_filename)
+                                     .arg((unsigned long)sizeof(sc))
+                                     .arg((unsigned long)nchars));
+
+            }
+            //ws_close(tmp_fd);
+            ws_unlink(follow_info->data_out_filename);
+            g_free(follow_info->data_out_filename);
+            g_free(follow_info->filter_out_filter);
+            g_free(follow_info);
+            return false;
+        }
+        fclose(data_out_file);
+    }
+
+    /* Stream to show */
+    follow_stats(&stats);
+
+    if (stats.is_ipv6) {
+        struct e_in6_addr ipaddr;
+        memcpy(&ipaddr, stats.ip_address[0], 16);
+        hostname0 = get_hostname6(&ipaddr);
+        memcpy(&ipaddr, (type == FOLLOW_TCP) ? stats.ip_address[1] : stats.ip_address[0], 16);
+        hostname1 = get_hostname6(&ipaddr);
+    } else {
+        guint32 ipaddr;
+        memcpy(&ipaddr, stats.ip_address[0], 4);
+        hostname0 = get_hostname(ipaddr);
+        memcpy(&ipaddr, stats.ip_address[1], 4);
+        hostname1 = get_hostname(ipaddr);
+    }
+
+    switch (type)
+    {
+    case FOLLOW_TCP:
+        port0 = get_tcp_port(stats.port[0]);
+        port1 = get_tcp_port(stats.port[1]);
+        break;
+    case FOLLOW_UDP:
+        port0 = get_udp_port(stats.port[0]);
+        port1 = get_udp_port(stats.port[1]);
+        break;
+    case FOLLOW_SSL:
+        port0 = get_tcp_port(stats.port[0]);
+        port1 = get_tcp_port(stats.port[1]);
+        break;
+    }
+
+    follow_info->is_ipv6 = stats.is_ipv6;
+
+    if (type == FOLLOW_TCP)
+    {
+        /* Host 0 --> Host 1 */
+        if(sc.src_port == stats.port[0]) {
+            server_to_client_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname0, port0,
+                                    hostname1, port1,
+                                    stats.bytes_written[0]);
+        } else {
+            server_to_client_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname1, port1,
+                                    hostname0,port0,
+                                    stats.bytes_written[0]);
+        }
+
+        /* Host 1 --> Host 0 */
+        if(sc.src_port == stats.port[1]) {
+            client_to_server_string =
+                g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                hostname0, port0,
+                                hostname1, port1,
+                                stats.bytes_written[1]);
+        } else {
+            client_to_server_string =
+                g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                hostname1, port1,
+                                hostname0, port0,
+                                stats.bytes_written[1]);
+        }
+
+    }
+    else
+    {
+        if(follow_info->client_port == stats.port[0]) {
+            server_to_client_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname0, port0,
+                                    hostname1, port1,
+                                    follow_info->bytes_written[0]);
+
+            client_to_server_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname1, port1,
+                                    hostname0, port0,
+                                    follow_info->bytes_written[1]);
+        } else {
+            server_to_client_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname1, port1,
+                                    hostname0, port0,
+                                    follow_info->bytes_written[0]);
+
+            client_to_server_string =
+                    g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+                                    hostname0, port0,
+                                    hostname1, port1,
+                                    follow_info->bytes_written[1]);
+        }
+    }
+
+    /* Both Stream Directions */
+    switch (type)
+    {
+    case FOLLOW_TCP:
+        both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]);
+        this->setWindowTitle(QString("Follow TCP Stream (%1)").arg(follow_filter));
+        break;
+    case FOLLOW_UDP:
+        both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+        this->setWindowTitle(QString("Follow UDP Stream (%1)").arg(follow_filter));
+        break;
+    case FOLLOW_SSL:
+        both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+        this->setWindowTitle(QString("Follow SSL Stream (%1)").arg(follow_filter));
+        break;
+    }
+
+
+    ui->cbDirections->clear();
+    this->ui->cbDirections->addItem(QString(both_directions_string));
+    this->ui->cbDirections->addItem(QString(client_to_server_string));
+    this->ui->cbDirections->addItem(QString(server_to_client_string));
+
+
+    follow_stream();
+
+    /* Free the filter string, as we're done with it. */
+    g_free(follow_filter);
+
+    data_out_file = NULL;
+
+    return true;
+}
+
+
+#define FLT_BUF_SIZE 1024
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()".  (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+FollowStreamDialog::follow_read_tcp_stream()
+{
+    FILE *data_out_file;
+    tcp_stream_chunk    sc;
+    size_t              bcount;
+    size_t              bytes_read;
+    int                 iplen;
+    guint8              client_addr[MAX_IPADDR_LEN];
+    guint16             client_port = 0;
+    gboolean            is_server;
+    guint32             global_client_pos = 0, global_server_pos = 0;
+    guint32             server_packet_count = 0;
+    guint32             client_packet_count = 0;
+    guint32             *global_pos;
+    gboolean            skip;
+    char                buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
+    size_t              nchars;
+    frs_return_t        frs_return;
+#ifdef HAVE_LIBZ
+    char                outbuffer[FLT_BUF_SIZE+1];
+    z_stream            strm;
+    gboolean            gunzip = FALSE;
+    int                 ret;
+#endif
+
+
+    iplen = (follow_info->is_ipv6) ? 16 : 4;
+
+    data_out_file = ws_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,
+                      g_strerror(errno));
+        return FRS_OPEN_ERROR;
+    }
+
+    while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) {
+        if (nchars != sizeof(sc)) {
+            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                          "Short read from temporary file %s: expected %lu, got %lu",
+                          follow_info->data_out_filename,
+                          (unsigned long)sizeof(sc),
+                          (unsigned long)nchars);
+            fclose(data_out_file);
+            data_out_file = NULL;
+            return FRS_READ_ERROR;
+        }
+        if (client_port == 0) {
+            memcpy(client_addr, sc.src_addr, iplen);
+            client_port = sc.src_port;
+        }
+        skip = FALSE;
+        if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
+                client_port == sc.src_port) {
+            is_server = FALSE;
+            global_pos = &global_client_pos;
+            if (follow_info->show_stream == FROM_SERVER) {
+                skip = TRUE;
+            }
+        }
+        else {
+            is_server = TRUE;
+            global_pos = &global_server_pos;
+            if (follow_info->show_stream == FROM_CLIENT) {
+                skip = TRUE;
+            }
+        }
+
+        bytes_read = 0;
+        while (bytes_read < sc.dlen) {
+            bcount = ((sc.dlen-bytes_read) < FLT_BUF_SIZE) ? (sc.dlen-bytes_read) : FLT_BUF_SIZE;
+            nchars = fread(buffer, 1, bcount, data_out_file);
+            if (nchars == 0)
+                break;
+            /* XXX - if we don't get "bcount" bytes, is that an error? */
+            bytes_read += nchars;
+
+#ifdef HAVE_LIBZ
+            /* If we are on the first packet of an HTTP response, check if data is gzip
+            * compressed.
+            */
+            if (is_server && bytes_read == nchars && !memcmp(buffer, "HTTP", 4)) {
+                size_t header_len;
+                gunzip = parse_http_header(buffer, nchars, &header_len);
+                if (gunzip) {
+                    /* show header (which is not gzipped)*/
+                    frs_return = follow_show(buffer,
+                                             header_len, is_server, global_pos,
+                                             &server_packet_count, &client_packet_count);
+                    if (frs_return == FRS_PRINT_ERROR) {
+                        fclose(data_out_file);
+                        data_out_file = NULL;
+                        return frs_return;
+                    }
+
+                    /* init gz_stream*/
+                    strm.next_in = Z_NULL;
+                    strm.avail_in = 0;
+                    strm.next_out = Z_NULL;
+                    strm.avail_out = 0;
+                    strm.zalloc = Z_NULL;
+                    strm.zfree = Z_NULL;
+                    strm.opaque = Z_NULL;
+                    ret = inflateInit2(&strm, MAX_WBITS+16);
+                    if (ret != Z_OK) {
+                        fclose(data_out_file);
+                        data_out_file = NULL;
+                        return FRS_READ_ERROR;
+                    }
+
+                    /* prepare remainder of buffer to be inflated below */
+                    memmove(buffer, buffer+header_len, nchars-header_len);
+                    nchars -= header_len;
+                }
+            }
+
+            if (gunzip) {
+                strm.next_in = (Bytef*)buffer;
+                strm.avail_in = (int)nchars;
+                do {
+                    strm.next_out = (Bytef*)outbuffer;
+                    strm.avail_out = FLT_BUF_SIZE;
+
+                    ret = inflate(&strm, Z_NO_FLUSH);
+                    if (ret < 0 || ret == Z_NEED_DICT) {
+                        inflateEnd(&strm);
+                        fclose(data_out_file);
+                        data_out_file = NULL;
+                        return FRS_READ_ERROR;
+                    } else if (ret == Z_STREAM_END) {
+                        inflateEnd(&strm);
+                    }
+
+                    frs_return = follow_show(outbuffer,
+                                             FLT_BUF_SIZE-strm.avail_out, is_server,
+                                             global_pos,
+                                             &server_packet_count,
+                                             &client_packet_count);
+                    if(frs_return == FRS_PRINT_ERROR) {
+                        inflateEnd(&strm);
+                        fclose(data_out_file);
+                        data_out_file = NULL;
+                        return frs_return;
+                    }
+                } while (strm.avail_out == 0);
+                skip = TRUE;
+            }
+#endif
+            if (!skip)
+            {
+                frs_return = follow_show(buffer,
+                                         nchars, is_server, global_pos,
+                                         &server_packet_count,
+                                         &client_packet_count);
+                if(frs_return == FRS_PRINT_ERROR) {
+                    fclose(data_out_file);
+                    data_out_file = NULL;
+                    return frs_return;
+                }
+
+            }
+        }
+    }
+
+    if (ferror(data_out_file)) {
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                      "Error reading temporary file %s: %s", follow_info->data_out_filename,
+                      g_strerror(errno));
+        fclose(data_out_file);
+        data_out_file = NULL;
+        return FRS_READ_ERROR;
+    }
+
+    fclose(data_out_file);
+    data_out_file = NULL;
+    return FRS_OK;
+}
+
+
+
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()".  (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+FollowStreamDialog::follow_read_udp_stream()
+{
+    guint32 global_client_pos = 0, global_server_pos = 0;
+    guint32 server_packet_count = 0;
+    guint32 client_packet_count = 0;
+    guint32 *global_pos;
+    gboolean skip;
+    GList* cur;
+    frs_return_t frs_return;
+    follow_record_t *follow_record;
+    char *buffer;
+
+
+    for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
+        follow_record = (follow_record_t *)cur->data;
+        skip = FALSE;
+        if (!follow_record->is_server) {
+            global_pos = &global_client_pos;
+            if(follow_info->show_stream == FROM_SERVER) {
+                skip = TRUE;
+            }
+        } else {
+            global_pos = &global_server_pos;
+            if (follow_info->show_stream == FROM_CLIENT) {
+                skip = TRUE;
+            }
+        }
+
+        if (!skip) {
+            buffer = (char *)g_memdup(follow_record->data->data,
+                                      follow_record->data->len);
+
+            frs_return = follow_show(
+                        buffer,
+                        follow_record->data->len,
+                        follow_record->is_server,
+                        global_pos,
+                        &server_packet_count,
+                        &client_packet_count);
+            g_free(buffer);
+            if(frs_return == FRS_PRINT_ERROR)
+                return frs_return;
+        }
+    }
+
+    return FRS_OK;
+}
+
+
+FollowStreamDialog::~FollowStreamDialog()
+{
+    delete ui;
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/follow_stream_dialog.h b/ui/qt/follow_stream_dialog.h
new file mode 100644 (file)
index 0000000..c977f0c
--- /dev/null
@@ -0,0 +1,161 @@
+/* follow_stream_dialog.h
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef FOLLOW_STREAM_DIALOG_H
+#define FOLLOW_STREAM_DIALOG_H
+
+#include <QDialog>
+#include <QMessageBox>
+
+#include <unistd.h>
+
+#include "config.h"
+
+#include "qt_ui_utils.h"
+
+#include <epan/follow.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/charsets.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/ipproto.h>
+#include <epan/charsets.h>
+#include <epan/plugins.h>
+#include <epan/tap.h>
+
+#include "../file.h"
+#include "ui/alert_box.h"
+#include "ui/simple_dialog.h"
+#include "ui/utf8_entities.h"
+#include "wsutil/tempfile.h"
+#include <wsutil/file_util.h>
+#include "ws_symbol_export.h"
+
+
+#include "globals.h"
+#include "file.h"
+
+#include "version_info.h"
+
+
+#include <QtGui>
+#include "../follow.h"
+
+WS_DLL_PUBLIC FILE *data_out_file;
+
+typedef struct {
+    follow_type_t   follow_type;
+    show_stream_t   show_stream;
+    show_type_t     show_type;
+    char            *data_out_filename;
+    gboolean        is_ipv6;
+    char            *filter_out_filter;
+    GList           *payload;
+    guint           bytes_written[2]; /* Index with FROM_CLIENT or FROM_SERVER for readability. */
+    guint           client_port;
+    address         client_ip;
+} follow_info_t;
+
+namespace Ui {
+class FollowStreamDialog;
+}
+
+class FollowStreamDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit FollowStreamDialog(QWidget *parent = 0);
+    ~FollowStreamDialog();
+
+    bool Follow(QString previous_filter_, follow_type_t type);
+
+    frs_return_t
+    follow_show(char *buffer, size_t nchars, gboolean is_from_server,
+            guint32 *global_pos, guint32 *server_packet_count,
+            guint32 *client_packet_count);
+
+    frs_return_t
+    follow_read_stream();
+
+    frs_return_t
+    follow_read_tcp_stream();
+
+    frs_return_t
+    follow_read_udp_stream();
+
+    frs_return_t
+    follow_read_ssl_stream();
+
+    void
+    follow_stream();
+
+    void add_text(char *buffer, size_t nchars, gboolean is_from_server);
+
+private slots:
+    void on_cbCharset_currentIndexChanged(int index);
+    void on_cbDirections_currentIndexChanged(int index);
+    void HelpButton();
+    void FilterOut();
+    void FindText();
+    void SaveAs();
+    void Print();
+//    void on_bNext_clicked();
+//    void on_bPrevious_clicked();
+
+signals:
+    void updateFilter(QString &filter, bool force);
+
+private:
+    Ui::FollowStreamDialog  *ui;
+
+    QPushButton             *bFilterOut;
+    QPushButton             *bFind;
+    QPushButton             *bPrint;
+    QPushButton             *bSave;
+
+    follow_info_t           *follow_info;
+
+    bool                    save_as;
+    QFile                   file;
+};
+
+
+
+#endif // FOLLOW_STREAM_DIALOG_H
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/follow_stream_dialog.ui b/ui/qt/follow_stream_dialog.ui
new file mode 100644 (file)
index 0000000..bf9b120
--- /dev/null
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FollowStreamDialog</class>
+ <widget class="QDialog" name="FollowStreamDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>667</width>
+    <height>426</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="QGroupBox" name="groupBox">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>0</y>
+     <width>651</width>
+     <height>381</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Stream contents</string>
+   </property>
+   <widget class="QComboBox" name="cbDirections">
+    <property name="geometry">
+     <rect>
+      <x>11</x>
+      <y>352</y>
+      <width>461</width>
+      <height>27</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QTextEdit" name="teStreamContent">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>20</y>
+      <width>631</width>
+      <height>321</height>
+     </rect>
+    </property>
+    <property name="readOnly">
+     <bool>true</bool>
+    </property>
+   </widget>
+   <widget class="QWidget" name="layoutWidget">
+    <property name="geometry">
+     <rect>
+      <x>480</x>
+      <y>350</y>
+      <width>158</width>
+      <height>29</height>
+     </rect>
+    </property>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Charset</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="cbCharset">
+       <property name="currentIndex">
+        <number>4</number>
+       </property>
+       <item>
+        <property name="text">
+         <string>ASCII</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>EBCDIC</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>C Arrays</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Hex dump</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Raw</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+    </layout>
+   </widget>
+  </widget>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>390</y>
+     <width>631</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Help</set>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 8e5e6502fcff10ea968aba5d931f248dbeba51dd..94a4b41d2b46083ec8cc00ebaae1a0829416a6f7 100644 (file)
@@ -111,6 +111,7 @@ MainWindow::MainWindow(QWidget *parent) :
     updateRecentFiles();
 
     connect(&summary_dialog_, SIGNAL(captureCommentChanged()), this, SLOT(updateForUnsavedChanges()));
+    connect(&follow_stream_dialog_, SIGNAL(updateFilter(QString&, bool)), this, SLOT(filterPackets(QString&, bool)));
 
     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
@@ -263,6 +264,8 @@ MainWindow::MainWindow(QWidget *parent) :
             this, SLOT(setMenusForSelectedPacket()));
     connect(packet_list_, SIGNAL(packetDissectionChanged()),
             this, SLOT(redissectPackets()));
+    connect(packet_list_, SIGNAL(setMenusFollowStream()),
+            this, SLOT(setMenusForFollowStream()));
 
     connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
             main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
@@ -289,6 +292,11 @@ MainWindow::~MainWindow()
     delete main_ui_;
 }
 
+QString MainWindow::getFilter()
+{
+    return df_combo_box_->itemText(df_combo_box_->count());
+}
+
 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
 {
     pipe_source_        = source;
@@ -1272,6 +1280,34 @@ void MainWindow::setTitlebarForCaptureInProgress()
 
 // Menu state
 
+void MainWindow::setMenusForFollowStream()
+{
+    if (!cap_file_)
+        return;
+
+    if (!cap_file_->edt)
+        return;
+
+    main_ui_->actionAnalyzeFollowTCPStream->setEnabled(false);
+    main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false);
+    main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false);
+
+    if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP)
+    {
+        main_ui_->actionAnalyzeFollowTCPStream->setEnabled(true);
+    }
+
+    if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP)
+    {
+        main_ui_->actionAnalyzeFollowUDPStream->setEnabled(true);
+    }
+
+    if ( epan_dissect_packet_contains_field(cap_file_->edt, "ssl") )
+    {
+        main_ui_->actionAnalyzeFollowSSLStream->setEnabled(true);
+    }
+}
+
 /* Enable or disable menu items based on whether you have a capture file
    you've finished reading and, if you have one, whether it's been saved
    and whether it could be saved except by copying the raw packet data. */
index bba37468ff8a3842c932b9313d42e3a28a941c44..d73173247ab839b3ceb0d677e9604a0f0b93c58f 100644 (file)
@@ -57,6 +57,7 @@
 #include "file_set_dialog.h"
 #include "capture_file_dialog.h"
 #include "summary_dialog.h"
+#include "follow_stream_dialog.h"
 
 class QAction;
 
@@ -73,6 +74,8 @@ public:
     ~MainWindow();
     void setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb);
 
+    QString getFilter();
+
 protected:
     bool eventFilter(QObject *obj, QEvent *event);
     void keyPressEvent(QKeyEvent *event);
@@ -108,6 +111,7 @@ private:
     SummaryDialog summary_dialog_;
     ByteViewTab *byte_view_tab_;
     QWidget *empty_pane_;
+    FollowStreamDialog follow_stream_dialog
 
     bool capture_stopping_;
     bool capture_filter_valid_;
@@ -188,6 +192,7 @@ private slots:
 
     void updateRecentFiles();
     void recentActionTriggered();
+    void setMenusForFollowStream();
     void setMenusForSelectedPacket();
     void setMenusForSelectedTreeRow(field_info *fi = NULL);
     void interfaceSelectionChanged();
@@ -269,6 +274,9 @@ private slots:
     void on_actionAnalyzePAFOrSelected_triggered();
     void on_actionAnalyzePAFAndNotSelected_triggered();
     void on_actionAnalyzePAFOrNotSelected_triggered();
+    void on_actionAnalyzeFollowTCPStream_triggered();
+    void on_actionAnalyzeFollowUDPStream_triggered();
+    void on_actionAnalyzeFollowSSLStream_triggered();
 
     void on_actionHelpContents_triggered();
     void on_actionHelpMPWireshark_triggered();
index 39985655933e126d3388d7096ed357f143f1808d..f55d193fad318b5213cec2c704bab1c0688a2f50 100644 (file)
     <addaction name="actionCaptureRestart"/>
     <addaction name="actionCaptureCaptureFilters"/>
     <addaction name="actionCaptureRefreshInterfaces"/>
+    <addaction name="separator"/>
    </widget>
    <widget class="QMenu" name="menuHelp">
     <property name="title">
     <addaction name="actionApplyAsColumn"/>
     <addaction name="menuApplyAsFilter"/>
     <addaction name="menuPrepareAFilter"/>
+    <addaction name="separator"/>
+    <addaction name="actionAnalyzeFollowTCPStream"/>
+    <addaction name="actionAnalyzeFollowUDPStream"/>
+    <addaction name="actionAnalyzeFollowSSLStream"/>
    </widget>
    <widget class="QMenu" name="menuStatistics">
     <property name="enabled">
     <string>TCP window scaling</string>
    </property>
   </action>
+  <action name="actionAnalyzeFollowTCPStream">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Follow TCP Stream</string>
+   </property>
+  </action>
+  <action name="actionAnalyzeFollowUDPStream">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Follow UDP Stream</string>
+   </property>
+  </action>
+  <action name="actionAnalyzeFollowSSLStream">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Follow SSL Stream</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
index 9cef7b6442e963b22db9965980743044481b24f0..b80e51547733c26ccb6e3437e3d98ae1629cd9d9 100644 (file)
@@ -199,8 +199,13 @@ void MainWindow::filterPackets(QString& new_filter, bool force)
     if (cf_status == CF_OK) {
         emit displayFilterSuccess(true);
         if (new_filter.length() > 0) {
-            if (df_combo_box_->findText(new_filter) < 0) {
+            int index = df_combo_box_->findText(new_filter);
+            if (index == -1) {
                 df_combo_box_->insertItem(0, new_filter);
+                df_combo_box_->setCurrentIndex(0);
+            }
+            else {
+                df_combo_box_->setCurrentIndex(index);
             }
         }
     } else {
@@ -463,6 +468,12 @@ void MainWindow::captureFileClosed(const capture_file *cf) {
 
     summary_dialog_.close();
 
+    if (df_combo_box_)
+    {
+        df_combo_box_->lineEdit()->setText("");
+        df_combo_box_->applyDisplayFilter();
+    }
+
     setTitlebarForSelectedTreeRow();
     setMenusForSelectedTreeRow();
 }
@@ -1690,6 +1701,57 @@ void MainWindow::on_actionAnalyzePAFOrNotSelected_triggered()
     matchSelectedFilter(MatchSelectedOrNot, false, false);
 }
 
+void MainWindow::on_actionAnalyzeFollowTCPStream_triggered()
+{
+    follow_stream_dialog_.Follow(getFilter(), FOLLOW_TCP);
+
+    if (follow_stream_dialog_.isMinimized() == true)
+    {
+        follow_stream_dialog_.showNormal();
+    }
+    else
+    {
+        follow_stream_dialog_.show();
+    }
+
+    follow_stream_dialog_.raise();
+    follow_stream_dialog_.activateWindow();
+}
+
+void MainWindow::on_actionAnalyzeFollowUDPStream_triggered()
+{
+    follow_stream_dialog_.Follow(getFilter(), FOLLOW_UDP);
+
+    if (follow_stream_dialog_.isMinimized() == true)
+    {
+        follow_stream_dialog_.showNormal();
+    }
+    else
+    {
+        follow_stream_dialog_.show();
+    }
+
+    follow_stream_dialog_.raise();
+    follow_stream_dialog_.activateWindow();
+}
+
+void MainWindow::on_actionAnalyzeFollowSSLStream_triggered()
+{
+    follow_stream_dialog_.Follow(getFilter(), FOLLOW_SSL);
+
+    if (follow_stream_dialog_.isMinimized() == true)
+    {
+        follow_stream_dialog_.showNormal();
+    }
+    else
+    {
+        follow_stream_dialog_.show();
+    }
+
+    follow_stream_dialog_.raise();
+    follow_stream_dialog_.activateWindow();
+}
+
 // Next / previous / first / last slots in packet_list
 
 // Statistics Menu
index ca7f0ea33f06fe47f9c6a71b60dc136aa625e31a..64fe9a940f5c8e00b7da10bf741ca47841988eb8 100644 (file)
@@ -35,6 +35,7 @@
 #include "packet_list.h"
 #include "proto_tree.h"
 #include "wireshark_application.h"
+#include <epan/ipproto.h>
 
 #include "qt_ui_utils.h"
 
@@ -246,6 +247,17 @@ PacketList::PacketList(QWidget *parent) :
     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditTimeShift"));
     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditPacketComment"));
 
+    ctx_menu_.addSeparator();
+    submenu = new QMenu(tr("Follow..."));
+    ctx_menu_.addMenu(submenu);
+    submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
+    submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
+    submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowSSLStream"));
+    filter_actions_ << submenu->actions();
+    //    "     <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n"
+    //    "     <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n"
+    //    "     <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n"
+
     ctx_menu_.addSeparator();
 //    "     <menuitem name='ManuallyResolveAddress' action='/ManuallyResolveAddress'/>\n"
     ctx_menu_.addSeparator();
@@ -414,6 +426,7 @@ void PacketList::selectionChanged (const QItemSelection & selected, const QItemS
     int row = selected.first().top();
     cf_select_packet(cap_file_, row);
     related_packet_delegate_.clear();
+    emit setMenusFollowStream();
 
     if (!cap_file_->edt) return;
 
@@ -447,8 +460,50 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event)
     bool fa_enabled = filter_actions_[0]->isEnabled();
     QAction *act;
 
-    foreach (act, filter_actions_) {
+
+    foreach (act, filter_actions_)
+    {
         act->setEnabled(true);
+
+
+        // check follow stream
+        if (act->text().contains("TCP"))
+        {
+            if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP)
+            {
+                act->setEnabled(true);
+            }
+            else
+            {
+                act->setEnabled(false);
+            }
+        }
+
+
+        if (act->text().contains("UDP"))
+        {
+            if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP)
+            {
+                act->setEnabled(true);
+            }
+            else
+            {
+                act->setEnabled(false);
+            }
+        }
+
+
+        if (act->text().contains("SSL"))
+        {
+            if (epan_dissect_packet_contains_field(cap_file_->edt, "ssl"))
+            {
+                act->setEnabled(true);
+            }
+            else
+            {
+                act->setEnabled(false);
+            }
+        }
     }
     ctx_column_ = columnAt(event->x());
     ctx_menu_.exec(event->globalPos());
index d7c397890105a6e92822dfe152deb627cc455570..aef9508e4a8d19af97ebac2cb33f5975c2cde724 100644 (file)
@@ -56,6 +56,7 @@ protected:
     void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected);
     void contextMenuEvent(QContextMenuEvent *event);
 
+
 private:
     PacketListModel *packet_list_model_;
     ProtoTree *proto_tree_;
@@ -74,6 +75,7 @@ private:
 
 signals:
     void packetDissectionChanged();
+    void setMenusFollowStream();
 
 public slots:
     void setCaptureFile(capture_file *cf);