2014-11-19 00:21:42 +00:00
/* voip_calls_dialog.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs < gerald @ wireshark . org >
* Copyright 1998 Gerald Combs
*
2018-04-30 07:47:58 +00:00
* SPDX - License - Identifier : GPL - 2.0 - or - later
*/
2014-11-19 00:21:42 +00:00
# include "voip_calls_dialog.h"
2015-06-25 16:17:03 +00:00
# include <ui_voip_calls_dialog.h>
2014-11-19 00:21:42 +00:00
# include "file.h"
# include "epan/addr_resolv.h"
# include "epan/dissectors/packet-h225.h"
2014-12-13 00:51:40 +00:00
# include "ui/rtp_stream.h"
2020-12-30 08:51:00 +00:00
# include "ui/rtp_stream_id.h"
2014-11-19 00:21:42 +00:00
2017-07-25 14:15:18 +00:00
# include <ui/qt/utils/qt_ui_utils.h>
2014-12-13 00:51:40 +00:00
# include "rtp_player_dialog.h"
2014-11-19 00:21:42 +00:00
# include "sequence_dialog.h"
2017-07-25 14:15:18 +00:00
# include <ui/qt/utils/stock_icon.h>
2022-02-01 03:30:09 +00:00
# include "main_application.h"
2017-07-25 14:47:16 +00:00
# include <ui/qt/models/voip_calls_info_model.h>
2014-11-19 00:21:42 +00:00
2016-06-17 10:39:09 +00:00
# include <QClipboard>
2014-11-19 00:21:42 +00:00
# include <QContextMenuEvent>
2021-05-01 22:33:54 +00:00
# include <QToolButton>
2014-11-19 00:21:42 +00:00
// To do:
// - More context menu items
// - Don't select on right click
// - Player
// - Add a screenshot to the user's guide
2017-02-13 00:36:58 +00:00
// - Add filter for quickly searching through list?
2014-11-19 00:21:42 +00:00
// Bugs:
// - Preparing a filter overwrites the existing filter. The GTK+ UI appends.
// We'll probably have to add an "append" parameter to MainWindow::filterPackets.
2014-12-13 00:51:40 +00:00
enum { voip_calls_type_ = 1000 } ;
2021-05-09 14:29:11 +00:00
VoipCallsDialog * VoipCallsDialog : : pinstance_voip_ { nullptr } ;
VoipCallsDialog * VoipCallsDialog : : pinstance_sip_ { nullptr } ;
2022-04-04 19:21:26 +00:00
std : : mutex VoipCallsDialog : : init_mutex_ ;
2021-05-09 14:29:11 +00:00
VoipCallsDialog * VoipCallsDialog : : openVoipCallsDialogVoip ( QWidget & parent , CaptureFile & cf , QObject * packet_list )
{
2022-04-04 19:21:26 +00:00
std : : lock_guard < std : : mutex > lock ( init_mutex_ ) ;
2021-05-09 14:29:11 +00:00
if ( pinstance_voip_ = = nullptr )
{
pinstance_voip_ = new VoipCallsDialog ( parent , cf , false ) ;
connect ( pinstance_voip_ , SIGNAL ( goToPacket ( int ) ) ,
packet_list , SLOT ( goToPacket ( int ) ) ) ;
}
return pinstance_voip_ ;
}
VoipCallsDialog * VoipCallsDialog : : openVoipCallsDialogSip ( QWidget & parent , CaptureFile & cf , QObject * packet_list )
{
2022-04-04 19:21:26 +00:00
std : : lock_guard < std : : mutex > lock ( init_mutex_ ) ;
2021-05-09 14:29:11 +00:00
if ( pinstance_sip_ = = nullptr )
{
pinstance_sip_ = new VoipCallsDialog ( parent , cf , true ) ;
connect ( pinstance_sip_ , SIGNAL ( goToPacket ( int ) ) ,
packet_list , SLOT ( goToPacket ( int ) ) ) ;
}
return pinstance_sip_ ;
}
2014-12-29 19:42:01 +00:00
VoipCallsDialog : : VoipCallsDialog ( QWidget & parent , CaptureFile & cf , bool all_flows ) :
WiresharkDialog ( parent , cf ) ,
2021-05-09 14:29:11 +00:00
all_flows_ ( all_flows ) ,
2015-02-14 00:18:57 +00:00
ui ( new Ui : : VoipCallsDialog ) ,
2020-01-05 22:33:04 +00:00
parent_ ( parent ) ,
voip_calls_tap_listeners_removed_ ( false )
2014-11-19 00:21:42 +00:00
{
ui - > setupUi ( this ) ;
2016-02-28 18:23:20 +00:00
loadGeometry ( parent . width ( ) * 4 / 5 , parent . height ( ) * 2 / 3 ) ;
2021-03-13 20:28:06 +00:00
ui - > callTreeView - > installEventFilter ( this ) ;
2016-02-28 18:23:20 +00:00
2017-02-13 00:36:58 +00:00
// Create the model that stores the actual data and the proxy model that is
// responsible for sorting and filtering data in the display.
call_infos_model_ = new VoipCallsInfoModel ( this ) ;
2017-03-16 23:51:08 +00:00
cache_model_ = new CacheProxyModel ( this ) ;
cache_model_ - > setSourceModel ( call_infos_model_ ) ;
2017-02-13 00:36:58 +00:00
sorted_model_ = new VoipCallsInfoSortedModel ( this ) ;
2017-03-16 23:51:08 +00:00
sorted_model_ - > setSourceModel ( cache_model_ ) ;
2017-02-13 00:36:58 +00:00
ui - > callTreeView - > setModel ( sorted_model_ ) ;
connect ( ui - > callTreeView - > selectionModel ( ) , SIGNAL ( selectionChanged ( QItemSelection , QItemSelection ) ) ,
this , SLOT ( updateWidgets ( ) ) ) ;
ui - > callTreeView - > sortByColumn ( VoipCallsInfoModel : : StartTime , Qt : : AscendingOrder ) ;
2021-05-09 14:29:11 +00:00
setWindowSubtitle ( all_flows_ ? tr ( " SIP Flows " ) : tr ( " VoIP Calls " ) ) ;
2014-11-19 00:21:42 +00:00
2021-03-23 10:37:59 +00:00
sequence_button_ = ui - > buttonBox - > addButton ( ui - > actionFlowSequence - > text ( ) , QDialogButtonBox : : ActionRole ) ;
sequence_button_ - > setToolTip ( ui - > actionFlowSequence - > toolTip ( ) ) ;
prepare_button_ = ui - > buttonBox - > addButton ( ui - > actionPrepareFilter - > text ( ) , QDialogButtonBox : : ActionRole ) ;
prepare_button_ - > setToolTip ( ui - > actionPrepareFilter - > toolTip ( ) ) ;
2021-03-27 20:01:43 +00:00
player_button_ = RtpPlayerDialog : : addPlayerButton ( ui - > buttonBox , this ) ;
2014-11-19 00:21:42 +00:00
2019-11-17 19:02:20 +00:00
connect ( ui - > todCheckBox , & QAbstractButton : : toggled , this , & VoipCallsDialog : : switchTimeOfDay ) ;
2019-08-02 18:39:13 +00:00
2021-03-23 10:37:59 +00:00
copy_button_ = ui - > buttonBox - > addButton ( ui - > actionCopyButton - > text ( ) , QDialogButtonBox : : ActionRole ) ;
copy_button_ - > setToolTip ( ui - > actionCopyButton - > toolTip ( ) ) ;
2017-09-17 21:48:12 +00:00
QMenu * copy_menu = new QMenu ( copy_button_ ) ;
2016-06-16 21:09:28 +00:00
QAction * ca ;
ca = copy_menu - > addAction ( tr ( " as CSV " ) ) ;
2019-08-02 18:39:13 +00:00
connect ( ca , SIGNAL ( triggered ( ) ) , this , SLOT ( copyAsCSV ( ) ) ) ;
2016-06-16 21:09:28 +00:00
ca = copy_menu - > addAction ( tr ( " as YAML " ) ) ;
2019-08-02 18:39:13 +00:00
connect ( ca , SIGNAL ( triggered ( ) ) , this , SLOT ( copyAsYAML ( ) ) ) ;
2016-06-16 21:09:28 +00:00
copy_button_ - > setMenu ( copy_menu ) ;
2021-01-01 15:51:47 +00:00
connect ( & cap_file_ , SIGNAL ( captureEvent ( CaptureEvent ) ) ,
this , SLOT ( captureEvent ( CaptureEvent ) ) ) ;
2016-06-16 21:09:28 +00:00
2021-04-19 13:54:58 +00:00
connect ( this , SIGNAL ( rtpStreamsDialogSelectRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpStreamsDialogSelectRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( this , SIGNAL ( rtpStreamsDialogDeselectRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpStreamsDialogDeselectRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
2014-11-19 00:21:42 +00:00
memset ( & tapinfo_ , 0 , sizeof ( tapinfo_ ) ) ;
tapinfo_ . tap_packet = tapPacket ;
2020-12-13 13:04:11 +00:00
tapinfo_ . tap_reset = tapReset ;
2014-11-19 00:21:42 +00:00
tapinfo_ . tap_draw = tapDraw ;
tapinfo_ . tap_data = this ;
tapinfo_ . callsinfos = g_queue_new ( ) ;
tapinfo_ . h225_cstype = H225_OTHER ;
2021-05-09 14:29:11 +00:00
tapinfo_ . fs_option = all_flows_ ? FLOW_ALL : FLOW_ONLY_INVITES ; /* flow show option */
2014-11-19 00:21:42 +00:00
tapinfo_ . graph_analysis = sequence_analysis_info_new ( ) ;
2017-09-16 14:52:23 +00:00
tapinfo_ . graph_analysis - > name = " voip " ;
2015-11-15 21:57:43 +00:00
sequence_info_ = new SequenceInfo ( tapinfo_ . graph_analysis ) ;
2020-12-31 00:00:57 +00:00
shown_callsinfos_ = g_queue_new ( ) ;
2014-11-19 00:21:42 +00:00
voip_calls_init_all_taps ( & tapinfo_ ) ;
2021-01-03 07:22:33 +00:00
if ( cap_file_ . isValid ( ) & & cap_file_ . capFile ( ) - > dfilter ) {
2021-01-01 15:51:47 +00:00
// Activate display filter checking
tapinfo_ . apply_display_filter = true ;
ui - > displayFilterCheckBox - > setChecked ( true ) ;
}
2014-11-19 00:21:42 +00:00
2021-05-09 14:29:11 +00:00
connect ( this , SIGNAL ( updateFilter ( QString , bool ) ) ,
& parent , SLOT ( filterPackets ( QString , bool ) ) ) ;
connect ( & parent , SIGNAL ( displayFilterSuccess ( bool ) ) ,
this , SLOT ( displayFilterSuccess ( bool ) ) ) ;
connect ( this , SIGNAL ( rtpPlayerDialogReplaceRtpStreams ( QVector < rtpstream_id_t * > ) ) ,
& parent , SLOT ( rtpPlayerDialogReplaceRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( this , SIGNAL ( rtpPlayerDialogAddRtpStreams ( QVector < rtpstream_id_t * > ) ) ,
& parent , SLOT ( rtpPlayerDialogAddRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( this , SIGNAL ( rtpPlayerDialogRemoveRtpStreams ( QVector < rtpstream_id_t * > ) ) ,
& parent , SLOT ( rtpPlayerDialogRemoveRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
2014-11-19 00:21:42 +00:00
updateWidgets ( ) ;
2015-09-04 16:23:51 +00:00
if ( cap_file_ . isValid ( ) ) {
tapinfo_ . session = cap_file_ . capFile ( ) - > epan ;
cap_file_ . delayedRetapPackets ( ) ;
}
2014-11-19 00:21:42 +00:00
}
2021-03-13 20:28:06 +00:00
bool VoipCallsDialog : : eventFilter ( QObject * , QEvent * event )
{
if ( ui - > callTreeView - > hasFocus ( ) & & event - > type ( ) = = QEvent : : KeyPress ) {
QKeyEvent & keyEvent = static_cast < QKeyEvent & > ( * event ) ;
switch ( keyEvent . key ( ) ) {
case Qt : : Key_I :
if ( keyEvent . modifiers ( ) = = Qt : : ControlModifier ) {
// Ctrl+I
on_actionSelectInvert_triggered ( ) ;
return true ;
}
break ;
case Qt : : Key_A :
if ( keyEvent . modifiers ( ) = = Qt : : ControlModifier ) {
// Ctrl+A
on_actionSelectAll_triggered ( ) ;
return true ;
} else if ( keyEvent . modifiers ( ) = = ( Qt : : ShiftModifier | Qt : : ControlModifier ) ) {
// Ctrl+Shift+A
on_actionSelectNone_triggered ( ) ;
return true ;
}
break ;
2021-04-19 13:54:58 +00:00
case Qt : : Key_S :
on_actionSelectRtpStreams_triggered ( ) ;
break ;
case Qt : : Key_D :
on_actionDeselectRtpStreams_triggered ( ) ;
break ;
2021-03-13 20:28:06 +00:00
default :
break ;
}
}
return false ;
}
2014-11-19 00:21:42 +00:00
VoipCallsDialog : : ~ VoipCallsDialog ( )
{
2022-04-04 19:21:26 +00:00
std : : lock_guard < std : : mutex > lock ( init_mutex_ ) ;
if ( ( all_flows_ & & ( pinstance_sip_ ! = nullptr ) )
| | ( ! all_flows_ & & ( pinstance_voip_ ! = nullptr ) )
) {
delete ui ;
voip_calls_reset_all_taps ( & tapinfo_ ) ;
if ( ! voip_calls_tap_listeners_removed_ ) {
voip_calls_remove_all_tap_listeners ( & tapinfo_ ) ;
voip_calls_tap_listeners_removed_ = true ;
}
sequence_info_ - > unref ( ) ;
g_queue_free ( tapinfo_ . callsinfos ) ;
// We don't need to clear shown_callsinfos_ data, it was shared
// with tapinfo_.callsinfos and was cleared
// during voip_calls_reset_all_taps
g_queue_free ( shown_callsinfos_ ) ;
if ( all_flows_ ) {
pinstance_sip_ = nullptr ;
} else {
pinstance_voip_ = nullptr ;
}
2021-05-09 14:29:11 +00:00
}
2014-11-19 00:21:42 +00:00
}
2018-01-14 20:37:16 +00:00
void VoipCallsDialog : : removeTapListeners ( )
2015-12-06 21:29:53 +00:00
{
2020-01-05 22:33:04 +00:00
if ( ! voip_calls_tap_listeners_removed_ ) {
voip_calls_remove_all_tap_listeners ( & tapinfo_ ) ;
voip_calls_tap_listeners_removed_ = true ;
}
2018-01-14 20:37:16 +00:00
WiresharkDialog : : removeTapListeners ( ) ;
2015-12-06 21:29:53 +00:00
}
2014-12-29 19:42:01 +00:00
void VoipCallsDialog : : captureFileClosing ( )
2014-11-19 00:21:42 +00:00
{
2017-03-16 23:51:08 +00:00
// The time formatting is currently provided by VoipCallsInfoModel, but when
// the cache is active, the ToD cannot be modified.
cache_model_ - > setSourceModel ( NULL ) ;
2020-01-05 22:33:04 +00:00
if ( ! voip_calls_tap_listeners_removed_ ) {
voip_calls_remove_all_tap_listeners ( & tapinfo_ ) ;
voip_calls_tap_listeners_removed_ = true ;
}
2014-12-29 19:42:01 +00:00
tapinfo_ . session = NULL ;
2021-01-06 22:49:36 +00:00
2014-12-29 19:42:01 +00:00
WiresharkDialog : : captureFileClosing ( ) ;
2014-11-19 00:21:42 +00:00
}
2021-01-10 20:57:47 +00:00
void VoipCallsDialog : : captureFileClosed ( )
{
// The time formatting is currently provided by VoipCallsInfoModel, but when
// the cache is active, the ToD cannot be modified.
ui - > todCheckBox - > setEnabled ( false ) ;
ui - > displayFilterCheckBox - > setEnabled ( false ) ;
WiresharkDialog : : captureFileClosed ( ) ;
}
2014-11-19 00:21:42 +00:00
void VoipCallsDialog : : contextMenuEvent ( QContextMenuEvent * event )
{
2019-08-02 18:39:13 +00:00
bool selected = ui - > callTreeView - > selectionModel ( ) - > hasSelection ( ) ;
2019-11-17 19:02:20 +00:00
if ( ! selected )
2019-08-02 18:39:13 +00:00
return ;
QMenu popupMenu ;
2021-01-06 22:49:36 +00:00
QAction * action ;
2019-08-02 18:39:13 +00:00
2021-04-14 13:47:07 +00:00
popupMenu . addMenu ( ui - > menuSelect ) ;
2019-08-02 18:39:13 +00:00
action = popupMenu . addAction ( tr ( " Display time as time of day " ) , this , SLOT ( switchTimeOfDay ( ) ) ) ;
action - > setCheckable ( true ) ;
action - > setChecked ( call_infos_model_ - > timeOfDay ( ) ) ;
2021-01-06 22:49:36 +00:00
action - > setEnabled ( ! file_closed_ ) ;
2019-08-02 18:39:13 +00:00
popupMenu . addSeparator ( ) ;
action = popupMenu . addAction ( tr ( " Copy as CSV " ) , this , SLOT ( copyAsCSV ( ) ) ) ;
action - > setToolTip ( tr ( " Copy stream list as CSV. " ) ) ;
action = popupMenu . addAction ( tr ( " Copy as YAML " ) , this , SLOT ( copyAsYAML ( ) ) ) ;
action - > setToolTip ( tr ( " Copy stream list as YAML. " ) ) ;
2021-04-19 13:54:58 +00:00
popupMenu . addSeparator ( ) ;
popupMenu . addAction ( ui - > actionSelectRtpStreams ) ;
popupMenu . addAction ( ui - > actionDeselectRtpStreams ) ;
2019-08-02 18:39:13 +00:00
popupMenu . exec ( event - > globalPos ( ) ) ;
2014-11-19 00:21:42 +00:00
}
void VoipCallsDialog : : changeEvent ( QEvent * event )
{
if ( 0 ! = event )
{
switch ( event - > type ( ) )
{
case QEvent : : LanguageChange :
ui - > retranslateUi ( this ) ;
break ;
default :
break ;
}
}
QDialog : : changeEvent ( event ) ;
}
2021-01-01 15:51:47 +00:00
void VoipCallsDialog : : captureEvent ( CaptureEvent e )
{
if ( e . captureContext ( ) = = CaptureEvent : : Retap )
{
switch ( e . eventType ( ) )
{
case CaptureEvent : : Started :
ui - > displayFilterCheckBox - > setEnabled ( false ) ;
break ;
case CaptureEvent : : Finished :
ui - > displayFilterCheckBox - > setEnabled ( true ) ;
break ;
default :
break ;
}
}
}
2020-12-13 13:04:11 +00:00
void VoipCallsDialog : : tapReset ( void * tapinfo_ptr )
{
voip_calls_tapinfo_t * tapinfo = static_cast < voip_calls_tapinfo_t * > ( tapinfo_ptr ) ;
VoipCallsDialog * voip_calls_dialog = static_cast < VoipCallsDialog * > ( tapinfo - > tap_data ) ;
2020-12-31 00:00:57 +00:00
// Create new callsinfos queue in tapinfo. Current callsinfos are
// in shown_callsinfos_.
voip_calls_dialog - > tapinfo_ . callsinfos = g_queue_new ( ) ;
2020-12-13 13:04:11 +00:00
voip_calls_reset_all_taps ( tapinfo ) ;
2020-12-29 15:52:33 +00:00
// Leave old graph_analysis as is and allocate new one
voip_calls_dialog - > sequence_info_ - > unref ( ) ;
voip_calls_dialog - > tapinfo_ . graph_analysis = sequence_analysis_info_new ( ) ;
voip_calls_dialog - > tapinfo_ . graph_analysis - > name = " voip " ;
voip_calls_dialog - > sequence_info_ = new SequenceInfo ( voip_calls_dialog - > tapinfo_ . graph_analysis ) ;
2020-12-13 13:04:11 +00:00
}
2014-11-19 00:21:42 +00:00
2022-06-09 15:47:35 +00:00
tap_packet_status VoipCallsDialog : : tapPacket ( void * , packet_info * , epan_dissect_t * , const void * , tap_flags_t )
2014-11-19 00:21:42 +00:00
{
2014-12-13 00:51:40 +00:00
# ifdef QT_MULTIMEDIA_LIB
2014-11-19 00:21:42 +00:00
// voip_calls_tapinfo_t *tapinfo = (voip_calls_tapinfo_t *) tapinfo_ptr;
// add_rtp_packet for voip player.
2019-01-01 03:36:12 +00:00
// return TAP_PACKET_REDRAW;
2014-11-19 00:21:42 +00:00
# endif
2019-01-01 03:36:12 +00:00
return TAP_PACKET_DONT_REDRAW ;
2014-11-19 00:21:42 +00:00
}
void VoipCallsDialog : : tapDraw ( void * tapinfo_ptr )
{
2019-08-14 08:18:51 +00:00
voip_calls_tapinfo_t * tapinfo = static_cast < voip_calls_tapinfo_t * > ( tapinfo_ptr ) ;
2014-11-19 00:21:42 +00:00
if ( ! tapinfo | | ! tapinfo - > redraw ) {
return ;
}
2014-12-13 00:51:40 +00:00
GList * graph_item = g_queue_peek_nth_link ( tapinfo - > graph_analysis - > items , 0 ) ;
2019-08-14 08:18:51 +00:00
for ( ; graph_item ; graph_item = gxx_list_next ( graph_item ) ) {
for ( GList * rsi_entry = g_list_first ( tapinfo - > rtpstream_list ) ; rsi_entry ; rsi_entry = gxx_list_next ( rsi_entry ) ) {
seq_analysis_item_t * sai = gxx_list_data ( seq_analysis_item_t * , graph_item ) ;
rtpstream_info_t * rsi = gxx_list_data ( rtpstream_info_t * , rsi_entry ) ;
2014-12-13 00:51:40 +00:00
2015-10-25 21:52:30 +00:00
if ( rsi - > start_fd - > num = = sai - > frame_number ) {
2014-12-13 00:51:40 +00:00
rsi - > call_num = sai - > conv_num ;
2015-12-06 21:29:53 +00:00
// VOIP_CALLS_DEBUG("setting conv num %u for frame %u", sai->conv_num, sai->frame_number);
2014-12-13 00:51:40 +00:00
}
}
}
2014-11-19 00:21:42 +00:00
VoipCallsDialog * voip_calls_dialog = static_cast < VoipCallsDialog * > ( tapinfo - > tap_data ) ;
if ( voip_calls_dialog ) {
voip_calls_dialog - > updateCalls ( ) ;
}
}
2020-12-31 13:14:14 +00:00
gint VoipCallsDialog : : compareCallNums ( gconstpointer a , gconstpointer b )
2020-12-31 00:00:57 +00:00
{
2021-04-19 19:02:31 +00:00
const voip_calls_info_t * call_a = ( const voip_calls_info_t * ) a ;
const voip_calls_info_t * call_b = ( const voip_calls_info_t * ) b ;
2020-12-31 00:00:57 +00:00
2020-12-31 13:14:14 +00:00
return ( call_a - > call_num ! = call_b - > call_num ) ;
2020-12-31 00:00:57 +00:00
}
2014-11-19 00:21:42 +00:00
void VoipCallsDialog : : updateCalls ( )
{
2020-12-31 00:00:57 +00:00
voip_calls_info_t * new_callsinfo ;
voip_calls_info_t * old_callsinfo ;
GList * found ;
2017-02-13 00:36:58 +00:00
ui - > callTreeView - > setSortingEnabled ( false ) ;
2014-11-19 00:21:42 +00:00
2020-12-31 00:00:57 +00:00
// Merge new callsinfos with old ones
// It keeps list of calls visible including selected items
GList * list = g_queue_peek_nth_link ( tapinfo_ . callsinfos , 0 ) ;
while ( list ) {
// Find new callsinfo
new_callsinfo = gxx_list_data ( voip_calls_info_t * , list ) ;
2020-12-31 13:14:14 +00:00
found = g_queue_find_custom ( shown_callsinfos_ , new_callsinfo , VoipCallsDialog : : compareCallNums ) ;
2020-12-31 00:00:57 +00:00
if ( ! found ) {
// New call, add it to list for show
g_queue_push_tail ( shown_callsinfos_ , new_callsinfo ) ;
} else {
// Existing call
old_callsinfo = ( voip_calls_info_t * ) found - > data ;
if ( new_callsinfo ! = old_callsinfo ) {
// Replace it
voip_calls_free_callsinfo ( old_callsinfo ) ;
found - > data = new_callsinfo ;
}
}
list = gxx_list_next ( list ) ;
}
// Update model
call_infos_model_ - > updateCalls ( shown_callsinfos_ ) ;
2014-11-19 00:21:42 +00:00
// Resize columns
2017-02-13 00:36:58 +00:00
for ( int i = 0 ; i < call_infos_model_ - > columnCount ( ) ; i + + ) {
ui - > callTreeView - > resizeColumnToContents ( i ) ;
2014-11-19 00:21:42 +00:00
}
2017-02-13 00:36:58 +00:00
ui - > callTreeView - > setSortingEnabled ( true ) ;
2014-11-19 00:21:42 +00:00
updateWidgets ( ) ;
}
void VoipCallsDialog : : updateWidgets ( )
{
2017-02-13 00:36:58 +00:00
bool selected = ui - > callTreeView - > selectionModel ( ) - > hasSelection ( ) ;
2014-11-19 00:21:42 +00:00
bool have_ga_items = false ;
if ( tapinfo_ . graph_analysis & & tapinfo_ . graph_analysis - > items ) {
have_ga_items = true ;
}
2021-01-06 22:49:36 +00:00
bool enable = selected & & have_ga_items & & ! file_closed_ ;
prepare_button_ - > setEnabled ( enable ) ;
sequence_button_ - > setEnabled ( enable ) ;
2021-04-19 13:54:58 +00:00
ui - > actionSelectRtpStreams - > setEnabled ( enable ) ;
ui - > actionDeselectRtpStreams - > setEnabled ( enable ) ;
2014-12-13 00:51:40 +00:00
# if defined(QT_MULTIMEDIA_LIB)
2021-01-06 22:49:36 +00:00
player_button_ - > setEnabled ( enable ) ;
2014-11-19 00:21:42 +00:00
# endif
2021-01-06 22:49:36 +00:00
WiresharkDialog : : updateWidgets ( ) ;
2014-11-19 00:21:42 +00:00
}
void VoipCallsDialog : : prepareFilter ( )
{
2017-02-13 00:36:58 +00:00
if ( ! ui - > callTreeView - > selectionModel ( ) - > hasSelection ( ) | | ! tapinfo_ . graph_analysis ) {
2014-11-19 00:21:42 +00:00
return ;
}
QString filter_str ;
QSet < guint16 > selected_calls ;
2018-06-02 12:43:40 +00:00
QString frame_numbers ;
2019-10-28 16:07:14 +00:00
QList < int > rows ;
2014-11-19 00:21:42 +00:00
/* Build a new filter based on frame numbers */
2017-02-13 00:36:58 +00:00
foreach ( QModelIndex index , ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) ) {
2019-11-17 19:02:20 +00:00
if ( index . isValid ( ) & & ! rows . contains ( index . row ( ) ) )
2019-10-28 16:07:14 +00:00
{
voip_calls_info_t * call_info = VoipCallsInfoModel : : indexToCallInfo ( index ) ;
if ( ! call_info ) {
return ;
}
selected_calls < < call_info - > call_num ;
rows < < index . row ( ) ;
2015-10-16 13:00:24 +00:00
}
2014-11-19 00:21:42 +00:00
}
GList * cur_ga_item = g_queue_peek_nth_link ( tapinfo_ . graph_analysis - > items , 0 ) ;
while ( cur_ga_item & & cur_ga_item - > data ) {
2019-08-14 08:18:51 +00:00
seq_analysis_item_t * ga_item = gxx_list_data ( seq_analysis_item_t * , cur_ga_item ) ;
2014-11-19 00:21:42 +00:00
if ( selected_calls . contains ( ga_item - > conv_num ) ) {
2021-11-16 20:02:05 +00:00
frame_numbers + = QString ( " %1, " ) . arg ( ga_item - > frame_number ) ;
2014-11-19 00:21:42 +00:00
}
2019-08-14 08:18:51 +00:00
cur_ga_item = gxx_list_next ( cur_ga_item ) ;
2014-11-19 00:21:42 +00:00
}
2018-06-02 12:43:40 +00:00
if ( ! frame_numbers . isEmpty ( ) ) {
frame_numbers . chop ( 1 ) ;
filter_str = QString ( " frame.number in {%1} or rtp.setup-frame in {%1} " ) . arg ( frame_numbers ) ;
}
2014-11-19 00:21:42 +00:00
#if 0
// XXX The GTK+ UI falls back to building a filter based on protocols if the filter
// length is too long. Leaving this here for the time being in case we need to do
// the same in the Qt UI.
const sip_calls_info_t * sipinfo ;
const isup_calls_info_t * isupinfo ;
const h323_calls_info_t * h323info ;
const h245_address_t * h245_add = NULL ;
const gcp_ctx_t * ctx ;
2015-01-29 21:55:18 +00:00
char * guid_str ;
2014-11-19 00:21:42 +00:00
if ( filter_length < max_filter_length ) {
gtk_editable_insert_text ( GTK_EDITABLE ( main_display_filter_widget ) , filter_string_fwd - > str , - 1 , & pos ) ;
} else {
g_string_free ( filter_string_fwd , TRUE ) ;
filter_string_fwd = g_string_new ( filter_prepend ) ;
g_string_append_printf ( filter_string_fwd , " ( " ) ;
is_first = TRUE ;
/* Build a new filter based on protocol fields */
lista = g_queue_peek_nth_link ( voip_calls_get_info ( ) - > callsinfos , 0 ) ;
while ( lista ) {
2019-08-14 08:18:51 +00:00
listinfo = gxx_list_data ( voip_calls_info_t * , lista ) ;
2014-11-19 00:21:42 +00:00
if ( listinfo - > selected ) {
if ( ! is_first )
g_string_append_printf ( filter_string_fwd , " or " ) ;
switch ( listinfo - > protocol ) {
case VOIP_SIP :
sipinfo = ( sip_calls_info_t * ) listinfo - > prot_info ;
g_string_append_printf ( filter_string_fwd ,
" (sip.Call-ID == \" %s \" ) " ,
sipinfo - > call_identifier
) ;
break ;
case VOIP_ISUP :
isupinfo = ( isup_calls_info_t * ) listinfo - > prot_info ;
g_string_append_printf ( filter_string_fwd ,
" (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))) " ,
isupinfo - > cic , listinfo - > start_fd - > num ,
listinfo - > stop_fd - > num ,
isupinfo - > ni , isupinfo - > dpc , isupinfo - > opc ,
isupinfo - > opc , isupinfo - > dpc
) ;
break ;
case VOIP_H323 :
2015-01-29 21:55:18 +00:00
{
2014-11-19 00:21:42 +00:00
h323info = ( h323_calls_info_t * ) listinfo - > prot_info ;
2015-01-08 00:24:52 +00:00
guid_str = guid_to_str ( NULL , & h323info - > guid [ 0 ] ) ;
2014-11-19 00:21:42 +00:00
g_string_append_printf ( filter_string_fwd ,
" ((h225.guid == %s || q931.call_ref == %x:%x || q931.call_ref == %x:%x) " ,
2015-01-08 00:24:52 +00:00
guid_str ,
2014-11-19 00:21:42 +00:00
( guint8 ) ( h323info - > q931_crv & 0x00ff ) ,
( guint8 ) ( ( h323info - > q931_crv & 0xff00 ) > > 8 ) ,
( guint8 ) ( h323info - > q931_crv2 & 0x00ff ) ,
( guint8 ) ( ( h323info - > q931_crv2 & 0xff00 ) > > 8 ) ) ;
listb = g_list_first ( h323info - > h245_list ) ;
2015-01-08 00:24:52 +00:00
wmem_free ( NULL , guid_str ) ;
2014-11-19 00:21:42 +00:00
while ( listb ) {
2019-08-14 08:18:51 +00:00
h245_add = gxx_list_data ( h245_address_t * , listb ) ;
2014-11-19 00:21:42 +00:00
g_string_append_printf ( filter_string_fwd ,
" || (ip.addr == %s && tcp.port == %d && h245) " ,
2015-01-29 21:55:18 +00:00
address_to_qstring ( & h245_add - > h245_address ) , h245_add - > h245_port ) ;
2019-08-14 08:18:51 +00:00
listb = gxx_list_next ( listb ) ;
2014-11-19 00:21:42 +00:00
}
g_string_append_printf ( filter_string_fwd , " ) " ) ;
2015-01-29 21:55:18 +00:00
}
2014-11-19 00:21:42 +00:00
break ;
case TEL_H248 :
ctx = ( gcp_ctx_t * ) listinfo - > prot_info ;
g_string_append_printf ( filter_string_fwd ,
" (h248.ctx == 0x%x) " , ctx - > id ) ;
break ;
default :
/* placeholder to assure valid display filter expression */
g_string_append_printf ( filter_string_fwd ,
" (frame) " ) ;
break ;
}
is_first = FALSE ;
}
2019-08-14 08:18:51 +00:00
lista = gxx_list_next ( lista ) ;
2014-11-19 00:21:42 +00:00
}
g_string_append_printf ( filter_string_fwd , " ) " ) ;
gtk_editable_insert_text ( GTK_EDITABLE ( main_display_filter_widget ) , filter_string_fwd - > str , - 1 , & pos ) ;
}
# endif
emit updateFilter ( filter_str ) ;
}
void VoipCallsDialog : : showSequence ( )
{
2014-12-29 19:42:01 +00:00
if ( file_closed_ ) return ;
2014-11-19 00:21:42 +00:00
QSet < guint16 > selected_calls ;
2017-02-13 00:36:58 +00:00
foreach ( QModelIndex index , ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) ) {
voip_calls_info_t * call_info = VoipCallsInfoModel : : indexToCallInfo ( index ) ;
2015-10-16 13:00:24 +00:00
if ( ! call_info ) {
return ;
}
2014-11-19 00:21:42 +00:00
selected_calls < < call_info - > call_num ;
}
sequence_analysis_list_sort ( tapinfo_ . graph_analysis ) ;
GList * cur_ga_item = g_queue_peek_nth_link ( tapinfo_ . graph_analysis - > items , 0 ) ;
while ( cur_ga_item & & cur_ga_item - > data ) {
2019-08-14 08:18:51 +00:00
seq_analysis_item_t * ga_item = gxx_list_data ( seq_analysis_item_t * , cur_ga_item ) ;
2014-11-19 00:21:42 +00:00
ga_item - > display = selected_calls . contains ( ga_item - > conv_num ) ;
2019-08-14 08:18:51 +00:00
cur_ga_item = gxx_list_next ( cur_ga_item ) ;
2014-11-19 00:21:42 +00:00
}
2015-11-15 21:57:43 +00:00
SequenceDialog * sequence_dialog = new SequenceDialog ( parent_ , cap_file_ , sequence_info_ ) ;
2021-03-27 20:01:43 +00:00
// Bypass this dialog and forward signals to parent
2021-04-19 13:54:58 +00:00
connect ( sequence_dialog , SIGNAL ( rtpStreamsDialogSelectRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpStreamsDialogSelectRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( sequence_dialog , SIGNAL ( rtpStreamsDialogDeselectRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpStreamsDialogDeselectRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
2021-04-22 19:33:11 +00:00
connect ( sequence_dialog , SIGNAL ( rtpPlayerDialogReplaceRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpPlayerDialogReplaceRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( sequence_dialog , SIGNAL ( rtpPlayerDialogAddRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpPlayerDialogAddRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
connect ( sequence_dialog , SIGNAL ( rtpPlayerDialogRemoveRtpStreams ( QVector < rtpstream_id_t * > ) ) , & parent_ , SLOT ( rtpPlayerDialogRemoveRtpStreams ( QVector < rtpstream_id_t * > ) ) ) ;
2021-04-19 13:54:58 +00:00
2019-10-02 14:29:35 +00:00
sequence_dialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2021-03-27 20:01:43 +00:00
sequence_dialog - > enableVoIPFeatures ( ) ;
2014-11-19 00:21:42 +00:00
sequence_dialog - > show ( ) ;
}
2021-04-22 19:33:11 +00:00
QVector < rtpstream_id_t * > VoipCallsDialog : : getSelectedRtpIds ( )
2014-12-13 00:51:40 +00:00
{
2021-04-22 19:33:11 +00:00
QVector < rtpstream_id_t * > stream_ids ;
2017-02-13 00:36:58 +00:00
foreach ( QModelIndex index , ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) ) {
voip_calls_info_t * vci = VoipCallsInfoModel : : indexToCallInfo ( index ) ;
if ( ! vci ) continue ;
2019-08-14 08:18:51 +00:00
for ( GList * rsi_entry = g_list_first ( tapinfo_ . rtpstream_list ) ; rsi_entry ; rsi_entry = gxx_list_next ( rsi_entry ) ) {
rtpstream_info_t * rsi = gxx_list_data ( rtpstream_info_t * , rsi_entry ) ;
2014-12-13 00:51:40 +00:00
if ( ! rsi ) continue ;
//VOIP_CALLS_DEBUG("checking call %u, start frame %u == stream call %u, start frame %u, setup frame %u",
2017-02-13 00:36:58 +00:00
// vci->call_num, vci->start_fd->num,
2014-12-13 00:51:40 +00:00
// rsi->call_num, rsi->start_fd->num, rsi->setup_frame_number);
2019-08-14 08:18:51 +00:00
if ( vci - > call_num = = static_cast < guint > ( rsi - > call_num ) ) {
2017-02-13 00:36:58 +00:00
//VOIP_CALLS_DEBUG("adding call number %u", vci->call_num);
2021-04-22 19:33:11 +00:00
if ( - 1 = = stream_ids . indexOf ( & ( rsi - > id ) ) ) {
2021-04-19 13:54:58 +00:00
// Add only new stream
2021-04-22 19:33:11 +00:00
stream_ids < < & ( rsi - > id ) ;
2021-04-19 13:54:58 +00:00
}
2014-12-13 00:51:40 +00:00
}
}
}
2021-04-22 19:33:11 +00:00
return stream_ids ;
2021-03-27 20:01:43 +00:00
}
void VoipCallsDialog : : rtpPlayerReplace ( )
{
if ( ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) . count ( ) < 1 ) return ;
2021-04-22 19:33:11 +00:00
emit rtpPlayerDialogReplaceRtpStreams ( getSelectedRtpIds ( ) ) ;
2021-03-27 20:01:43 +00:00
}
void VoipCallsDialog : : rtpPlayerAdd ( )
{
if ( ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) . count ( ) < 1 ) return ;
2021-04-22 19:33:11 +00:00
emit rtpPlayerDialogAddRtpStreams ( getSelectedRtpIds ( ) ) ;
2021-03-27 20:01:43 +00:00
}
void VoipCallsDialog : : rtpPlayerRemove ( )
{
if ( ui - > callTreeView - > selectionModel ( ) - > selectedIndexes ( ) . count ( ) < 1 ) return ;
2014-12-13 00:51:40 +00:00
2021-04-22 19:33:11 +00:00
emit rtpPlayerDialogRemoveRtpStreams ( getSelectedRtpIds ( ) ) ;
2014-12-13 00:51:40 +00:00
}
2016-06-16 21:09:28 +00:00
QList < QVariant > VoipCallsDialog : : streamRowData ( int row ) const
{
QList < QVariant > row_data ;
2017-02-13 00:36:58 +00:00
if ( row > = sorted_model_ - > rowCount ( ) ) {
2016-06-16 21:09:28 +00:00
return row_data ;
}
2017-02-13 00:36:58 +00:00
for ( int col = 0 ; col < sorted_model_ - > columnCount ( ) ; col + + ) {
2016-06-16 21:09:28 +00:00
if ( row < 0 ) {
2017-02-13 00:36:58 +00:00
row_data < < sorted_model_ - > headerData ( col , Qt : : Horizontal ) ;
2016-06-16 21:09:28 +00:00
} else {
2017-02-13 00:36:58 +00:00
row_data < < sorted_model_ - > index ( row , col ) . data ( ) ;
2016-06-16 21:09:28 +00:00
}
}
return row_data ;
}
2017-02-13 00:36:58 +00:00
void VoipCallsDialog : : on_callTreeView_activated ( const QModelIndex & index )
2014-11-19 00:21:42 +00:00
{
2017-02-13 00:36:58 +00:00
voip_calls_info_t * call_info = VoipCallsInfoModel : : indexToCallInfo ( index ) ;
2014-11-19 00:21:42 +00:00
if ( ! call_info ) {
return ;
}
emit goToPacket ( call_info - > start_fd - > num ) ;
}
2019-08-02 18:39:13 +00:00
void VoipCallsDialog : : selectAll ( )
2014-11-19 00:21:42 +00:00
{
2017-02-13 00:36:58 +00:00
ui - > callTreeView - > selectAll ( ) ;
2014-11-19 00:21:42 +00:00
}
2021-01-06 22:49:36 +00:00
void VoipCallsDialog : : selectNone ( )
{
ui - > callTreeView - > clearSelection ( ) ;
}
2019-08-02 18:39:13 +00:00
void VoipCallsDialog : : copyAsCSV ( )
2016-06-16 21:09:28 +00:00
{
QString csv ;
QTextStream stream ( & csv , QIODevice : : Text ) ;
2017-02-13 00:36:58 +00:00
for ( int row = - 1 ; row < sorted_model_ - > rowCount ( ) ; row + + ) {
2016-06-16 21:09:28 +00:00
QStringList rdsl ;
foreach ( QVariant v , streamRowData ( row ) ) {
2017-02-13 00:36:58 +00:00
QString strval = v . toString ( ) ;
// XXX should quotes (") in strval be stripped/sanitized?
rdsl < < QString ( " \" %1 \" " ) . arg ( strval ) ;
2016-06-16 21:09:28 +00:00
}
2020-05-28 18:40:08 +00:00
stream < < rdsl . join ( " , " ) < < ' \n ' ;
2016-06-16 21:09:28 +00:00
}
2022-02-01 03:30:09 +00:00
mainApp - > clipboard ( ) - > setText ( stream . readAll ( ) ) ;
2016-06-16 21:09:28 +00:00
}
2019-08-02 18:39:13 +00:00
void VoipCallsDialog : : copyAsYAML ( )
2016-06-16 21:09:28 +00:00
{
QString yaml ;
QTextStream stream ( & yaml , QIODevice : : Text ) ;
2020-05-28 18:40:08 +00:00
stream < < " --- " < < ' \n ' ;
2017-02-13 00:36:58 +00:00
for ( int row = - 1 ; row < sorted_model_ - > rowCount ( ) ; row + + ) {
2020-05-28 18:40:08 +00:00
stream < < " - " < < ' \n ' ;
2016-06-16 21:09:28 +00:00
foreach ( QVariant v , streamRowData ( row ) ) {
2020-05-28 18:40:08 +00:00
stream < < " - " < < v . toString ( ) < < ' \n ' ;
2016-06-16 21:09:28 +00:00
}
}
2022-02-01 03:30:09 +00:00
mainApp - > clipboard ( ) - > setText ( stream . readAll ( ) ) ;
2016-06-16 21:09:28 +00:00
}
2014-11-19 00:21:42 +00:00
void VoipCallsDialog : : on_buttonBox_clicked ( QAbstractButton * button )
{
if ( button = = prepare_button_ ) {
prepareFilter ( ) ;
} else if ( button = = sequence_button_ ) {
showSequence ( ) ;
}
}
2021-01-01 15:51:47 +00:00
void VoipCallsDialog : : removeAllCalls ( )
{
voip_calls_info_t * callsinfo ;
GList * list = NULL ;
call_infos_model_ - > removeAllCalls ( ) ;
/* Free shown callsinfos */
list = g_queue_peek_nth_link ( shown_callsinfos_ , 0 ) ;
while ( list )
{
callsinfo = ( voip_calls_info_t * ) list - > data ;
voip_calls_free_callsinfo ( callsinfo ) ;
list = g_list_next ( list ) ;
}
g_queue_clear ( shown_callsinfos_ ) ;
}
void VoipCallsDialog : : on_displayFilterCheckBox_toggled ( bool checked )
{
if ( ! cap_file_ . isValid ( ) ) {
return ;
}
tapinfo_ . apply_display_filter = checked ;
removeAllCalls ( ) ;
cap_file_ . retapPackets ( ) ;
}
2014-11-19 00:21:42 +00:00
void VoipCallsDialog : : on_buttonBox_helpRequested ( )
{
2022-02-01 03:30:09 +00:00
mainApp - > helpTopicAction ( HELP_TELEPHONY_VOIP_CALLS_DIALOG ) ;
2014-11-19 00:21:42 +00:00
}
2019-08-02 18:39:13 +00:00
void VoipCallsDialog : : switchTimeOfDay ( )
2016-10-22 09:32:51 +00:00
{
2019-08-02 18:39:13 +00:00
bool checked = ! call_infos_model_ - > timeOfDay ( ) ;
ui - > todCheckBox - > setChecked ( checked ) ;
call_infos_model_ - > setTimeOfDay ( checked ) ;
2017-02-13 00:36:58 +00:00
ui - > callTreeView - > resizeColumnToContents ( VoipCallsInfoModel : : StartTime ) ;
ui - > callTreeView - > resizeColumnToContents ( VoipCallsInfoModel : : StopTime ) ;
2016-10-22 09:32:51 +00:00
}
2021-01-01 15:51:47 +00:00
void VoipCallsDialog : : displayFilterSuccess ( bool success )
{
if ( success & & ui - > displayFilterCheckBox - > isChecked ( ) ) {
removeAllCalls ( ) ;
cap_file_ . retapPackets ( ) ;
}
}
2021-03-13 20:28:06 +00:00
void VoipCallsDialog : : invertSelection ( )
{
QModelIndex rootIndex = ui - > callTreeView - > rootIndex ( ) ;
QModelIndex first = sorted_model_ - > index ( 0 , 0 , QModelIndex ( ) ) ;
int numOfItems = sorted_model_ - > rowCount ( rootIndex ) ;
int numOfCols = sorted_model_ - > columnCount ( rootIndex ) ;
QModelIndex last = sorted_model_ - > index ( numOfItems - 1 , numOfCols - 1 , QModelIndex ( ) ) ;
QItemSelection selection ( first , last ) ;
ui - > callTreeView - > selectionModel ( ) - > select ( selection , QItemSelectionModel : : Toggle ) ;
}
void VoipCallsDialog : : on_actionSelectAll_triggered ( )
{
ui - > callTreeView - > selectAll ( ) ;
}
void VoipCallsDialog : : on_actionSelectInvert_triggered ( )
{
invertSelection ( ) ;
}
void VoipCallsDialog : : on_actionSelectNone_triggered ( )
{
ui - > callTreeView - > clearSelection ( ) ;
}
2021-04-19 13:54:58 +00:00
void VoipCallsDialog : : on_actionSelectRtpStreams_triggered ( )
{
2021-04-22 19:33:11 +00:00
QVector < rtpstream_id_t * > stream_ids = qvector_rtpstream_ids_copy ( getSelectedRtpIds ( ) ) ;
2021-04-19 13:54:58 +00:00
emit rtpStreamsDialogSelectRtpStreams ( stream_ids ) ;
qvector_rtpstream_ids_free ( stream_ids ) ;
raise ( ) ;
}
void VoipCallsDialog : : on_actionDeselectRtpStreams_triggered ( )
{
2021-04-22 19:33:11 +00:00
QVector < rtpstream_id_t * > stream_ids = qvector_rtpstream_ids_copy ( getSelectedRtpIds ( ) ) ;
2021-04-19 13:54:58 +00:00
emit rtpStreamsDialogDeselectRtpStreams ( stream_ids ) ;
qvector_rtpstream_ids_free ( stream_ids ) ;
raise ( ) ;
}