1 /* voip_calls_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "voip_calls_dialog.h"
11 #include <ui_voip_calls_dialog.h>
15 #include "epan/addr_resolv.h"
16 #include "epan/dissectors/packet-h225.h"
18 #include "ui/rtp_stream.h"
19 #include "ui/rtp_stream_id.h"
21 #include <ui/qt/utils/qt_ui_utils.h>
22 #include "rtp_player_dialog.h"
23 #include "sequence_dialog.h"
24 #include <ui/qt/utils/stock_icon.h>
25 #include "progress_frame.h"
26 #include "main_application.h"
27 #include <ui/qt/models/voip_calls_info_model.h>
30 #include <QContextMenuEvent>
31 #include <QToolButton>
34 // - More context menu items
35 // - Don't select on right click
37 // - Add a screenshot to the user's guide
38 // - Add filter for quickly searching through list?
41 // - Preparing a filter overwrites the existing filter. The GTK+ UI appends.
42 // We'll probably have to add an "append" parameter to MainWindow::filterPackets.
44 enum { voip_calls_type_
= 1000 };
46 VoipCallsDialog
*VoipCallsDialog::pinstance_voip_
{nullptr};
47 VoipCallsDialog
*VoipCallsDialog::pinstance_sip_
{nullptr};
48 std::mutex
VoipCallsDialog::init_mutex_
;
50 VoipCallsDialog
*VoipCallsDialog::openVoipCallsDialogVoip(QWidget
&parent
, CaptureFile
&cf
, QObject
*packet_list
)
52 std::lock_guard
<std::mutex
> lock(init_mutex_
);
53 if (pinstance_voip_
== nullptr)
55 pinstance_voip_
= new VoipCallsDialog(parent
, cf
, false);
56 connect(pinstance_voip_
, SIGNAL(goToPacket(int)),
57 packet_list
, SLOT(goToPacket(int)));
59 return pinstance_voip_
;
62 VoipCallsDialog
*VoipCallsDialog::openVoipCallsDialogSip(QWidget
&parent
, CaptureFile
&cf
, QObject
*packet_list
)
64 std::lock_guard
<std::mutex
> lock(init_mutex_
);
65 if (pinstance_sip_
== nullptr)
67 pinstance_sip_
= new VoipCallsDialog(parent
, cf
, true);
68 connect(pinstance_sip_
, SIGNAL(goToPacket(int)),
69 packet_list
, SLOT(goToPacket(int)));
71 return pinstance_sip_
;
74 VoipCallsDialog::VoipCallsDialog(QWidget
&parent
, CaptureFile
&cf
, bool all_flows
) :
75 WiresharkDialog(parent
, cf
),
76 all_flows_(all_flows
),
77 ui(new Ui::VoipCallsDialog
),
79 voip_calls_tap_listeners_removed_(false)
82 loadGeometry(parent
.width() * 4 / 5, parent
.height() * 2 / 3);
83 ui
->callTreeView
->installEventFilter(this);
85 // Create the model that stores the actual data and the proxy model that is
86 // responsible for sorting and filtering data in the display.
87 call_infos_model_
= new VoipCallsInfoModel(this);
88 cache_model_
= new CacheProxyModel(this);
89 cache_model_
->setSourceModel(call_infos_model_
);
90 sorted_model_
= new VoipCallsInfoSortedModel(this);
91 sorted_model_
->setSourceModel(cache_model_
);
92 ui
->callTreeView
->setModel(sorted_model_
);
94 connect(ui
->callTreeView
->selectionModel(), SIGNAL(selectionChanged(QItemSelection
,QItemSelection
)),
95 this, SLOT(updateWidgets()));
96 ui
->callTreeView
->sortByColumn(VoipCallsInfoModel::StartTime
, Qt::AscendingOrder
);
97 setWindowSubtitle(all_flows_
? tr("SIP Flows") : tr("VoIP Calls"));
99 sequence_button_
= ui
->buttonBox
->addButton(ui
->actionFlowSequence
->text(), QDialogButtonBox::ActionRole
);
100 sequence_button_
->setToolTip(ui
->actionFlowSequence
->toolTip());
101 prepare_button_
= ui
->buttonBox
->addButton(ui
->actionPrepareFilter
->text(), QDialogButtonBox::ActionRole
);
102 prepare_button_
->setToolTip(ui
->actionPrepareFilter
->toolTip());
103 player_button_
= RtpPlayerDialog::addPlayerButton(ui
->buttonBox
, this);
105 connect (ui
->todCheckBox
, &QAbstractButton::toggled
, this, &VoipCallsDialog::switchTimeOfDay
);
107 copy_button_
= ui
->buttonBox
->addButton(ui
->actionCopyButton
->text(), QDialogButtonBox::ActionRole
);
108 copy_button_
->setToolTip(ui
->actionCopyButton
->toolTip());
109 QMenu
*copy_menu
= new QMenu(copy_button_
);
111 ca
= copy_menu
->addAction(tr("as CSV"));
112 connect(ca
, SIGNAL(triggered()), this, SLOT(copyAsCSV()));
113 ca
= copy_menu
->addAction(tr("as YAML"));
114 connect(ca
, SIGNAL(triggered()), this, SLOT(copyAsYAML()));
115 copy_button_
->setMenu(copy_menu
);
116 connect(&cap_file_
, SIGNAL(captureEvent(CaptureEvent
)),
117 this, SLOT(captureEvent(CaptureEvent
)));
119 connect(this, SIGNAL(rtpStreamsDialogSelectRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpStreamsDialogSelectRtpStreams(QVector
<rtpstream_id_t
*>)));
120 connect(this, SIGNAL(rtpStreamsDialogDeselectRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpStreamsDialogDeselectRtpStreams(QVector
<rtpstream_id_t
*>)));
122 memset (&tapinfo_
, 0, sizeof(tapinfo_
));
123 tapinfo_
.tap_packet
= tapPacket
;
124 tapinfo_
.tap_reset
= tapReset
;
125 tapinfo_
.tap_draw
= tapDraw
;
126 tapinfo_
.tap_data
= this;
127 tapinfo_
.callsinfos
= g_queue_new();
128 tapinfo_
.h225_cstype
= H225_OTHER
;
129 tapinfo_
.fs_option
= all_flows_
? FLOW_ALL
: FLOW_ONLY_INVITES
; /* flow show option */
130 tapinfo_
.graph_analysis
= sequence_analysis_info_new();
131 tapinfo_
.graph_analysis
->name
= "voip";
132 sequence_info_
= new SequenceInfo(tapinfo_
.graph_analysis
);
133 shown_callsinfos_
= g_queue_new();
135 voip_calls_init_all_taps(&tapinfo_
);
136 if (cap_file_
.isValid() && cap_file_
.capFile()->dfilter
) {
137 // Activate display filter checking
138 tapinfo_
.apply_display_filter
= true;
139 ui
->displayFilterCheckBox
->setChecked(true);
142 connect(ui
->displayFilterCheckBox
, &QCheckBox::toggled
,
143 this, &VoipCallsDialog::displayFilterCheckBoxToggled
);
144 connect(this, SIGNAL(updateFilter(QString
, bool)),
145 &parent
, SLOT(filterPackets(QString
, bool)));
146 connect(&parent
, SIGNAL(displayFilterSuccess(bool)),
147 this, SLOT(displayFilterSuccess(bool)));
148 connect(this, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector
<rtpstream_id_t
*>)),
149 &parent
, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector
<rtpstream_id_t
*>)));
150 connect(this, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector
<rtpstream_id_t
*>)),
151 &parent
, SLOT(rtpPlayerDialogAddRtpStreams(QVector
<rtpstream_id_t
*>)));
152 connect(this, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector
<rtpstream_id_t
*>)),
153 &parent
, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector
<rtpstream_id_t
*>)));
155 ProgressFrame::addToButtonBox(ui
->buttonBox
, &parent
);
159 if (cap_file_
.isValid()) {
160 tapinfo_
.session
= cap_file_
.capFile()->epan
;
161 cap_file_
.delayedRetapPackets();
165 bool VoipCallsDialog::eventFilter(QObject
*, QEvent
*event
)
167 if (ui
->callTreeView
->hasFocus() && event
->type() == QEvent::KeyPress
) {
168 QKeyEvent
&keyEvent
= static_cast<QKeyEvent
&>(*event
);
169 switch(keyEvent
.key()) {
171 if (keyEvent
.modifiers() == Qt::ControlModifier
) {
173 on_actionSelectInvert_triggered();
178 if (keyEvent
.modifiers() == Qt::ControlModifier
) {
180 on_actionSelectAll_triggered();
182 } else if (keyEvent
.modifiers() == (Qt::ShiftModifier
| Qt::ControlModifier
)) {
184 on_actionSelectNone_triggered();
189 on_actionSelectRtpStreams_triggered();
192 on_actionDeselectRtpStreams_triggered();
201 VoipCallsDialog::~VoipCallsDialog()
203 std::lock_guard
<std::mutex
> lock(init_mutex_
);
204 if ((all_flows_
&& (pinstance_sip_
!= nullptr))
205 || (!all_flows_
&& (pinstance_voip_
!= nullptr))
209 voip_calls_reset_all_taps(&tapinfo_
);
210 if (!voip_calls_tap_listeners_removed_
) {
211 voip_calls_remove_all_tap_listeners(&tapinfo_
);
212 voip_calls_tap_listeners_removed_
= true;
214 sequence_info_
->unref();
215 g_queue_free(tapinfo_
.callsinfos
);
216 // We don't need to clear shown_callsinfos_ data, it was shared
217 // with tapinfo_.callsinfos and was cleared
218 // during voip_calls_reset_all_taps
219 g_queue_free(shown_callsinfos_
);
221 pinstance_sip_
= nullptr;
223 pinstance_voip_
= nullptr;
228 void VoipCallsDialog::removeTapListeners()
230 if (!voip_calls_tap_listeners_removed_
) {
231 voip_calls_remove_all_tap_listeners(&tapinfo_
);
232 voip_calls_tap_listeners_removed_
= true;
234 WiresharkDialog::removeTapListeners();
237 void VoipCallsDialog::captureFileClosing()
239 // The time formatting is currently provided by VoipCallsInfoModel, but when
240 // the cache is active, the ToD cannot be modified.
241 cache_model_
->setSourceModel(NULL
);
242 if (!voip_calls_tap_listeners_removed_
) {
243 voip_calls_remove_all_tap_listeners(&tapinfo_
);
244 voip_calls_tap_listeners_removed_
= true;
246 tapinfo_
.session
= NULL
;
248 WiresharkDialog::captureFileClosing();
251 void VoipCallsDialog::captureFileClosed()
253 // The time formatting is currently provided by VoipCallsInfoModel, but when
254 // the cache is active, the ToD cannot be modified.
255 ui
->todCheckBox
->setEnabled(false);
256 ui
->displayFilterCheckBox
->setEnabled(false);
258 WiresharkDialog::captureFileClosed();
261 void VoipCallsDialog::contextMenuEvent(QContextMenuEvent
*event
)
263 bool selected
= ui
->callTreeView
->selectionModel()->hasSelection();
268 QMenu
*popupMenu
= new QMenu(this);
271 popupMenu
->setAttribute(Qt::WA_DeleteOnClose
);
272 popupMenu
->addMenu(ui
->menuSelect
);
273 action
= popupMenu
->addAction(tr("Display time as time of day"), this, SLOT(switchTimeOfDay()));
274 action
->setCheckable(true);
275 action
->setChecked(call_infos_model_
->timeOfDay());
276 action
->setEnabled(!file_closed_
);
277 popupMenu
->addSeparator();
278 action
= popupMenu
->addAction(tr("Copy as CSV"), this, SLOT(copyAsCSV()));
279 action
->setToolTip(tr("Copy stream list as CSV."));
280 action
= popupMenu
->addAction(tr("Copy as YAML"), this, SLOT(copyAsYAML()));
281 action
->setToolTip(tr("Copy stream list as YAML."));
282 popupMenu
->addSeparator();
283 popupMenu
->addAction(ui
->actionSelectRtpStreams
);
284 popupMenu
->addAction(ui
->actionDeselectRtpStreams
);
286 popupMenu
->popup(event
->globalPos());
289 void VoipCallsDialog::changeEvent(QEvent
*event
)
293 switch (event
->type())
295 case QEvent::LanguageChange
:
296 ui
->retranslateUi(this);
302 QDialog::changeEvent(event
);
305 void VoipCallsDialog::captureEvent(CaptureEvent e
)
307 if (e
.captureContext() == CaptureEvent::Retap
)
309 switch (e
.eventType())
311 case CaptureEvent::Started
:
312 ui
->displayFilterCheckBox
->setEnabled(false);
314 case CaptureEvent::Finished
:
315 ui
->displayFilterCheckBox
->setEnabled(true);
324 void VoipCallsDialog::tapReset(void *tapinfo_ptr
)
326 voip_calls_tapinfo_t
*tapinfo
= static_cast<voip_calls_tapinfo_t
*>(tapinfo_ptr
);
327 VoipCallsDialog
*voip_calls_dialog
= static_cast<VoipCallsDialog
*>(tapinfo
->tap_data
);
329 // Create new callsinfos queue in tapinfo. Current callsinfos are
330 // in shown_callsinfos_, so don't free the [shared] data stored in
331 // the queue, but do free the queue itself. (Do this before calling
332 // voip_calls_reset_all_taps(), as that frees the data in the queue.)
333 g_queue_free(voip_calls_dialog
->tapinfo_
.callsinfos
);
334 voip_calls_dialog
->tapinfo_
.callsinfos
= g_queue_new();
335 voip_calls_reset_all_taps(tapinfo
);
337 // Leave old graph_analysis as is and allocate new one
338 voip_calls_dialog
->sequence_info_
->unref();
339 voip_calls_dialog
->tapinfo_
.graph_analysis
= sequence_analysis_info_new();
340 voip_calls_dialog
->tapinfo_
.graph_analysis
->name
= "voip";
341 voip_calls_dialog
->sequence_info_
= new SequenceInfo(voip_calls_dialog
->tapinfo_
.graph_analysis
);
344 tap_packet_status
VoipCallsDialog::tapPacket(void *, packet_info
*, epan_dissect_t
*, const void *, tap_flags_t
)
346 #ifdef QT_MULTIMEDIA_LIB
347 // voip_calls_tapinfo_t *tapinfo = (voip_calls_tapinfo_t *) tapinfo_ptr;
348 // add_rtp_packet for voip player.
349 // return TAP_PACKET_REDRAW;
351 return TAP_PACKET_DONT_REDRAW
;
354 void VoipCallsDialog::tapDraw(void *tapinfo_ptr
)
356 voip_calls_tapinfo_t
*tapinfo
= static_cast<voip_calls_tapinfo_t
*>(tapinfo_ptr
);
358 if (!tapinfo
|| !tapinfo
->redraw
) {
362 GList
*graph_item
= g_queue_peek_nth_link(tapinfo
->graph_analysis
->items
, 0);
363 for (; graph_item
; graph_item
= gxx_list_next(graph_item
)) {
364 for (GList
*rsi_entry
= g_list_first(tapinfo
->rtpstream_list
); rsi_entry
; rsi_entry
= gxx_list_next(rsi_entry
)) {
365 seq_analysis_item_t
* sai
= gxx_list_data(seq_analysis_item_t
*, graph_item
);
366 rtpstream_info_t
*rsi
= gxx_list_data(rtpstream_info_t
*, rsi_entry
);
368 if (rsi
->start_fd
->num
== sai
->frame_number
) {
369 rsi
->call_num
= sai
->conv_num
;
370 // VOIP_CALLS_DEBUG("setting conv num %u for frame %u", sai->conv_num, sai->frame_number);
375 VoipCallsDialog
*voip_calls_dialog
= static_cast<VoipCallsDialog
*>(tapinfo
->tap_data
);
376 if (voip_calls_dialog
) {
377 voip_calls_dialog
->updateCalls();
381 int VoipCallsDialog::compareCallNums(const void *a
, const void *b
)
383 const voip_calls_info_t
*call_a
= (const voip_calls_info_t
*)a
;
384 const voip_calls_info_t
*call_b
= (const voip_calls_info_t
*)b
;
386 return (call_a
->call_num
!= call_b
->call_num
);
389 void VoipCallsDialog::updateCalls()
391 voip_calls_info_t
*new_callsinfo
;
392 voip_calls_info_t
*old_callsinfo
;
395 ui
->callTreeView
->setSortingEnabled(false);
397 // Merge new callsinfos with old ones
398 // It keeps list of calls visible including selected items
399 GList
*list
= g_queue_peek_nth_link(tapinfo_
.callsinfos
, 0);
401 // Find new callsinfo
402 new_callsinfo
= gxx_list_data(voip_calls_info_t
*, list
);
403 found
= g_queue_find_custom(shown_callsinfos_
, new_callsinfo
, VoipCallsDialog::compareCallNums
);
405 // New call, add it to list for show
406 g_queue_push_tail(shown_callsinfos_
, new_callsinfo
);
409 old_callsinfo
= (voip_calls_info_t
*)found
->data
;
410 if (new_callsinfo
!= old_callsinfo
) {
412 voip_calls_free_callsinfo(old_callsinfo
);
413 found
->data
= new_callsinfo
;
417 list
= gxx_list_next(list
);
421 call_infos_model_
->updateCalls(shown_callsinfos_
);
424 for (int i
= 0; i
< call_infos_model_
->columnCount(); i
++) {
425 ui
->callTreeView
->resizeColumnToContents(i
);
428 ui
->callTreeView
->setSortingEnabled(true);
433 void VoipCallsDialog::updateWidgets()
435 bool selected
= ui
->callTreeView
->selectionModel()->hasSelection();
436 bool have_ga_items
= false;
438 if (tapinfo_
.graph_analysis
&& tapinfo_
.graph_analysis
->items
) {
439 have_ga_items
= true;
442 bool enable
= selected
&& have_ga_items
&& !file_closed_
;
444 prepare_button_
->setEnabled(enable
);
445 sequence_button_
->setEnabled(enable
);
446 ui
->actionSelectRtpStreams
->setEnabled(enable
);
447 ui
->actionDeselectRtpStreams
->setEnabled(enable
);
448 #if defined(QT_MULTIMEDIA_LIB)
449 player_button_
->setEnabled(enable
);
452 WiresharkDialog::updateWidgets();
455 void VoipCallsDialog::prepareFilter()
457 if (!ui
->callTreeView
->selectionModel()->hasSelection() || !tapinfo_
.graph_analysis
) {
462 QSet
<uint16_t> selected_calls
;
463 QString frame_numbers
;
466 /* Build a new filter based on frame numbers */
467 foreach (QModelIndex index
, ui
->callTreeView
->selectionModel()->selectedIndexes()) {
468 if (index
.isValid() && ! rows
.contains(index
.row()))
470 voip_calls_info_t
*call_info
= VoipCallsInfoModel::indexToCallInfo(index
);
475 selected_calls
<< call_info
->call_num
;
480 GList
*cur_ga_item
= g_queue_peek_nth_link(tapinfo_
.graph_analysis
->items
, 0);
481 while (cur_ga_item
&& cur_ga_item
->data
) {
482 seq_analysis_item_t
*ga_item
= gxx_list_data(seq_analysis_item_t
*, cur_ga_item
);
483 if (selected_calls
.contains(ga_item
->conv_num
)) {
484 frame_numbers
+= QStringLiteral("%1,").arg(ga_item
->frame_number
);
486 cur_ga_item
= gxx_list_next(cur_ga_item
);
489 if (!frame_numbers
.isEmpty()) {
490 frame_numbers
.chop(1);
491 filter_str
= QStringLiteral("frame.number in {%1} or rtp.setup-frame in {%1}").arg(frame_numbers
);
495 // XXX The GTK+ UI falls back to building a filter based on protocols if the filter
496 // length is too long. Leaving this here for the time being in case we need to do
497 // the same in the Qt UI.
498 const sip_calls_info_t
*sipinfo
;
499 const isup_calls_info_t
*isupinfo
;
500 const h323_calls_info_t
*h323info
;
501 const h245_address_t
*h245_add
= NULL
;
502 const gcp_ctx_t
* ctx
;
505 if (filter_length
< max_filter_length
) {
506 gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget
), filter_string_fwd
->str
, -1, &pos
);
508 g_string_free(filter_string_fwd
, TRUE
);
509 filter_string_fwd
= g_string_new(filter_prepend
);
511 g_string_append_printf(filter_string_fwd
, "(");
513 /* Build a new filter based on protocol fields */
514 lista
= g_queue_peek_nth_link(voip_calls_get_info()->callsinfos
, 0);
516 listinfo
= gxx_list_data(voip_calls_info_t
*, lista
);
517 if (listinfo
->selected
) {
519 g_string_append_printf(filter_string_fwd
, " or ");
520 switch (listinfo
->protocol
) {
522 sipinfo
= (sip_calls_info_t
*)listinfo
->prot_info
;
523 g_string_append_printf(filter_string_fwd
,
524 "(sip.Call-ID == \"%s\")",
525 sipinfo
->call_identifier
529 isupinfo
= (isup_calls_info_t
*)listinfo
->prot_info
;
530 g_string_append_printf(filter_string_fwd
,
531 "(isup.cic == %i and frame.number >= %i and frame.number <= %i and mtp3.network_indicator == %i and ((mtp3.dpc == %i) and (mtp3.opc == %i)) or ((mtp3.dpc == %i) and (mtp3.opc == %i)))",
532 isupinfo
->cic
, listinfo
->start_fd
->num
,
533 listinfo
->stop_fd
->num
,
534 isupinfo
->ni
, isupinfo
->dpc
, isupinfo
->opc
,
535 isupinfo
->opc
, isupinfo
->dpc
540 h323info
= (h323_calls_info_t
*)listinfo
->prot_info
;
541 guid_str
= guid_to_str(NULL
, &h323info
->guid
[0]);
542 g_string_append_printf(filter_string_fwd
,
543 "((h225.guid == %s || q931.call_ref == %x:%x || q931.call_ref == %x:%x)",
545 (uint8_t) (h323info
->q931_crv
& 0x00ff),
546 (uint8_t)((h323info
->q931_crv
& 0xff00)>>8),
547 (uint8_t) (h323info
->q931_crv2
& 0x00ff),
548 (uint8_t)((h323info
->q931_crv2
& 0xff00)>>8));
549 listb
= g_list_first(h323info
->h245_list
);
550 wmem_free(NULL
, guid_str
);
552 h245_add
= gxx_list_data(h245_address_t
*, listb
);
553 g_string_append_printf(filter_string_fwd
,
554 " || (ip.addr == %s && tcp.port == %d && h245)",
555 address_to_qstring(&h245_add
->h245_address
), h245_add
->h245_port
);
556 listb
= gxx_list_next(listb
);
558 g_string_append_printf(filter_string_fwd
, ")");
562 ctx
= (gcp_ctx_t
*)listinfo
->prot_info
;
563 g_string_append_printf(filter_string_fwd
,
564 "(h248.ctx == 0x%x)", ctx
->id
);
567 /* placeholder to assure valid display filter expression */
568 g_string_append_printf(filter_string_fwd
,
574 lista
= gxx_list_next(lista
);
577 g_string_append_printf(filter_string_fwd
, ")");
578 gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget
), filter_string_fwd
->str
, -1, &pos
);
582 emit
updateFilter(filter_str
);
585 void VoipCallsDialog::showSequence()
587 if (file_closed_
) return;
589 QSet
<uint16_t> selected_calls
;
590 foreach (QModelIndex index
, ui
->callTreeView
->selectionModel()->selectedIndexes()) {
591 voip_calls_info_t
*call_info
= VoipCallsInfoModel::indexToCallInfo(index
);
595 selected_calls
<< call_info
->call_num
;
598 sequence_analysis_list_sort(tapinfo_
.graph_analysis
);
599 GList
*cur_ga_item
= g_queue_peek_nth_link(tapinfo_
.graph_analysis
->items
, 0);
600 while (cur_ga_item
&& cur_ga_item
->data
) {
601 seq_analysis_item_t
*ga_item
= gxx_list_data(seq_analysis_item_t
*, cur_ga_item
);
602 ga_item
->display
= selected_calls
.contains(ga_item
->conv_num
);
603 cur_ga_item
= gxx_list_next(cur_ga_item
);
606 SequenceDialog
*sequence_dialog
= new SequenceDialog(parent_
, cap_file_
, sequence_info_
, true);
607 // Bypass this dialog and forward signals to parent
608 connect(sequence_dialog
, SIGNAL(rtpStreamsDialogSelectRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpStreamsDialogSelectRtpStreams(QVector
<rtpstream_id_t
*>)));
609 connect(sequence_dialog
, SIGNAL(rtpStreamsDialogDeselectRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpStreamsDialogDeselectRtpStreams(QVector
<rtpstream_id_t
*>)));
610 connect(sequence_dialog
, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector
<rtpstream_id_t
*>)));
611 connect(sequence_dialog
, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpPlayerDialogAddRtpStreams(QVector
<rtpstream_id_t
*>)));
612 connect(sequence_dialog
, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector
<rtpstream_id_t
*>)), &parent_
, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector
<rtpstream_id_t
*>)));
614 sequence_dialog
->setAttribute(Qt::WA_DeleteOnClose
);
615 sequence_dialog
->show();
618 QVector
<rtpstream_id_t
*>VoipCallsDialog::getSelectedRtpIds()
620 QVector
<rtpstream_id_t
*> stream_ids
;
621 foreach (QModelIndex index
, ui
->callTreeView
->selectionModel()->selectedIndexes()) {
622 voip_calls_info_t
*vci
= VoipCallsInfoModel::indexToCallInfo(index
);
625 for (GList
*rsi_entry
= g_list_first(tapinfo_
.rtpstream_list
); rsi_entry
; rsi_entry
= gxx_list_next(rsi_entry
)) {
626 rtpstream_info_t
*rsi
= gxx_list_data(rtpstream_info_t
*, rsi_entry
);
629 //VOIP_CALLS_DEBUG("checking call %u, start frame %u == stream call %u, start frame %u, setup frame %u",
630 // vci->call_num, vci->start_fd->num,
631 // rsi->call_num, rsi->start_fd->num, rsi->setup_frame_number);
632 if (vci
->call_num
== static_cast<unsigned>(rsi
->call_num
)) {
633 //VOIP_CALLS_DEBUG("adding call number %u", vci->call_num);
634 if (-1 == stream_ids
.indexOf(&(rsi
->id
))) {
635 // Add only new stream
636 stream_ids
<< &(rsi
->id
);
645 void VoipCallsDialog::rtpPlayerReplace()
647 if (ui
->callTreeView
->selectionModel()->selectedIndexes().count() < 1) return;
649 emit
rtpPlayerDialogReplaceRtpStreams(getSelectedRtpIds());
652 void VoipCallsDialog::rtpPlayerAdd()
654 if (ui
->callTreeView
->selectionModel()->selectedIndexes().count() < 1) return;
656 emit
rtpPlayerDialogAddRtpStreams(getSelectedRtpIds());
659 void VoipCallsDialog::rtpPlayerRemove()
661 if (ui
->callTreeView
->selectionModel()->selectedIndexes().count() < 1) return;
663 emit
rtpPlayerDialogRemoveRtpStreams(getSelectedRtpIds());
666 QList
<QVariant
> VoipCallsDialog::streamRowData(int row
) const
668 QList
<QVariant
> row_data
;
670 if (row
>= sorted_model_
->rowCount()) {
674 for (int col
= 0; col
< sorted_model_
->columnCount(); col
++) {
676 row_data
<< sorted_model_
->headerData(col
, Qt::Horizontal
);
678 row_data
<< sorted_model_
->index(row
, col
).data();
684 void VoipCallsDialog::on_callTreeView_activated(const QModelIndex
&index
)
686 voip_calls_info_t
*call_info
= VoipCallsInfoModel::indexToCallInfo(index
);
690 emit
goToPacket(call_info
->start_fd
->num
);
693 void VoipCallsDialog::selectAll()
695 ui
->callTreeView
->selectAll();
698 void VoipCallsDialog::selectNone()
700 ui
->callTreeView
->clearSelection();
703 void VoipCallsDialog::copyAsCSV()
706 QTextStream
stream(&csv
, QIODevice::Text
);
707 for (int row
= -1; row
< sorted_model_
->rowCount(); row
++) {
709 foreach (QVariant v
, streamRowData(row
)) {
710 QString strval
= v
.toString();
711 // XXX should quotes (") in strval be stripped/sanitized?
712 rdsl
<< QStringLiteral("\"%1\"").arg(strval
);
714 stream
<< rdsl
.join(",") << '\n';
716 mainApp
->clipboard()->setText(stream
.readAll());
719 void VoipCallsDialog::copyAsYAML()
722 QTextStream
stream(&yaml
, QIODevice::Text
);
723 stream
<< "---" << '\n';
724 for (int row
= -1; row
< sorted_model_
->rowCount(); row
++) {
725 stream
<< "-" << '\n';
726 foreach (QVariant v
, streamRowData(row
)) {
727 stream
<< " - " << v
.toString() << '\n';
730 mainApp
->clipboard()->setText(stream
.readAll());
733 void VoipCallsDialog::on_buttonBox_clicked(QAbstractButton
*button
)
735 if (button
== prepare_button_
) {
737 } else if (button
== sequence_button_
) {
742 void VoipCallsDialog::removeAllCalls()
744 voip_calls_info_t
*callsinfo
;
747 call_infos_model_
->removeAllCalls();
749 /* Free shown callsinfos */
750 list
= g_queue_peek_nth_link(shown_callsinfos_
, 0);
753 callsinfo
= (voip_calls_info_t
*)list
->data
;
754 voip_calls_free_callsinfo(callsinfo
);
755 list
= g_list_next(list
);
757 g_queue_clear(shown_callsinfos_
);
760 void VoipCallsDialog::displayFilterCheckBoxToggled(bool checked
)
762 if (!cap_file_
.isValid()) {
766 tapinfo_
.apply_display_filter
= checked
;
769 cap_file_
.retapPackets();
772 void VoipCallsDialog::on_buttonBox_helpRequested()
774 mainApp
->helpTopicAction(HELP_TELEPHONY_VOIP_CALLS_DIALOG
);
777 void VoipCallsDialog::switchTimeOfDay()
779 bool checked
= ! call_infos_model_
->timeOfDay();
781 ui
->todCheckBox
->setChecked(checked
);
782 call_infos_model_
->setTimeOfDay(checked
);
783 ui
->callTreeView
->resizeColumnToContents(VoipCallsInfoModel::StartTime
);
784 ui
->callTreeView
->resizeColumnToContents(VoipCallsInfoModel::StopTime
);
787 void VoipCallsDialog::displayFilterSuccess(bool success
)
789 if (success
&& ui
->displayFilterCheckBox
->isChecked()) {
791 cap_file_
.retapPackets();
795 void VoipCallsDialog::invertSelection()
797 QModelIndex rootIndex
= ui
->callTreeView
->rootIndex();
798 QModelIndex first
= sorted_model_
->index(0, 0, QModelIndex());
799 int numOfItems
= sorted_model_
->rowCount(rootIndex
);
800 int numOfCols
= sorted_model_
->columnCount(rootIndex
);
801 QModelIndex last
= sorted_model_
->index(numOfItems
- 1, numOfCols
- 1, QModelIndex());
803 QItemSelection
selection(first
, last
);
804 ui
->callTreeView
->selectionModel()->select(selection
, QItemSelectionModel::Toggle
);
807 void VoipCallsDialog::on_actionSelectAll_triggered()
809 ui
->callTreeView
->selectAll();
812 void VoipCallsDialog::on_actionSelectInvert_triggered()
817 void VoipCallsDialog::on_actionSelectNone_triggered()
819 ui
->callTreeView
->clearSelection();
822 void VoipCallsDialog::on_actionSelectRtpStreams_triggered()
824 QVector
<rtpstream_id_t
*>stream_ids
= qvector_rtpstream_ids_copy(getSelectedRtpIds());
826 emit
rtpStreamsDialogSelectRtpStreams(stream_ids
);
828 qvector_rtpstream_ids_free(stream_ids
);
832 void VoipCallsDialog::on_actionDeselectRtpStreams_triggered()
834 QVector
<rtpstream_id_t
*>stream_ids
= qvector_rtpstream_ids_copy(getSelectedRtpIds());
836 emit
rtpStreamsDialogDeselectRtpStreams(stream_ids
);
838 qvector_rtpstream_ids_free(stream_ids
);