aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2015-09-03 11:18:13 -0700
committerGerald Combs <gerald@wireshark.org>2015-09-09 21:57:08 +0000
commitcd9f163eb91d4c70977f669472d5acaf7a4fbe7c (patch)
tree268428ad90ed1a46ae67df78c3124fc751e40b2d
parent302b03a0bbe3702f93eced884d8103528c35e8d7 (diff)
Add the IAX2 Analysis dialog.
Copied from the RTP Analysis dialog, just like the GTK+ version. Change-Id: I111020bc4073a3a3ba583bdace51a91ee5fef300 Reviewed-on: https://code.wireshark.org/review/10447 Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
-rw-r--r--docbook/wsug_src/WSUG_chapter_telephony.asciidoc9
-rw-r--r--epan/dissectors/packet-iax2.h6
-rw-r--r--ui/gtk/iax2_analysis.c5
-rw-r--r--ui/help_url.c2
-rw-r--r--ui/help_url.h3
-rw-r--r--ui/qt/CMakeLists.txt3
-rw-r--r--ui/qt/Makefile.am8
-rw-r--r--ui/qt/Makefile.common8
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/iax2_analysis_dialog.cpp1330
-rw-r--r--ui/qt/iax2_analysis_dialog.h151
-rw-r--r--ui/qt/iax2_analysis_dialog.ui388
-rw-r--r--ui/qt/main_window.h3
-rw-r--r--ui/qt/main_window.ui11
-rw-r--r--ui/qt/main_window_slots.cpp15
-rw-r--r--ui/qt/rtp_analysis_dialog.cpp8
-rw-r--r--ui/qt/rtp_analysis_dialog.ui84
-rw-r--r--ui/tap-iax2-analysis.h23
18 files changed, 2019 insertions, 41 deletions
diff --git a/docbook/wsug_src/WSUG_chapter_telephony.asciidoc b/docbook/wsug_src/WSUG_chapter_telephony.asciidoc
index 0144defe4d..12e6c9c91c 100644
--- a/docbook/wsug_src/WSUG_chapter_telephony.asciidoc
+++ b/docbook/wsug_src/WSUG_chapter_telephony.asciidoc
@@ -38,6 +38,13 @@ The RTP Stream Analysis window further provides the option to save the RTP
payload (as raw data or, if in a PCM encoding, in an Audio file). Other options
a to export and plot various statistics on the RTP streams.
+[[ChTelIAX2Analysis]]
+
+=== IAX2 Analysis
+
+The ``IAX2 Analysis'' dialog shows statistics for the forward and reverse
+streams of a selected IAX2 call along with a graph.
+
[[ChTelVoipCalls]]
=== VoIP Calls
@@ -97,4 +104,4 @@ link:wireshark-wiki-site:[]Statistics[wireshark-wiki-site:[]Statistics] pages.
++++++++++++++++++++++++++++++++++++++
<!-- End of WSUG Chapter Telephony -->
-++++++++++++++++++++++++++++++++++++++ \ No newline at end of file
+++++++++++++++++++++++++++++++++++++++
diff --git a/epan/dissectors/packet-iax2.h b/epan/dissectors/packet-iax2.h
index 273dec9feb..fd926a8a80 100644
--- a/epan/dissectors/packet-iax2.h
+++ b/epan/dissectors/packet-iax2.h
@@ -22,8 +22,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef _PACKET_IAX2_H
-#define _PACKET_IAX2_H
+#ifndef __PACKET_IAX2_H__
+#define __PACKET_IAX2_H__
#include <epan/tap-voip.h>
@@ -259,4 +259,4 @@ typedef struct _iax2_dissector_info_t
guint32 circuit_id;
} iax2_dissector_info_t;
-#endif
+#endif /* __PACKET_IAX2_H__ */
diff --git a/ui/gtk/iax2_analysis.c b/ui/gtk/iax2_analysis.c
index 054ffb3e5b..9819304e20 100644
--- a/ui/gtk/iax2_analysis.c
+++ b/ui/gtk/iax2_analysis.c
@@ -3482,7 +3482,7 @@ get_int_value_from_proto_tree(proto_tree *protocol_tree,
#endif
/****************************************************************************/
-void
+static void
iax2_analysis(
address *ip_src_fwd,
guint16 port_src_fwd,
@@ -3608,7 +3608,7 @@ void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
guint16 port_dst_rev;
/* unsigned int ptype; */
- gchar filter_text[256];
+ const gchar *filter_text = "iax2 && (ip || ipv6)";
dfilter_t *sfcode;
gchar *err_msg;
capture_file *cf;
@@ -3620,7 +3620,6 @@ void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
rtp_stream_info_t *strinfo;
/* Try to compile the filter. */
- g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
if (!dfilter_compile(filter_text, &sfcode, &err_msg)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
g_free(err_msg);
diff --git a/ui/help_url.c b/ui/help_url.c
index 0e2fe5dce6..af7c94dcf0 100644
--- a/ui/help_url.c
+++ b/ui/help_url.c
@@ -347,6 +347,8 @@ topic_action_url(topic_action_e action)
break;
case(HELP_NEW_PACKET_DIALOG):
return user_guide_url("ChapterWork.html#ChWorkPacketSepView");
+ case(HELP_IAX2_ANALYSIS_DIALOG):
+ url = user_guide_url("ChTelIAX2Analysis.html");
case(TOPIC_ACTION_NONE):
default:
diff --git a/ui/help_url.h b/ui/help_url.h
index bfc80ea3f2..120b762137 100644
--- a/ui/help_url.h
+++ b/ui/help_url.h
@@ -117,7 +117,8 @@ typedef enum {
HELP_FILTER_SAVE_DIALOG,
HELP_TELEPHONY_VOIP_CALLS_DIALOG,
HELP_RTP_ANALYSIS_DIALOG,
- HELP_NEW_PACKET_DIALOG
+ HELP_NEW_PACKET_DIALOG,
+ HELP_IAX2_ANALYSIS_DIALOG
} topic_action_e;
/** Given a filename return a filesystem URL. Relative paths are prefixed with
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index 2f52a35f66..2e6c0c47bf 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -69,6 +69,7 @@ set(WIRESHARK_QT_HEADERS
funnel_text_dialog.h
funnel_statistics.h
gsm_map_summary_dialog.h
+ iax2_analysis_dialog.h
import_text_dialog.h
interface_tree.h
io_graph_dialog.h
@@ -204,6 +205,7 @@ set(WIRESHARK_QT_SRC
font_color_preferences_frame.cpp
funnel_string_dialog.cpp
funnel_text_dialog.cpp
+ iax2_analysis_dialog.cpp
import_text_dialog.cpp
interface_tree.cpp
label_stack.cpp
@@ -332,6 +334,7 @@ set(WIRESHARK_QT_UI
funnel_string_dialog.ui
funnel_text_dialog.ui
gsm_map_summary_dialog.ui
+ iax2_analysis_dialog.ui
import_text_dialog.ui
io_graph_dialog.ui
layout_preferences_frame.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index 64eaa948ee..16de1ab5dd 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -182,6 +182,8 @@ funnel_text_dialog.$(OBJEXT): ui_funnel_text_dialog.h
gsm_map_summary_dialog.$(OBJEXT): ui_gsm_map_summary_dialog.h
+iax2_analysis_dialog.$(OBJEXT): ui_iax2_analysis_dialog.h
+
import_text_dialog.$(OBJEXT): ui_import_text_dialog.h
io_graph_dialog.$(OBJEXT): ui_io_graph_dialog.h
@@ -226,10 +228,6 @@ print_dialog.$(OBJEXT): ui_print_dialog.h
progress_frame.$(OBJEXT): ui_progress_frame.h
-rtp_analysis_dialog.$(OBJEXT): ui_rtp_analysis_dialog.h
-
-rtp_stream_dialog.$(OBJEXT): ui_rtp_stream_dialog.h
-
profile_dialog.$(OBJEXT): ui_profile_dialog.h
protocol_hierarchy_dialog.$(OBJEXT): ui_protocol_hierarchy_dialog.h
@@ -240,6 +238,8 @@ remote_settings_dialog.$(OBJEXT): ui_remote_settings_dialog.h
resolved_addresses_dialog.$(OBJEXT): ui_resolved_addresses_dialog.h
+rtp_analysis_dialog.$(OBJEXT): ui_rtp_analysis_dialog.h
+
rtp_stream_dialog.$(OBJEXT): ui_rtp_stream_dialog.h
search_frame.$(OBJEXT): ui_search_frame.h
diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common
index 161bf17715..a796e46f89 100644
--- a/ui/qt/Makefile.common
+++ b/ui/qt/Makefile.common
@@ -55,7 +55,8 @@ NODIST_GENERATED_HEADER_FILES = \
ui_font_color_preferences_frame.h \
ui_funnel_string_dialog.h \
ui_funnel_text_dialog.h \
- ui_gsm_map_summary_dialog.h \
+ ui_gsm_map_summary_dialog.h \
+ ui_iax2_analysis_dialog.h \
ui_import_text_dialog.h \
ui_io_graph_dialog.h \
ui_layout_preferences_frame.h \
@@ -183,7 +184,8 @@ MOC_HDRS = \
funnel_string_dialog.h \
funnel_text_dialog.h \
funnel_statistics.h \
- gsm_map_summary_dialog.h \
+ gsm_map_summary_dialog.h \
+ iax2_analysis_dialog.h \
import_text_dialog.h \
interface_tree.h \
io_graph_dialog.h \
@@ -285,6 +287,7 @@ UI_FILES = \
funnel_string_dialog.ui \
funnel_text_dialog.ui \
gsm_map_summary_dialog.ui \
+ iax2_analysis_dialog.ui \
import_text_dialog.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
@@ -424,6 +427,7 @@ WIRESHARK_QT_SRC = \
font_color_preferences_frame.cpp \
funnel_string_dialog.cpp \
funnel_text_dialog.cpp \
+ iax2_analysis_dialog.cpp \
import_text_dialog.cpp \
interface_tree.cpp \
label_stack.cpp \
diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro
index 6e23f2afda..5ecc77fa67 100644
--- a/ui/qt/Wireshark.pro
+++ b/ui/qt/Wireshark.pro
@@ -235,6 +235,7 @@ FORMS += \
funnel_string_dialog.ui \
funnel_text_dialog.ui \
gsm_map_summary_dialog.ui \
+ iax2_analysis_dialog.ui \
import_text_dialog.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
@@ -616,6 +617,7 @@ HEADERS += \
display_filter_edit.h \
file_set_dialog.h \
filter_dialog.h \
+ iax2_analysis_dialog.h \
import_text_dialog.h \
interface_tree.h \
io_graph_dialog.h \
@@ -695,6 +697,7 @@ SOURCES += \
funnel_text_dialog.cpp \
funnel_statistics.cpp \
gsm_map_summary_dialog.cpp \
+ iax2_analysis_dialog.cpp \
import_text_dialog.cpp \
interface_tree.cpp \
io_graph_dialog.cpp \
diff --git a/ui/qt/iax2_analysis_dialog.cpp b/ui/qt/iax2_analysis_dialog.cpp
new file mode 100644
index 0000000000..6b166a148f
--- /dev/null
+++ b/ui/qt/iax2_analysis_dialog.cpp
@@ -0,0 +1,1330 @@
+/* iax2_analysis_dialog.cpp
+ *
+ * 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 "iax2_analysis_dialog.h"
+#include "ui_iax2_analysis_dialog.h"
+
+#include "file.h"
+#include "frame_tvbuff.h"
+
+#include <epan/epan_dissect.h>
+#include <epan/rtp_pt.h>
+
+#include <epan/dfilter/dfilter.h>
+
+#include <epan/dissectors/packet-iax2.h>
+
+#include "ui/help_url.h"
+#ifdef IAX2_RTP_STREAM_CHECK
+#include "ui/rtp_stream.h"
+#endif
+#include "ui/utf8_entities.h"
+
+#include <wsutil/g711.h>
+#include <wsutil/pint.h>
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QTemporaryFile>
+
+#include "color_utils.h"
+#include "qt_ui_utils.h"
+#include "stock_icon.h"
+#include "wireshark_application.h"
+
+/*
+ * @file RTP stream analysis dialog
+ *
+ * Displays forward and reverse RTP streams and graphs each stream
+ */
+
+// To do:
+// - Progress bar for tapping and saving.
+// - Add a refresh button and/or action.
+// - Fixup output file names.
+// - Add a graph title and legend when saving?
+
+enum {
+ packet_col_,
+ delta_col_,
+ jitter_col_,
+ bandwidth_col_,
+ status_col_,
+ length_col_
+};
+
+static const QRgb color_rtp_warn_ = 0xffdbbf;
+
+enum { iax2_analysis_type_ = 1000 };
+class Iax2AnalysisTreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ Iax2AnalysisTreeWidgetItem(QTreeWidget *tree, tap_iax2_stat_t *statinfo, packet_info *pinfo) :
+ QTreeWidgetItem(tree, iax2_analysis_type_)
+ {
+ frame_num_ = pinfo->fd->num;
+ pkt_len_ = pinfo->fd->pkt_len;
+ flags_ = statinfo->flags;
+ if (flags_ & STAT_FLAG_FIRST) {
+ delta_ = 0.0;
+ jitter_ = 0.0;
+ } else {
+ delta_ = statinfo->delta;
+ jitter_ = statinfo->jitter;
+ }
+ bandwidth_ = statinfo->bandwidth;
+ ok_ = false;
+
+ QColor bg_color = QColor();
+ QString status;
+
+ if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
+ status = QObject::tr("Wrong sequence number");
+ bg_color = ColorUtils::expert_color_error;
+ } else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
+ status = QObject::tr("Payload changed to PT=%1").arg(statinfo->pt);
+ bg_color = color_rtp_warn_;
+ } else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
+ status = QObject::tr("Incorrect timestamp");
+ /* color = COLOR_WARNING; */
+ bg_color = color_rtp_warn_;
+ } else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
+ && !(statinfo->flags & STAT_FLAG_FIRST)
+ && !(statinfo->flags & STAT_FLAG_PT_CN)
+ && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
+ && !(statinfo->flags & STAT_FLAG_MARKER)) {
+ status = QObject::tr("Marker missing?");
+ bg_color = color_rtp_warn_;
+ } else {
+ if (statinfo->flags & STAT_FLAG_MARKER) {
+ bg_color = color_rtp_warn_;
+ }
+ }
+
+ if (status.isEmpty()) {
+ ok_ = true;
+ status = UTF8_CHECK_MARK;
+ }
+
+ setText(packet_col_, QString::number(frame_num_));
+ setText(delta_col_, QString::number(delta_, 'f', 2));
+ setText(jitter_col_, QString::number(jitter_, 'f', 2));
+ setText(bandwidth_col_, QString::number(bandwidth_, 'f', 2));
+ setText(status_col_, status);
+ setText(length_col_, QString::number(pkt_len_));
+
+ setTextAlignment(packet_col_, Qt::AlignRight);
+ setTextAlignment(delta_col_, Qt::AlignRight);
+ setTextAlignment(jitter_col_, Qt::AlignRight);
+ setTextAlignment(bandwidth_col_, Qt::AlignRight);
+ setTextAlignment(length_col_, Qt::AlignRight);
+
+ if (bg_color.isValid()) {
+ for (int col = 0; col < columnCount(); col++) {
+ setBackground(col, bg_color);
+ setForeground(col, ColorUtils::expert_color_foreground);
+ }
+ }
+ }
+
+ guint32 frameNum() { return frame_num_; }
+ bool frameStatus() { return ok_; }
+
+ QList<QVariant> rowData() {
+ QString marker_str;
+ QString status_str = ok_ ? "OK" : text(status_col_);
+
+ if (marker_) marker_str = "SET";
+
+ return QList<QVariant>()
+ << frame_num_ << delta_ << jitter_ << bandwidth_
+ << status_str << pkt_len_;
+ }
+
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ if (other.type() != iax2_analysis_type_) return QTreeWidgetItem::operator< (other);
+ const Iax2AnalysisTreeWidgetItem *other_row = static_cast<const Iax2AnalysisTreeWidgetItem *>(&other);
+
+ switch (treeWidget()->sortColumn()) {
+ case (packet_col_):
+ return frame_num_ < other_row->frame_num_;
+ break;
+ case (delta_col_):
+ return delta_ < other_row->delta_;
+ break;
+ case (jitter_col_):
+ return jitter_ < other_row->jitter_;
+ break;
+ case (bandwidth_col_):
+ return bandwidth_ < other_row->bandwidth_;
+ break;
+ case (length_col_):
+ return pkt_len_ < other_row->pkt_len_;
+ break;
+ default:
+ break;
+ }
+
+ // Fall back to string comparison
+ return QTreeWidgetItem::operator <(other);
+ }
+private:
+ guint32 frame_num_;
+ guint32 pkt_len_;
+ guint32 flags_;
+ double delta_;
+ double jitter_;
+ double bandwidth_;
+ bool marker_;
+ bool ok_;
+};
+
+enum {
+ fwd_jitter_graph_,
+ fwd_diff_graph_,
+ rev_jitter_graph_,
+ rev_diff_graph_,
+ num_graphs_
+};
+
+Iax2AnalysisDialog::Iax2AnalysisDialog(QWidget &parent, CaptureFile &cf) :
+ WiresharkDialog(parent, cf),
+ ui(new Ui::Iax2AnalysisDialog),
+ port_src_fwd_(0),
+ port_dst_fwd_(0),
+ port_src_rev_(0),
+ port_dst_rev_(0)
+{
+ ui->setupUi(this);
+ setWindowSubtitle(tr("IAX2 Stream Analysis"));
+
+ // XXX Use recent settings instead
+ resize(parent.width() * 4 / 5, parent.height() * 4 / 5);
+ ui->progressFrame->hide();
+
+ stream_ctx_menu_.addAction(ui->actionGoToPacket);
+ stream_ctx_menu_.addAction(ui->actionNextProblem);
+ stream_ctx_menu_.addSeparator();
+ stream_ctx_menu_.addAction(ui->actionSaveAudio);
+ stream_ctx_menu_.addAction(ui->actionSaveForwardAudio);
+ stream_ctx_menu_.addAction(ui->actionSaveReverseAudio);
+ stream_ctx_menu_.addSeparator();
+ stream_ctx_menu_.addAction(ui->actionSaveCsv);
+ stream_ctx_menu_.addAction(ui->actionSaveForwardCsv);
+ stream_ctx_menu_.addAction(ui->actionSaveReverseCsv);
+ stream_ctx_menu_.addSeparator();
+ stream_ctx_menu_.addAction(ui->actionSaveGraph);
+ ui->forwardTreeWidget->installEventFilter(this);
+ ui->forwardTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->forwardTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showStreamMenu(QPoint)));
+ ui->reverseTreeWidget->installEventFilter(this);
+ ui->reverseTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->reverseTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showStreamMenu(QPoint)));
+ connect(ui->streamGraph, SIGNAL(mousePress(QMouseEvent*)),
+ this, SLOT(graphClicked(QMouseEvent*)));
+
+ graph_ctx_menu_.addAction(ui->actionSaveGraph);
+
+ QStringList header_labels;
+ for (int i = 0; i < ui->forwardTreeWidget->columnCount(); i++) {
+ header_labels << ui->forwardTreeWidget->headerItem()->text(i);
+ }
+ ui->reverseTreeWidget->setHeaderLabels(header_labels);
+
+ memset(&src_fwd_, 0, sizeof(address));
+ memset(&dst_fwd_, 0, sizeof(address));
+ memset(&src_rev_, 0, sizeof(address));
+ memset(&dst_rev_, 0, sizeof(address));
+
+ QList<QCheckBox *> graph_cbs = QList<QCheckBox *>()
+ << ui->fJitterCheckBox << ui->fDiffCheckBox
+ << ui->rJitterCheckBox << ui->rDiffCheckBox;
+
+ for (int i = 0; i < num_graphs_; i++) {
+ QCPGraph *graph = ui->streamGraph->addGraph();
+ graph->setPen(QPen(ColorUtils::graph_colors_[i]));
+ graph->setName(graph_cbs[i]->text());
+ graphs_ << graph;
+ graph_cbs[i]->setChecked(true);
+ graph_cbs[i]->setIcon(StockIcon::colorIcon(ColorUtils::graph_colors_[i], QPalette::Text));
+ }
+ ui->streamGraph->xAxis->setLabel("Arrival Time");
+ ui->streamGraph->yAxis->setLabel("Value (ms)");
+
+ // We keep our temp files open for the lifetime of the dialog. The GTK+
+ // UI opens and closes at various points.
+ QString tempname = QString("%1/wireshark_iax2_f").arg(QDir::tempPath());
+ fwd_tempfile_ = new QTemporaryFile(tempname, this);
+ fwd_tempfile_->open();
+ tempname = QString("%1/wireshark_iax2_r").arg(QDir::tempPath());
+ rev_tempfile_ = new QTemporaryFile(tempname, this);
+ rev_tempfile_->open();
+
+ if (fwd_tempfile_->error() != QFile::NoError || rev_tempfile_->error() != QFile::NoError) {
+ err_str_ = tr("Unable to save RTP data.");
+ ui->actionSaveAudio->setEnabled(false);
+ ui->actionSaveForwardAudio->setEnabled(false);
+ ui->actionSaveReverseAudio->setEnabled(false);
+ }
+
+ QMenu *save_menu = new QMenu();
+ save_menu->addAction(ui->actionSaveAudio);
+ save_menu->addAction(ui->actionSaveForwardAudio);
+ save_menu->addAction(ui->actionSaveReverseAudio);
+ save_menu->addSeparator();
+ save_menu->addAction(ui->actionSaveCsv);
+ save_menu->addAction(ui->actionSaveForwardCsv);
+ save_menu->addAction(ui->actionSaveReverseCsv);
+ save_menu->addSeparator();
+ save_menu->addAction(ui->actionSaveGraph);
+ ui->buttonBox->button(QDialogButtonBox::Save)->setMenu(save_menu);
+
+ const gchar *filter_text = "iax2 && (ip || ipv6)";
+ dfilter_t *sfcode;
+ gchar *err_msg;
+
+ if (!dfilter_compile(filter_text, &sfcode, &err_msg)) {
+ QMessageBox::warning(this, tr("No IAX2 packets found"), QString("%1").arg(err_msg));
+ g_free(err_msg);
+ close();
+ }
+
+ if (!cap_file_.capFile() || !cap_file_.capFile()->current_frame) close();
+
+ frame_data *fdata = cap_file_.capFile()->current_frame;
+
+ if (!cf_read_record(cap_file_.capFile(), fdata)) close();
+
+ epan_dissect_t edt;
+
+ epan_dissect_init(&edt, cap_file_.capFile()->epan, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ epan_dissect_run(&edt, cap_file_.capFile()->cd_t, &cap_file_.capFile()->phdr,
+ frame_tvbuff_new_buffer(fdata, &cap_file_.capFile()->buf), fdata, NULL);
+
+ // This shouldn't happen (the menu item should be disabled) but check anyway
+ if (!dfilter_apply_edt(sfcode, &edt)) {
+ epan_dissect_cleanup(&edt);
+ dfilter_free(sfcode);
+ err_str_ = tr("Please select an IAX2 packet");
+ updateWidgets();
+ return;
+ }
+
+ dfilter_free(sfcode);
+
+ /* ok, it is a IAX2 frame, so let's get the ip and port values */
+ COPY_ADDRESS(&(src_fwd_), &(edt.pi.src));
+ COPY_ADDRESS(&(dst_fwd_), &(edt.pi.dst));
+ port_src_fwd_ = edt.pi.srcport;
+ port_dst_fwd_ = edt.pi.destport;
+
+ /* assume the inverse ip/port combination for the reverse direction */
+ COPY_ADDRESS(&(src_rev_), &(edt.pi.dst));
+ COPY_ADDRESS(&(dst_rev_), &(edt.pi.src));
+ port_src_rev_ = edt.pi.destport;
+ port_dst_rev_ = edt.pi.srcport;
+
+#if 0
+ /* check if it is Voice or MiniPacket */
+ bool ok;
+ getIntFromProtoTree(edt.tree, "iax2", "iax2.call", &ok);
+ if (!ok) {
+ err_str_ = tr("Please select an IAX2 packet.");
+ updateWidgets();
+ return;
+ }
+#endif
+
+#ifdef IAX2_RTP_STREAM_CHECK
+ rtpstream_tapinfot tapinfo;
+
+ /* Register the tap listener */
+ memset(&tapinfo, 0, sizeof(rtpstream_tapinfot));
+ tapinfo.tap_data = this;
+ tapinfo.mode = TAP_ANALYSE;
+
+// register_tap_listener_rtp_stream(&tapinfo, NULL);
+ /* Scan for RTP streams (redissect all packets) */
+ rtpstream_scan(&tapinfo, cap_file_.capFile(), NULL);
+
+ int num_streams = 0;
+ GList *filtered_list = NULL;
+ for (GList *strinfo_list = g_list_first(tapinfo.strinfo_list); strinfo_list; strinfo_list = g_list_next(strinfo_list)) {
+ rtp_stream_info_t * strinfo = (rtp_stream_info_t*)(strinfo_list->data);
+ << address_to_qstring(&strinfo->dest_addr) << address_to_qstring(&src_rev_) << address_to_qstring(&dst_rev_);
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_fwd_))
+ && (strinfo->src_port == port_src_fwd_)
+ && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_fwd_)))
+ && (strinfo->dest_port == port_dst_fwd_))
+ {
+ ++num_streams;
+ filtered_list = g_list_prepend(filtered_list, strinfo);
+ }
+
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_rev_))
+ && (strinfo->src_port == port_src_rev_)
+ && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_rev_)))
+ && (strinfo->dest_port == port_dst_rev_))
+ {
+ ++num_streams;
+ filtered_list = g_list_append(filtered_list, strinfo);
+ }
+ }
+
+ if (num_streams > 1) {
+ // Open the RTP streams dialog.
+ }
+#endif
+
+ connect(ui->tabWidget, SIGNAL(currentChanged(int)),
+ this, SLOT(updateWidgets()));
+ connect(ui->forwardTreeWidget, SIGNAL(itemSelectionChanged()),
+ this, SLOT(updateWidgets()));
+ connect(ui->reverseTreeWidget, SIGNAL(itemSelectionChanged()),
+ this, SLOT(updateWidgets()));
+ connect(&cap_file_, SIGNAL(captureFileClosing()),
+ this, SLOT(updateWidgets()));
+ updateWidgets();
+
+ registerTapListener("IAX2", this, NULL, 0, tapReset, tapPacket, tapDraw);
+ cap_file_.retapPackets();
+ removeTapListeners();
+
+ updateStatistics();
+}
+
+Iax2AnalysisDialog::~Iax2AnalysisDialog()
+{
+ delete ui;
+// remove_tap_listener_rtp_stream(&tapinfo);
+ delete fwd_tempfile_;
+ delete rev_tempfile_;
+}
+
+void Iax2AnalysisDialog::updateWidgets()
+{
+ bool enable_tab = false;
+ QString hint = err_str_;
+
+ if (hint.isEmpty()) {
+ enable_tab = true;
+ }
+
+ bool enable_nav = false;
+ if (!file_closed_
+ && ((ui->tabWidget->currentWidget() == ui->forwardTreeWidget
+ && ui->forwardTreeWidget->selectedItems().length() > 0)
+ || (ui->tabWidget->currentWidget() == ui->reverseTreeWidget
+ && ui->reverseTreeWidget->selectedItems().length() > 0))) {
+ enable_nav = true;
+ }
+ ui->actionGoToPacket->setEnabled(enable_nav);
+ ui->actionNextProblem->setEnabled(enable_nav);
+
+ if (enable_nav) {
+ hint.append(tr(" G: Go to packet, N: Next problem packet"));
+ }
+
+ bool enable_save_fwd_audio = fwd_tempfile_->isOpen();
+ bool enable_save_rev_audio = rev_tempfile_->isOpen();
+ ui->actionSaveAudio->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveForwardAudio->setEnabled(enable_save_fwd_audio);
+ ui->actionSaveReverseAudio->setEnabled(enable_save_rev_audio);
+
+ bool enable_save_fwd_csv = ui->forwardTreeWidget->topLevelItemCount() > 0;
+ bool enable_save_rev_csv = ui->reverseTreeWidget->topLevelItemCount() > 0;
+ ui->actionSaveCsv->setEnabled(enable_save_fwd_csv && enable_save_rev_csv);
+ ui->actionSaveForwardCsv->setEnabled(enable_save_fwd_csv);
+ ui->actionSaveReverseCsv->setEnabled(enable_save_rev_csv);
+
+ ui->tabWidget->setEnabled(enable_tab);
+ hint.prepend("<small><i>");
+ hint.append("</i></small>");
+ ui->hintLabel->setText(hint);
+}
+
+void Iax2AnalysisDialog::on_actionGoToPacket_triggered()
+{
+ if (file_closed_) return;
+ QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ if (!cur_tree || cur_tree->selectedItems().length() < 1) return;
+
+ QTreeWidgetItem *ti = cur_tree->selectedItems()[0];
+ if (ti->type() != iax2_analysis_type_) return;
+
+ Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast<Iax2AnalysisTreeWidgetItem *>((Iax2AnalysisTreeWidgetItem *)ti);
+ emit goToPacket(ra_ti->frameNum());
+}
+
+void Iax2AnalysisDialog::on_actionNextProblem_triggered()
+{
+ QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ if (!cur_tree || cur_tree->topLevelItemCount() < 2) return;
+
+ // Choose convenience over correctness.
+ if (cur_tree->selectedItems().length() < 1) {
+ cur_tree->setCurrentItem(cur_tree->topLevelItem(0));
+ }
+
+ QTreeWidgetItem *sel_ti = cur_tree->selectedItems()[0];
+ if (sel_ti->type() != iax2_analysis_type_) return;
+ QTreeWidgetItem *test_ti = cur_tree->itemBelow(sel_ti);
+ while (test_ti != sel_ti) {
+ if (!test_ti) test_ti = cur_tree->topLevelItem(0);
+ Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast<Iax2AnalysisTreeWidgetItem *>((Iax2AnalysisTreeWidgetItem *)test_ti);
+ if (!ra_ti->frameStatus()) {
+ cur_tree->setCurrentItem(ra_ti);
+ break;
+ }
+
+ test_ti = cur_tree->itemBelow(test_ti);
+ }
+}
+
+void Iax2AnalysisDialog::on_fJitterCheckBox_toggled(bool checked)
+{
+ ui->streamGraph->graph(fwd_jitter_graph_)->setVisible(checked);
+ updateGraph();
+}
+
+void Iax2AnalysisDialog::on_fDiffCheckBox_toggled(bool checked)
+{
+ ui->streamGraph->graph(fwd_diff_graph_)->setVisible(checked);
+ updateGraph();
+}
+
+void Iax2AnalysisDialog::on_rJitterCheckBox_toggled(bool checked)
+{
+ ui->streamGraph->graph(rev_jitter_graph_)->setVisible(checked);
+ updateGraph();
+}
+
+void Iax2AnalysisDialog::on_rDiffCheckBox_toggled(bool checked)
+{
+ ui->streamGraph->graph(rev_diff_graph_)->setVisible(checked);
+ updateGraph();
+}
+
+void Iax2AnalysisDialog::on_actionSaveAudio_triggered()
+{
+ saveAudio(dir_both_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveForwardAudio_triggered()
+{
+ saveAudio(dir_forward_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveReverseAudio_triggered()
+{
+ saveAudio(dir_reverse_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveCsv_triggered()
+{
+ saveCsv(dir_both_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveForwardCsv_triggered()
+{
+ saveCsv(dir_forward_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveReverseCsv_triggered()
+{
+ saveCsv(dir_reverse_);
+}
+
+void Iax2AnalysisDialog::on_actionSaveGraph_triggered()
+{
+ ui->tabWidget->setCurrentWidget(ui->graphTab);
+
+ QString file_name, extension;
+ QDir path(wsApp->lastOpenDir());
+ QString pdf_filter = tr("Portable Document Format (*.pdf)");
+ QString png_filter = tr("Portable Network Graphics (*.png)");
+ QString bmp_filter = tr("Windows Bitmap (*.bmp)");
+ // Gaze upon my beautiful graph with lossy artifacts!
+ QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)");
+ QString filter = QString("%1;;%2;;%3;;%4")
+ .arg(pdf_filter)
+ .arg(png_filter)
+ .arg(bmp_filter)
+ .arg(jpeg_filter);
+
+ QString save_file = path.canonicalPath();
+ if (!file_closed_) {
+ save_file += QString("/%1").arg(cap_file_.fileTitle());
+ }
+ file_name = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save Graph As" UTF8_HORIZONTAL_ELLIPSIS)),
+ save_file, filter, &extension);
+
+ if (!file_name.isEmpty()) {
+ bool save_ok = false;
+ // http://www.qcustomplot.com/index.php/support/forum/63
+// ui->streamGraph->legend->setVisible(true);
+ if (extension.compare(pdf_filter) == 0) {
+ save_ok = ui->streamGraph->savePdf(file_name);
+ } else if (extension.compare(png_filter) == 0) {
+ save_ok = ui->streamGraph->savePng(file_name);
+ } else if (extension.compare(bmp_filter) == 0) {
+ save_ok = ui->streamGraph->saveBmp(file_name);
+ } else if (extension.compare(jpeg_filter) == 0) {
+ save_ok = ui->streamGraph->saveJpg(file_name);
+ }
+// ui->streamGraph->legend->setVisible(false);
+ // else error dialog?
+ if (save_ok) {
+ path = QDir(file_name);
+ wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
+ }
+ }
+}
+
+void Iax2AnalysisDialog::on_buttonBox_helpRequested()
+{
+ wsApp->helpTopicAction(HELP_IAX2_ANALYSIS_DIALOG);
+}
+
+void Iax2AnalysisDialog::tapReset(void *tapinfoptr)
+{
+ Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast<Iax2AnalysisDialog *>((Iax2AnalysisDialog*)tapinfoptr);
+ if (!iax2_analysis_dialog) return;
+
+ iax2_analysis_dialog->resetStatistics();
+}
+
+gboolean Iax2AnalysisDialog::tapPacket(void *tapinfoptr, packet_info *pinfo, struct epan_dissect *, const void *iax2info_ptr)
+{
+ Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast<Iax2AnalysisDialog *>((Iax2AnalysisDialog*)tapinfoptr);
+ if (!iax2_analysis_dialog) return FALSE;
+
+ const iax2_info_t *iax2info = (const iax2_info_t *)iax2info_ptr;
+ if (!iax2info) return FALSE;
+
+ /* we ignore packets that are not displayed */
+ if (pinfo->fd->flags.passed_dfilter == 0)
+ return FALSE;
+
+ /* we ignore packets that carry no data */
+ if (iax2info->payload_len < 1)
+ return FALSE;
+
+ /* is it the forward direction? */
+ else if ((CMP_ADDRESS(&(iax2_analysis_dialog->src_fwd_), &(pinfo->src)) == 0)
+ && (iax2_analysis_dialog->port_src_fwd_ == pinfo->srcport)
+ && (CMP_ADDRESS(&(iax2_analysis_dialog->dst_fwd_), &(pinfo->dst)) == 0)
+ && (iax2_analysis_dialog->port_dst_fwd_ == pinfo->destport)) {
+
+ iax2_analysis_dialog->addPacket(true, pinfo, iax2info);
+ }
+ /* is it the reversed direction? */
+ else if ((CMP_ADDRESS(&(iax2_analysis_dialog->src_rev_), &(pinfo->src)) == 0)
+ && (iax2_analysis_dialog->port_src_rev_ == pinfo->srcport)
+ && (CMP_ADDRESS(&(iax2_analysis_dialog->dst_rev_), &(pinfo->dst)) == 0)
+ && (iax2_analysis_dialog->port_dst_rev_ == pinfo->destport)) {
+
+ iax2_analysis_dialog->addPacket(false, pinfo, iax2info);
+ }
+ return FALSE;
+}
+
+void Iax2AnalysisDialog::tapDraw(void *tapinfoptr)
+{
+ Iax2AnalysisDialog *iax2_analysis_dialog = dynamic_cast<Iax2AnalysisDialog *>((Iax2AnalysisDialog*)tapinfoptr);
+ if (!iax2_analysis_dialog) return;
+ iax2_analysis_dialog->updateStatistics();
+}
+
+void Iax2AnalysisDialog::resetStatistics()
+{
+ memset(&fwd_statinfo_, 0, sizeof(fwd_statinfo_));
+ memset(&rev_statinfo_, 0, sizeof(rev_statinfo_));
+
+ fwd_statinfo_.first_packet = TRUE;
+ rev_statinfo_.first_packet = TRUE;
+ fwd_statinfo_.reg_pt = PT_UNDEFINED;
+ rev_statinfo_.reg_pt = PT_UNDEFINED;
+
+ ui->forwardTreeWidget->clear();
+ ui->reverseTreeWidget->clear();
+
+ for (int i = 0; i < ui->streamGraph->graphCount(); i++) {
+ ui->streamGraph->graph(i)->clearData();
+ }
+
+ fwd_time_vals_.clear();
+ fwd_jitter_vals_.clear();
+ fwd_diff_vals_.clear();
+ rev_time_vals_.clear();
+ rev_jitter_vals_.clear();
+ rev_diff_vals_.clear();
+
+ fwd_tempfile_->resize(0);
+ rev_tempfile_->resize(0);
+}
+
+void Iax2AnalysisDialog::addPacket(bool forward, packet_info *pinfo, const struct _iax2_info_t *iax2info)
+{
+ /* add this RTP for future listening using the RTP Player*/
+// add_rtp_packet(rtpinfo, pinfo);
+
+ if (forward) {
+ iax2_packet_analyse(&fwd_statinfo_, pinfo, iax2info);
+ new Iax2AnalysisTreeWidgetItem(ui->forwardTreeWidget, &fwd_statinfo_, pinfo);
+
+ fwd_time_vals_.append((fwd_statinfo_.time - fwd_statinfo_.start_time));
+ fwd_jitter_vals_.append(fwd_statinfo_.jitter * 1000);
+ fwd_diff_vals_.append(fwd_statinfo_.diff * 1000);
+
+ savePayload(fwd_tempfile_, pinfo, iax2info);
+ } else {
+ iax2_packet_analyse(&rev_statinfo_, pinfo, iax2info);
+ new Iax2AnalysisTreeWidgetItem(ui->reverseTreeWidget, &rev_statinfo_, pinfo);
+
+ rev_time_vals_.append((rev_statinfo_.time - rev_statinfo_.start_time));
+ rev_jitter_vals_.append(rev_statinfo_.jitter * 1000);
+ rev_diff_vals_.append(rev_statinfo_.diff * 1000);
+
+ savePayload(rev_tempfile_, pinfo, iax2info);
+ }
+
+}
+
+// iax2_analysis.c:rtp_packet_save_payload
+const guint8 silence_pcmu_ = 0xff;
+const guint8 silence_pcma_ = 0x55;
+void Iax2AnalysisDialog::savePayload(QTemporaryFile *tmpfile, packet_info *pinfo, const struct _iax2_info_t *iax2info)
+{
+ /* Is this the first packet we got in this direction? */
+// if (statinfo->flags & STAT_FLAG_FIRST) {
+// if (saveinfo->fp == NULL) {
+// saveinfo->saved = FALSE;
+// saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
+// } else {
+// saveinfo->saved = TRUE;
+// }
+// }
+
+ /* Save the voice information */
+
+ /* If there was already an error, we quit */
+ if (!tmpfile->isOpen() || tmpfile->error() != QFile::NoError) return;
+
+ /* Quit if the captured length and packet length aren't equal.
+ */
+ if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
+ tmpfile->close();
+ err_str_ = tr("Can't save in a file: Wrong length of captured packets.");
+ return;
+ }
+
+ if (iax2info->payload_len > 0) {
+ const char *data = (const char *) iax2info->payload_data;
+ size_t nchars;
+
+ nchars = tmpfile->write(data, iax2info->payload_len);
+ if (nchars != (iax2info->payload_len)) {
+ /* Write error or short write */
+ err_str_ = tr("Can't save in a file: File I/O problem.");
+ tmpfile->close();
+ return;
+ }
+ return;
+ }
+ return;
+}
+
+void Iax2AnalysisDialog::updateStatistics()
+{
+ double f_duration = fwd_statinfo_.time - fwd_statinfo_.start_time; // s
+ double r_duration = rev_statinfo_.time - rev_statinfo_.start_time;
+#if 0 // Marked as "TODO" in tap-iax2-analysis.c:128
+ unsigned int f_expected = fwd_statinfo_.stop_seq_nr - fwd_statinfo_.start_seq_nr + 1;
+ unsigned int r_expected = rev_statinfo_.stop_seq_nr - rev_statinfo_.start_seq_nr + 1;
+ int f_lost = f_expected - fwd_statinfo_.total_nr;
+ int r_lost = r_expected - rev_statinfo_.total_nr;
+ double f_perc, r_perc;
+
+ if (f_expected) {
+ f_perc = (double)(f_lost*100)/(double)f_expected;
+ } else {
+ f_perc = 0;
+ }
+ if (r_expected) {
+ r_perc = (double)(r_lost*100)/(double)r_expected;
+ } else {
+ r_perc = 0;
+ }
+#endif
+
+ QString stats_tables = "<html><head></head><body>\n";
+ stats_tables += QString("<p>%1:%2 " UTF8_LEFT_RIGHT_ARROW)
+ .arg(address_to_qstring(&src_fwd_, true))
+ .arg(port_src_fwd_);
+ stats_tables += QString("<br>%1:%2</p>\n")
+ .arg(address_to_qstring(&dst_fwd_, true))
+ .arg(port_dst_fwd_);
+ stats_tables += "<h4>Forward</h4>\n";
+ stats_tables += "<p><table>\n";
+ stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>")
+ .arg(fwd_statinfo_.max_delta, 0, 'f', 2)
+ .arg(fwd_statinfo_.max_nr);
+ stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</tr>")
+ .arg(fwd_statinfo_.max_jitter, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</tr>")
+ .arg(fwd_statinfo_.mean_jitter, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">IAX2 Packets</th><td>%1</tr>")
+ .arg(fwd_statinfo_.total_nr);
+#if 0
+ stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</tr>")
+ .arg(f_expected);
+ stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</tr>")
+ .arg(f_lost).arg(f_perc, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</tr>")
+ .arg(fwd_statinfo_.sequence);
+#endif
+ stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</tr>")
+ .arg(f_duration, 0, 'f', 2);
+ stats_tables += "</table></p>\n";
+
+ stats_tables += "<h4>Reverse</h4>\n";
+ stats_tables += "<p><table>\n";
+ stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>")
+ .arg(rev_statinfo_.max_delta, 0, 'f', 2)
+ .arg(rev_statinfo_.max_nr);
+ stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</tr>")
+ .arg(rev_statinfo_.max_jitter, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</tr>")
+ .arg(rev_statinfo_.mean_jitter, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">IAX2 Packets</th><td>%1</tr>")
+ .arg(rev_statinfo_.total_nr);
+#if 0
+ stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</tr>")
+ .arg(r_expected);
+ stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</tr>")
+ .arg(r_lost).arg(r_perc, 0, 'f', 2);
+ stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</tr>")
+ .arg(rev_statinfo_.sequence);
+#endif
+ stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</tr>")
+ .arg(r_duration, 0, 'f', 2);
+ stats_tables += "</table></p></body>\n";
+
+ ui->statisticsLabel->setText(stats_tables);
+
+ for (int col = 0; col < ui->forwardTreeWidget->columnCount() - 1; col++) {
+ ui->forwardTreeWidget->resizeColumnToContents(col);
+ ui->reverseTreeWidget->resizeColumnToContents(col);
+ }
+
+ graphs_[fwd_jitter_graph_]->setData(fwd_time_vals_, fwd_jitter_vals_);
+ graphs_[fwd_diff_graph_]->setData(fwd_time_vals_, fwd_diff_vals_);
+ graphs_[rev_jitter_graph_]->setData(rev_time_vals_, rev_jitter_vals_);
+ graphs_[rev_diff_graph_]->setData(rev_time_vals_, rev_diff_vals_);
+
+ updateGraph();
+
+ updateWidgets();
+}
+
+void Iax2AnalysisDialog::updateGraph()
+{
+ for (int i = 0; i < ui->streamGraph->graphCount(); i++) {
+ if (ui->streamGraph->graph(i)->visible()) {
+ ui->streamGraph->graph(i)->rescaleAxes(i > 0);
+ }
+ }
+ ui->streamGraph->replot();
+}
+
+// iax2_analysis.c:copy_file
+enum { save_audio_none_, save_audio_au_, save_audio_raw_ };
+void Iax2AnalysisDialog::saveAudio(Iax2AnalysisDialog::StreamDirection direction)
+{
+ if (!fwd_tempfile_->isOpen() || !rev_tempfile_->isOpen()) return;
+
+ QString caption;
+
+ switch (direction) {
+ case dir_forward_:
+ caption = tr("Save forward stream audio");
+ break;
+ case dir_reverse_:
+ caption = tr("Save reverse stream audio");
+ break;
+ case dir_both_:
+ default:
+ caption = tr("Save audio");
+ break;
+ }
+
+ QString ext_filter = tr("Sun Audio (*.au)");
+ if (direction != dir_both_) {
+ ext_filter.append(tr(";;Raw (*.raw)"));
+ }
+ QString sel_filter;
+ QString file_path = QFileDialog::getSaveFileName(
+ this, caption, wsApp->lastOpenDir().absoluteFilePath("Saved RTP Audio.au"),
+ ext_filter, &sel_filter);
+
+ if (file_path.isEmpty()) return;
+
+ int save_format = save_audio_none_;
+ if (file_path.endsWith(".au")) {
+ save_format = save_audio_au_;
+ } else if (file_path.endsWith(".raw")) {
+ save_format = save_audio_raw_;
+ }
+
+ if (save_format == save_audio_none_) {
+ QMessageBox::warning(this, tr("Warning"), tr("Unable to save in that format"));
+ return;
+ }
+
+ QFile save_file(file_path);
+ gint16 sample;
+ gchar pd[4];
+ gboolean stop_flag = FALSE;
+ size_t nchars;
+
+ save_file.open(QIODevice::WriteOnly);
+ fwd_tempfile_->seek(0);
+ rev_tempfile_->seek(0);
+
+ if (save_file.error() != QFile::NoError) {
+ QMessageBox::warning(this, tr("Warning"), tr("Unable to save %1").arg(save_file.fileName()));
+ return;
+ }
+
+ ui->hintLabel->setText(tr("Saving %1" UTF8_HORIZONTAL_ELLIPSIS).arg(save_file.fileName()));
+ ui->progressFrame->showProgress(true, true, &stop_flag);
+
+ if (save_format == save_audio_au_) { /* au format */
+ /* First we write the .au header. XXX Hope this is endian independent */
+ /* the magic word 0x2e736e64 == .snd */
+ phton32(pd, 0x2e736e64);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* header offset == 24 bytes */
+ phton32(pd, 24);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* total length; it is permitted to set this to 0xffffffff */
+ phton32(pd, 0xffffffff);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* encoding format == 16-bit linear PCM */
+ phton32(pd, 3);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* sample rate == 8000 Hz */
+ phton32(pd, 8000);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* channels == 1 */
+ phton32(pd, 1);
+ nchars = save_file.write((const char *)pd, 4);
+ if (nchars != 4)
+ goto copy_file_err;
+
+ switch (direction) {
+ /* Only forward direction */
+ case dir_forward_:
+ {
+ char f_rawvalue;
+ while (fwd_tempfile_->getChar(&f_rawvalue)) {
+ if (stop_flag) {
+ break;
+ }
+ ui->progressFrame->setValue(fwd_tempfile_->pos() * 100 / fwd_tempfile_->size());
+
+ if (fwd_statinfo_.pt == PT_PCMU) {
+ sample = ulaw2linear((unsigned char)f_rawvalue);
+ phton16(pd, sample);
+ } else if (fwd_statinfo_.pt == PT_PCMA) {
+ sample = alaw2linear((unsigned char)f_rawvalue);
+ phton16(pd, sample);
+ } else {
+ goto copy_file_err;
+ }
+
+ nchars = save_file.write((const char *)pd, 2);
+ if (nchars < 2) {
+ goto copy_file_err;
+ }
+ }
+ break;
+ }
+ /* Only reverse direction */
+ case dir_reverse_:
+ {
+ char r_rawvalue;
+ while (rev_tempfile_->getChar(&r_rawvalue)) {
+ if (stop_flag) {
+ break;
+ }
+ ui->progressFrame->setValue(rev_tempfile_->pos() * 100 / rev_tempfile_->size());
+
+ if (rev_statinfo_.pt == PT_PCMU) {
+ sample = ulaw2linear((unsigned char)r_rawvalue);
+ phton16(pd, sample);
+ } else if (rev_statinfo_.pt == PT_PCMA) {
+ sample = alaw2linear((unsigned char)r_rawvalue);
+ phton16(pd, sample);
+ } else {
+ goto copy_file_err;
+ }
+
+ nchars = save_file.write((const char *)pd, 2);
+ if (nchars < 2) {
+ goto copy_file_err;
+ }
+ }
+ break;
+ }
+ /* Both directions */
+ case dir_both_:
+ {
+ char f_rawvalue, r_rawvalue;
+ guint32 f_write_silence = 0;
+ guint32 r_write_silence = 0;
+ /* since conversation in one way can start later than in the other one,
+ * we have to write some silence information for one channel */
+ if (fwd_statinfo_.start_time > rev_statinfo_.start_time) {
+ f_write_silence = (guint32)
+ ((fwd_statinfo_.start_time - rev_statinfo_.start_time)
+ * (8000/1000));
+ } else if (fwd_statinfo_.start_time < rev_statinfo_.start_time) {
+ r_write_silence = (guint32)
+ ((rev_statinfo_.start_time - fwd_statinfo_.start_time)
+ * (8000/1000));
+ }
+ for (;;) {
+ if (stop_flag) {
+ break;
+ }
+ int fwd_pct = fwd_tempfile_->pos() * 100 / fwd_tempfile_->size();
+ int rev_pct = rev_tempfile_->pos() * 100 / rev_tempfile_->size();
+ ui->progressFrame->setValue(qMin(fwd_pct, rev_pct));
+
+ if (f_write_silence > 0) {
+ rev_tempfile_->getChar(&r_rawvalue);
+ switch (fwd_statinfo_.reg_pt) {
+ case PT_PCMU:
+ f_rawvalue = silence_pcmu_;
+ break;
+ case PT_PCMA:
+ f_rawvalue = silence_pcma_;
+ break;
+ default:
+ f_rawvalue = 0;
+ break;
+ }
+ f_write_silence--;
+ } else if (r_write_silence > 0) {
+ fwd_tempfile_->getChar(&f_rawvalue);
+ switch (rev_statinfo_.reg_pt) {
+ case PT_PCMU:
+ r_rawvalue = silence_pcmu_;
+ break;
+ case PT_PCMA:
+ r_rawvalue = silence_pcma_;
+ break;
+ default:
+ r_rawvalue = 0;
+ break;
+ }
+ r_write_silence--;
+ } else {
+ fwd_tempfile_->getChar(&f_rawvalue);
+ rev_tempfile_->getChar(&r_rawvalue);
+ }
+ if (fwd_tempfile_->atEnd() && rev_tempfile_->atEnd())
+ break;
+ if ((fwd_statinfo_.pt == PT_PCMU)
+ && (rev_statinfo_.pt == PT_PCMU)) {
+ sample = (ulaw2linear((unsigned char)r_rawvalue)
+ + ulaw2linear((unsigned char)f_rawvalue)) / 2;
+ phton16(pd, sample);
+ }
+ else if ((fwd_statinfo_.pt == PT_PCMA)
+ && (rev_statinfo_.pt == PT_PCMA)) {
+ sample = (alaw2linear((unsigned char)r_rawvalue)
+ + alaw2linear((unsigned char)f_rawvalue)) / 2;
+ phton16(pd, sample);
+ } else {
+ goto copy_file_err;
+ }
+
+ nchars = save_file.write((const char *)pd, 2);
+ if (nchars < 2) {
+ goto copy_file_err;
+ }
+ }
+ }
+ }
+ } else if (save_format == save_audio_raw_) { /* raw format */
+ QFile *tempfile;
+ int progress_pct;
+
+ switch (direction) {
+ /* Only forward direction */
+ case dir_forward_: {
+ progress_pct = fwd_tempfile_->pos() * 100 / fwd_tempfile_->size();
+ tempfile = fwd_tempfile_;
+ break;
+ }
+ /* only reversed direction */
+ case dir_reverse_: {
+ progress_pct = rev_tempfile_->pos() * 100 / rev_tempfile_->size();
+ tempfile = rev_tempfile_;
+ break;
+ }
+ default: {
+ goto copy_file_err;
+ }
+ }
+
+ int chunk_size = 65536;
+ /* XXX how do you just copy the file? */
+ while (chunk_size > 0) {
+ if (stop_flag)
+ break;
+ QByteArray bytes = tempfile->read(chunk_size);
+ ui->progressFrame->setValue(progress_pct);
+
+ if (!save_file.write(bytes)) {
+ goto copy_file_err;
+ }
+ chunk_size = bytes.length();
+ }
+ }
+
+copy_file_err:
+ ui->progressFrame->hide();
+ updateWidgets();
+ return;
+}
+
+// XXX The GTK+ UI saves the length and timestamp.
+void Iax2AnalysisDialog::saveCsv(Iax2AnalysisDialog::StreamDirection direction)
+{
+ QString caption;
+
+ switch (direction) {
+ case dir_forward_:
+ caption = tr("Save forward stream CSV");
+ break;
+ case dir_reverse_:
+ caption = tr("Save reverse stream CSV");
+ break;
+ case dir_both_:
+ default:
+ caption = tr("Save CSV");
+ break;
+ }
+
+ QString file_path = QFileDialog::getSaveFileName(
+ this, caption, wsApp->lastOpenDir().absoluteFilePath("RTP Packet Data.csv"),
+ tr("Comma-separated values (*.csv)"));
+
+ if (file_path.isEmpty()) return;
+
+ QFile save_file(file_path);
+ save_file.open(QFile::WriteOnly);
+
+ if (direction == dir_forward_ || direction == dir_both_) {
+ save_file.write("Forward\n");
+
+ for (int row = 0; row < ui->forwardTreeWidget->topLevelItemCount(); row++) {
+ QTreeWidgetItem *ti = ui->forwardTreeWidget->topLevelItem(row);
+ if (ti->type() != iax2_analysis_type_) continue;
+ Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast<Iax2AnalysisTreeWidgetItem *>((Iax2AnalysisTreeWidgetItem *)ti);
+ QStringList values;
+ foreach (QVariant v, ra_ti->rowData()) {
+ if (!v.isValid()) {
+ values << "\"\"";
+ } else if ((int) v.type() == (int) QMetaType::QString) {
+ values << QString("\"%1\"").arg(v.toString());
+ } else {
+ values << v.toString();
+ }
+ }
+ save_file.write(values.join(",").toUtf8());
+ save_file.write("\n");
+ }
+ }
+ if (direction == dir_both_) {
+ save_file.write("\n");
+ }
+ if (direction == dir_reverse_ || direction == dir_both_) {
+ save_file.write("Reverse\n");
+
+ for (int row = 0; row < ui->forwardTreeWidget->topLevelItemCount(); row++) {
+ QTreeWidgetItem *ti = ui->forwardTreeWidget->topLevelItem(row);
+ if (ti->type() != iax2_analysis_type_) continue;
+ Iax2AnalysisTreeWidgetItem *ra_ti = dynamic_cast<Iax2AnalysisTreeWidgetItem *>((Iax2AnalysisTreeWidgetItem *)ti);
+ QStringList values;
+ foreach (QVariant v, ra_ti->rowData()) {
+ if (!v.isValid()) {
+ values << "\"\"";
+ } else if ((int) v.type() == (int) QMetaType::QString) {
+ values << QString("\"%1\"").arg(v.toString());
+ } else {
+ values << v.toString();
+ }
+ }
+ save_file.write(values.join(",").toUtf8());
+ save_file.write("\n");
+ }
+ }
+}
+
+#if 0
+// Adapted from iax2_analysis.c:process_node
+guint32 Iax2AnalysisDialog::processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar *proto_field, bool *ok)
+{
+ field_info *finfo;
+ proto_node *proto_sibling_node;
+ header_field_info *hfssrc;
+ ipv4_addr *ipv4;
+
+ finfo = PNODE_FINFO(ptree_node);
+
+ /* Caller passed top of the protocol tree. Expected child node */
+ g_assert(finfo);
+
+ if (hfinformation == (finfo->hfinfo)) {
+ hfssrc = proto_registrar_get_byname(proto_field);
+ if (hfssrc == NULL) {
+ return 0;
+ }
+ for (ptree_node = ptree_node->first_child;
+ ptree_node != NULL;
+ ptree_node = ptree_node->next) {
+ finfo = PNODE_FINFO(ptree_node);
+ if (hfssrc == finfo->hfinfo) {
+ guint32 result;
+ if (hfinformation->type == FT_IPv4) {
+ ipv4 = (ipv4_addr *)fvalue_get(&finfo->value);
+ result = ipv4_get_net_order_addr(ipv4);
+ } else {
+ result = fvalue_get_uinteger(&finfo->value);
+ }
+ if (ok) *ok = true;
+ return result;
+ }
+ }
+ if (!ptree_node) {
+ return 0;
+ }
+ }
+
+ proto_sibling_node = ptree_node->next;
+
+ if (proto_sibling_node) {
+ return processNode(proto_sibling_node, hfinformation, proto_field, ok);
+ } else {
+ return 0;
+ }
+}
+
+// Adapted from iax2_analysis.c:get_int_value_from_proto_tree
+guint32 Iax2AnalysisDialog::getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok)
+{
+ proto_node *ptree_node;
+ header_field_info *hfinformation;
+
+ if (ok) *ok = false;
+
+ hfinformation = proto_registrar_get_byname(proto_name);
+ if (hfinformation == NULL) {
+ return 0;
+ }
+
+ ptree_node = ((proto_node *)protocol_tree)->first_child;
+ if (!ptree_node) {
+ return 0;
+ }
+
+ return processNode(ptree_node, hfinformation, proto_field, ok);
+}
+#endif
+
+bool Iax2AnalysisDialog::eventFilter(QObject *, QEvent *event)
+{
+ if (event->type() != QEvent::KeyPress) return false;
+
+ QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
+
+ switch(kevt->key()) {
+ case Qt::Key_G:
+ on_actionGoToPacket_triggered();
+ return true;
+ case Qt::Key_N:
+ on_actionNextProblem_triggered();
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+void Iax2AnalysisDialog::graphClicked(QMouseEvent *event)
+{
+ updateWidgets();
+ if (event->button() == Qt::RightButton) {
+ graph_ctx_menu_.exec(event->globalPos());
+ }
+}
+
+void Iax2AnalysisDialog::showStreamMenu(QPoint pos)
+{
+ QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ if (!cur_tree) return;
+
+ updateWidgets();
+ stream_ctx_menu_.popup(cur_tree->viewport()->mapToGlobal(pos));
+}
+
+/*
+ * 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/iax2_analysis_dialog.h b/ui/qt/iax2_analysis_dialog.h
new file mode 100644
index 0000000000..2d867216db
--- /dev/null
+++ b/ui/qt/iax2_analysis_dialog.h
@@ -0,0 +1,151 @@
+/* iax2_analysis_dialog.h
+ *
+ * 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 IAX2_ANALYSIS_DIALOG_H
+#define IAX2_ANALYSIS_DIALOG_H
+
+// The GTK+ UI checks for multiple RTP streams, and if found opens the RTP
+// stream dialog. That seems to violate the principle of least surprise.
+// Migrate the code but disable it.
+// #define IAX2_RTP_STREAM_CHECK
+
+#include <config.h>
+
+#include <glib.h>
+
+#include <epan/address.h>
+
+#include "ui/tap-iax2-analysis.h"
+
+#include <QAbstractButton>
+#include <QMenu>
+
+#include "wireshark_dialog.h"
+
+namespace Ui {
+class Iax2AnalysisDialog;
+}
+
+class QCPGraph;
+class QTemporaryFile;
+
+class Iax2AnalysisDialog : public WiresharkDialog
+{
+ Q_OBJECT
+
+public:
+ explicit Iax2AnalysisDialog(QWidget &parent, CaptureFile &cf);
+ ~Iax2AnalysisDialog();
+
+signals:
+ void goToPacket(int packet_num);
+
+protected slots:
+ virtual void updateWidgets();
+
+private slots:
+ void on_actionGoToPacket_triggered();
+ void on_actionNextProblem_triggered();
+ void on_fJitterCheckBox_toggled(bool checked);
+ void on_fDiffCheckBox_toggled(bool checked);
+ void on_rJitterCheckBox_toggled(bool checked);
+ void on_rDiffCheckBox_toggled(bool checked);
+ void on_actionSaveAudio_triggered();
+ void on_actionSaveForwardAudio_triggered();
+ void on_actionSaveReverseAudio_triggered();
+ void on_actionSaveCsv_triggered();
+ void on_actionSaveForwardCsv_triggered();
+ void on_actionSaveReverseCsv_triggered();
+ void on_actionSaveGraph_triggered();
+ void on_buttonBox_helpRequested();
+ void showStreamMenu(QPoint pos);
+ void graphClicked(QMouseEvent *event);
+
+private:
+ Ui::Iax2AnalysisDialog *ui;
+ enum StreamDirection { dir_both_, dir_forward_, dir_reverse_ };
+
+ address src_fwd_;
+ guint32 port_src_fwd_;
+ address dst_fwd_;
+ guint32 port_dst_fwd_;
+ address src_rev_;
+ guint32 port_src_rev_;
+ address dst_rev_;
+ guint32 port_dst_rev_;
+
+ tap_iax2_stat_t fwd_statinfo_;
+ tap_iax2_stat_t rev_statinfo_;
+
+ QTemporaryFile *fwd_tempfile_;
+ QTemporaryFile *rev_tempfile_;
+
+ // Graph data for QCustomPlot
+ QList<QCPGraph *>graphs_;
+ QVector<double> fwd_time_vals_;
+ QVector<double> fwd_jitter_vals_;
+ QVector<double> fwd_diff_vals_;
+
+ QVector<double> rev_time_vals_;
+ QVector<double> rev_jitter_vals_;
+ QVector<double> rev_diff_vals_;
+
+ QString err_str_;
+
+ QMenu stream_ctx_menu_;
+ QMenu graph_ctx_menu_;
+
+ // Tap callbacks
+ static void tapReset(void *tapinfo_ptr);
+ static gboolean tapPacket(void *tapinfo_ptr, packet_info *pinfo, struct epan_dissect *, const void *iax2info_ptr);
+ static void tapDraw(void *tapinfo_ptr);
+
+ void resetStatistics();
+ void addPacket(bool forward, packet_info *pinfo, const struct _iax2_info_t *iax2info);
+ void savePayload(QTemporaryFile *tmpfile, packet_info *pinfo, const struct _iax2_info_t *iax2info);
+ void updateStatistics();
+ void updateGraph();
+
+ void saveAudio(StreamDirection direction);
+ void saveCsv(StreamDirection direction);
+
+#if 0
+ guint32 processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar* proto_field, bool *ok);
+ guint32 getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok);
+#endif
+
+ bool eventFilter(QObject*, QEvent* event);
+};
+
+#endif // IAX2_ANALYSIS_DIALOG_H
+
+/*
+ * 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/iax2_analysis_dialog.ui b/ui/qt/iax2_analysis_dialog.ui
new file mode 100644
index 0000000000..0c0ecbe283
--- /dev/null
+++ b/ui/qt/iax2_analysis_dialog.ui
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Iax2AnalysisDialog</class>
+ <widget class="QDialog" name="Iax2AnalysisDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>650</width>
+ <height>475</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="statisticsLabel">
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:medium; font-weight:600;&quot;&gt;Forward&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:medium; font-weight:600;&quot;&gt;Reverse&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QTreeWidget" name="forwardTreeWidget">
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="title">
+ <string>Forward</string>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Packet</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Delta (ms)</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Jitter (ms)</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Bandwidth</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Length</string>
+ </property>
+ </column>
+ </widget>
+ <widget class="QTreeWidget" name="reverseTreeWidget">
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <attribute name="title">
+ <string>Reverse</string>
+ </attribute>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ </widget>
+ <widget class="QWidget" name="graphTab">
+ <attribute name="title">
+ <string>Graph</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0,0">
+ <item>
+ <widget class="QCustomPlot" name="streamGraph" native="true"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="forwardHorizontalLayout" stretch="0,0,0,1">
+ <item>
+ <widget class="QCheckBox" name="fJitterCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward jitter values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Forward Jitter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="fDiffCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Forward Difference</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="reverseHorizontalLayout" stretch="0,0,0,1">
+ <item>
+ <widget class="QCheckBox" name="rJitterCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse jitter values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Reverse Jitter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="rDiffCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Reverse Difference</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
+ <item>
+ <widget class="QLabel" name="hintLabel">
+ <property name="text">
+ <string>&lt;small&gt;&lt;i&gt;A hint.&lt;/i&gt;&lt;/small&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ProgressFrame" name="progressFrame">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <action name="actionSaveAudio">
+ <property name="text">
+ <string>Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the audio data for both channels.</string>
+ </property>
+ </action>
+ <action name="actionSaveForwardAudio">
+ <property name="text">
+ <string>Forward Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the forward stream audio data.</string>
+ </property>
+ </action>
+ <action name="actionSaveReverseAudio">
+ <property name="text">
+ <string>Reverse Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the reverse stream audio data.</string>
+ </property>
+ </action>
+ <action name="actionSaveCsv">
+ <property name="text">
+ <string>CSV</string>
+ </property>
+ <property name="toolTip">
+ <string>Save both tables as CSV.</string>
+ </property>
+ </action>
+ <action name="actionSaveForwardCsv">
+ <property name="text">
+ <string>Forward Stream CSV</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the forward table as CSV.</string>
+ </property>
+ </action>
+ <action name="actionSaveReverseCsv">
+ <property name="text">
+ <string>Reverse Stream CSV</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the reverse table as CSV.</string>
+ </property>
+ </action>
+ <action name="actionSaveGraph">
+ <property name="text">
+ <string>Save Graph</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the graph image.</string>
+ </property>
+ </action>
+ <action name="actionGoToPacket">
+ <property name="text">
+ <string>Go to Packet</string>
+ </property>
+ <property name="toolTip">
+ <string>Select the corresponding packet in the packet list.</string>
+ </property>
+ <property name="shortcut">
+ <string>G</string>
+ </property>
+ </action>
+ <action name="actionNextProblem">
+ <property name="text">
+ <string>Next Problem Packet</string>
+ </property>
+ <property name="toolTip">
+ <string>Go to the next problem packet</string>
+ </property>
+ <property name="shortcut">
+ <string>N</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QCustomPlot</class>
+ <extends>QWidget</extends>
+ <header>qcustomplot.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ProgressFrame</class>
+ <extends>QFrame</extends>
+ <header>progress_frame.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Iax2AnalysisDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Iax2AnalysisDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index f1cf8b541c..e36969b0ed 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -532,8 +532,9 @@ private slots:
void openVoipCallsDialog(bool all_flows = false);
void on_actionTelephonyVoipCalls_triggered();
void on_actionTelephonyGsmMapSummary_triggered();
- void on_actionTelephonyMtp3Summary_triggered();
+ void on_actionTelephonyIax2StreamAnalysis_triggered();
void on_actionTelephonyISUPMessages_triggered();
+ void on_actionTelephonyMtp3Summary_triggered();
void on_actionTelephonyRTPStreams_triggered();
void on_actionTelephonyRTPStreamAnalysis_triggered();
void on_actionTelephonyRTSPPacketCounter_triggered();
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index afd5de4273..b71d333574 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -545,6 +545,7 @@
<addaction name="actionTelephonyVoipCalls"/>
<addaction name="menuANSI"/>
<addaction name="menuGSM"/>
+ <addaction name="actionTelephonyIax2StreamAnalysis"/>
<addaction name="actionTelephonyISUPMessages"/>
<addaction name="menuLTE"/>
<addaction name="menuMTP3"/>
@@ -2642,12 +2643,20 @@
</action>
<action name="actionTelephonyRTPStreamAnalysis">
<property name="text">
- <string>Analyze RTP Stream</string>
+ <string>Stream Analysis</string>
</property>
<property name="toolTip">
<string>RTP Stream Analysis</string>
</property>
</action>
+ <action name="actionTelephonyIax2StreamAnalysis">
+ <property name="text">
+ <string>IAX2 Stream Analysis</string>
+ </property>
+ <property name="toolTip">
+ <string>IAX2 Stream Analysis</string>
+ </property>
+ </action>
<action name="actionViewEditResolvedName">
<property name="text">
<string>Edit Resolved Name</string>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index fd86f4a9c6..9abd2331d0 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -104,6 +104,7 @@
#include "filter_dialog.h"
#include "funnel_statistics.h"
#include "gsm_map_summary_dialog.h"
+#include "iax2_analysis_dialog.h"
#include "io_graph_dialog.h"
#include "lbm_stream_dialog.h"
#include "lbm_uimflow_dialog.h"
@@ -2954,10 +2955,12 @@ void MainWindow::on_actionTelephonyGsmMapSummary_triggered()
gms_dialog->show();
}
-void MainWindow::on_actionTelephonyMtp3Summary_triggered()
+void MainWindow::on_actionTelephonyIax2StreamAnalysis_triggered()
{
- Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_);
- mtp3s_dialog->show();
+ Iax2AnalysisDialog *iax2_analysis_dialog = new Iax2AnalysisDialog(*this, capture_file_);
+ connect(iax2_analysis_dialog, SIGNAL(goToPacket(int)),
+ packet_list_, SLOT(goToPacket(int)));
+ iax2_analysis_dialog->show();
}
void MainWindow::on_actionTelephonyISUPMessages_triggered()
@@ -2965,6 +2968,12 @@ void MainWindow::on_actionTelephonyISUPMessages_triggered()
openStatisticsTreeDialog("isup_msg");
}
+void MainWindow::on_actionTelephonyMtp3Summary_triggered()
+{
+ Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_);
+ mtp3s_dialog->show();
+}
+
void MainWindow::on_actionTelephonyRTPStreams_triggered()
{
RtpStreamDialog *rtp_stream_dialog = new RtpStreamDialog(*this, capture_file_);
diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp
index 493012da00..79eb1dd9e9 100644
--- a/ui/qt/rtp_analysis_dialog.cpp
+++ b/ui/qt/rtp_analysis_dialog.cpp
@@ -71,9 +71,9 @@ enum {
status_col_
};
-const QRgb color_cn_ = 0xbfbfff;
-const QRgb color_rtp_warn_ = 0xffdbbf;
-const QRgb color_pt_event_ = 0xefffff;
+static const QRgb color_cn_ = 0xbfbfff;
+static const QRgb color_rtp_warn_ = 0xffdbbf;
+static const QRgb color_pt_event_ = 0xefffff;
enum { rtp_analysis_type_ = 1000 };
class RtpAnalysisTreeWidgetItem : public QTreeWidgetItem
@@ -115,7 +115,7 @@ public:
status = "Suspected duplicate (MAC address) only delta time calculated";
bg_color = color_rtp_warn_;
} else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
- status = QString("Payload changed to PT=%u").arg(statinfo->pt);
+ status = QString("Payload changed to PT=%1").arg(statinfo->pt);
if (statinfo->flags & STAT_FLAG_PT_T_EVENT) {
status.append(" telephone/event");
}
diff --git a/ui/qt/rtp_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui
index a69b27cc0e..6611089996 100644
--- a/ui/qt/rtp_analysis_dialog.ui
+++ b/ui/qt/rtp_analysis_dialog.ui
@@ -81,7 +81,7 @@
</column>
<column>
<property name="text">
- <string>Jitter</string>
+ <string>Jitter (ms)</string>
</property>
</column>
<column>
@@ -130,7 +130,7 @@
<widget class="QCustomPlot" name="streamGraph" native="true"/>
</item>
<item>
- <layout class="QHBoxLayout" name="forwardHorizontalLayout">
+ <layout class="QHBoxLayout" name="forwardHorizontalLayout" stretch="0,0,0,0,0,1">
<item>
<widget class="QCheckBox" name="fJitterCheckBox">
<property name="toolTip">
@@ -142,6 +142,19 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="QCheckBox" name="fDiffCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -152,6 +165,19 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="QCheckBox" name="fDeltaCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward delta values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -161,10 +187,23 @@
</property>
</widget>
</item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="reverseHorizontalLayout">
+ <layout class="QHBoxLayout" name="reverseHorizontalLayout" stretch="0,0,0,0,0,1">
<item>
<widget class="QCheckBox" name="rJitterCheckBox">
<property name="toolTip">
@@ -176,6 +215,19 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="QCheckBox" name="rDiffCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -186,6 +238,19 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="QCheckBox" name="rDeltaCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse delta values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -195,6 +260,19 @@
</property>
</widget>
</item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</item>
</layout>
diff --git a/ui/tap-iax2-analysis.h b/ui/tap-iax2-analysis.h
index 43d196afac..9f4b4b467c 100644
--- a/ui/tap-iax2-analysis.h
+++ b/ui/tap-iax2-analysis.h
@@ -39,16 +39,9 @@
* @todo what's this?
*/
-void iax2_analysis(
- address *ip_src_fwd,
- guint16 port_src_fwd,
- address *ip_dst_fwd,
- guint16 port_dst_fwd,
- address *ip_src_rev,
- guint16 port_src_rev,
- address *ip_dst_rev,
- guint16 port_dst_rev
- );
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
/****************************************************************************/
/* structure that holds the information about the forward and reversed direction */
@@ -86,8 +79,8 @@ typedef struct _tap_iax2_stat_t {
guint16 stop_seq_nr;
guint32 total_nr;
guint32 sequence;
- gboolean under;
- gint cycles;
+ gboolean under; /* Unused? */
+ gint cycles; /* Unused? */
guint16 pt;
int reg_pt;
} tap_iax2_stat_t;
@@ -104,14 +97,14 @@ typedef struct _tap_iax2_stat_t {
#define STAT_FLAG_REG_PT_CHANGE 0x040
#define STAT_FLAG_WRONG_TIMESTAMP 0x080
-/* forward */
-struct _rtp_info;
-
/* function for analysing an IAX2 packet. Called from iax2_analysis. */
extern void iax2_packet_analyse(tap_iax2_stat_t *statinfo,
packet_info *pinfo,
const struct _iax2_info_t *iax2info);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
#endif /* __TAP_IAX2_ANALYSIS_H__ */