1 /* bluetooth_devices_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 "bluetooth_devices_dialog.h"
11 #include <ui_bluetooth_devices_dialog.h>
13 #include "bluetooth_device_dialog.h"
15 #include <ui/qt/utils/color_utils.h>
17 #include "epan/epan.h"
18 #include "epan/addr_resolv.h"
19 #include "epan/to_str.h"
20 #include "epan/epan_dissect.h"
21 #include "epan/prefs.h"
22 #include "epan/dissectors/packet-bluetooth.h"
23 #include "epan/dissectors/packet-bthci_evt.h"
25 #include <ui/qt/utils/variant_pointer.h>
27 #include "ui/simple_dialog.h"
28 #include "ui/qt/widgets/wireshark_file_dialog.h"
31 #include <QContextMenuEvent>
32 #include <QPushButton>
33 #include <QTreeWidget>
35 static const int column_number_bd_addr
= 0;
36 static const int column_number_bd_addr_oui
= 1;
37 static const int column_number_name
= 2;
38 static const int column_number_lmp_version
= 3;
39 static const int column_number_lmp_subversion
= 4;
40 static const int column_number_manufacturer
= 5;
41 static const int column_number_hci_version
= 6;
42 static const int column_number_hci_revision
= 7;
43 static const int column_number_is_local_adapter
= 8;
46 static tap_packet_status
47 bluetooth_device_tap_packet(void *tapinfo_ptr
, packet_info
*pinfo
, epan_dissect_t
*edt
, const void* data
, tap_flags_t flags
)
49 bluetooth_devices_tapinfo_t
*tapinfo
= (bluetooth_devices_tapinfo_t
*) tapinfo_ptr
;
51 if (tapinfo
->tap_packet
)
52 tapinfo
->tap_packet(tapinfo
, pinfo
, edt
, data
, flags
);
54 return TAP_PACKET_REDRAW
;
58 bluetooth_device_tap_reset(void *tapinfo_ptr
)
60 bluetooth_devices_tapinfo_t
*tapinfo
= (bluetooth_devices_tapinfo_t
*) tapinfo_ptr
;
62 if (tapinfo
->tap_reset
)
63 tapinfo
->tap_reset(tapinfo
);
66 BluetoothDevicesDialog::BluetoothDevicesDialog(QWidget
&parent
, CaptureFile
&cf
, PacketList
*packet_list
) :
67 WiresharkDialog(parent
, cf
),
68 ui(new Ui::BluetoothDevicesDialog
)
71 loadGeometry(parent
.width() * 4 / 5, parent
.height() * 2 / 3);
73 packet_list_
= packet_list
;
75 connect(ui
->tableTreeWidget
, SIGNAL(customContextMenuRequested(const QPoint
&)), this, SLOT(tableContextMenu(const QPoint
&)));
76 connect(ui
->tableTreeWidget
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*, int)), this, SLOT(tableItemDoubleClicked(QTreeWidgetItem
*, int)));
77 connect(ui
->interfaceComboBox
, SIGNAL(currentIndexChanged(int)), this, SLOT(interfaceCurrentIndexChanged(int)));
78 connect(ui
->showInformationStepsCheckBox
, SIGNAL(stateChanged(int)), this, SLOT(showInformationStepsChanged(int)));
80 ui
->tableTreeWidget
->sortByColumn(column_number_bd_addr
, Qt::AscendingOrder
);
82 ui
->tableTreeWidget
->setStyleSheet("QTreeView::item:hover{background-color:lightyellow; color:black;}");
84 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionMark_Unmark_Cell
);
85 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionMark_Unmark_Row
);
86 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionCopy_Cell
);
87 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionCopy_Rows
);
88 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionCopy_All
);
89 context_menu_
.addActions(QList
<QAction
*>() << ui
->actionSave_as_image
);
91 tapinfo_
.tap_packet
= tapPacket
;
92 tapinfo_
.tap_reset
= tapReset
;
95 registerTapListener("bluetooth.device", &tapinfo_
, NULL
,
97 bluetooth_device_tap_reset
,
98 bluetooth_device_tap_packet
,
101 ui
->hintLabel
->setText(ui
->hintLabel
->text().arg(0));
103 cap_file_
.retapPackets();
107 BluetoothDevicesDialog::~BluetoothDevicesDialog()
113 void BluetoothDevicesDialog::captureFileClosed()
115 ui
->interfaceComboBox
->setEnabled(false);
116 ui
->showInformationStepsCheckBox
->setEnabled(false);
118 WiresharkDialog::captureFileClosed();
122 void BluetoothDevicesDialog::changeEvent(QEvent
*event
)
126 switch (event
->type())
128 case QEvent::LanguageChange
:
129 ui
->retranslateUi(this);
135 QDialog::changeEvent(event
);
139 void BluetoothDevicesDialog::keyPressEvent(QKeyEvent
*event
)
141 /* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user
142 * to use Enter button to jump to frame from tree widget */
143 /* * - reimplement shortcuts from contex menu */
145 if (event
->modifiers() & Qt::ControlModifier
&& event
->key()== Qt::Key_M
)
146 on_actionMark_Unmark_Row_triggered();
150 void BluetoothDevicesDialog::tableContextMenu(const QPoint
&pos
)
152 context_menu_
.popup(ui
->tableTreeWidget
->viewport()->mapToGlobal(pos
));
155 void BluetoothDevicesDialog::tableItemDoubleClicked(QTreeWidgetItem
*item
, int)
157 bluetooth_item_data_t
*item_data
;
158 BluetoothDeviceDialog
*bluetooth_device_dialog
;
160 item_data
= VariantPointer
<bluetooth_item_data_t
>::asPtr(item
->data(0, Qt::UserRole
));
161 bluetooth_device_dialog
= new BluetoothDeviceDialog(*this, cap_file_
, item
->text(column_number_bd_addr
), item
->text(column_number_name
), item_data
->interface_id
, item_data
->adapter_id
, !item
->text(column_number_is_local_adapter
).isEmpty());
162 connect(bluetooth_device_dialog
, SIGNAL(goToPacket(int)),
163 packet_list_
, SLOT(goToPacket(int)));
164 bluetooth_device_dialog
->show();
168 void BluetoothDevicesDialog::on_actionMark_Unmark_Cell_triggered()
173 if (ui
->tableTreeWidget
->currentItem()->background(ui
->tableTreeWidget
->currentColumn()) == QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_bg
))) {
177 fg
= QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_fg
));
178 bg
= QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_bg
));
181 ui
->tableTreeWidget
->currentItem()->setForeground(ui
->tableTreeWidget
->currentColumn(), fg
);
182 ui
->tableTreeWidget
->currentItem()->setBackground(ui
->tableTreeWidget
->currentColumn(), bg
);
186 void BluetoothDevicesDialog::on_actionMark_Unmark_Row_triggered()
190 bool is_marked
= true;
192 for (int i
= 0; i
< ui
->tableTreeWidget
->columnCount(); i
+= 1) {
193 if (ui
->tableTreeWidget
->currentItem()->background(i
) != QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_bg
)))
201 fg
= QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_fg
));
202 bg
= QBrush(ColorUtils::fromColorT(&prefs
.gui_marked_bg
));
205 for (int i
= 0; i
< ui
->tableTreeWidget
->columnCount(); i
+= 1) {
206 ui
->tableTreeWidget
->currentItem()->setForeground(i
, fg
);
207 ui
->tableTreeWidget
->currentItem()->setBackground(i
, bg
);
212 void BluetoothDevicesDialog::on_actionCopy_Cell_triggered()
214 QClipboard
*clipboard
= QApplication::clipboard();
217 copy
= QString(ui
->tableTreeWidget
->currentItem()->text(ui
->tableTreeWidget
->currentColumn()));
219 clipboard
->setText(copy
);
223 void BluetoothDevicesDialog::on_actionCopy_Rows_triggered()
225 QClipboard
*clipboard
= QApplication::clipboard();
227 QList
<QTreeWidgetItem
*> items
;
228 QList
<QTreeWidgetItem
*>::iterator i_item
;
230 items
= ui
->tableTreeWidget
->selectedItems();
232 for (i_item
= items
.begin(); i_item
!= items
.end(); ++i_item
) {
233 copy
+= QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9\n")
234 .arg((*i_item
)->text(column_number_bd_addr
), -20)
235 .arg((*i_item
)->text(column_number_bd_addr_oui
), -20)
236 .arg((*i_item
)->text(column_number_name
), -30)
237 .arg((*i_item
)->text(column_number_lmp_version
), -20)
238 .arg((*i_item
)->text(column_number_lmp_subversion
), -20)
239 .arg((*i_item
)->text(column_number_manufacturer
), -30)
240 .arg((*i_item
)->text(column_number_hci_version
), -20)
241 .arg((*i_item
)->text(column_number_hci_revision
), -20)
242 .arg((*i_item
)->text(column_number_is_local_adapter
), -20);
245 clipboard
->setText(copy
);
248 void BluetoothDevicesDialog::tapReset(void *tapinfo_ptr
)
250 bluetooth_devices_tapinfo_t
*tapinfo
= (bluetooth_devices_tapinfo_t
*) tapinfo_ptr
;
251 BluetoothDevicesDialog
*bluetooth_devices_dialog
= static_cast<BluetoothDevicesDialog
*>(tapinfo
->ui
);
253 bluetooth_devices_dialog
->ui
->tableTreeWidget
->clear();
256 tap_packet_status
BluetoothDevicesDialog::tapPacket(void *tapinfo_ptr
, packet_info
*pinfo
, epan_dissect_t
*, const void *data
, tap_flags_t
)
258 bluetooth_devices_tapinfo_t
*tapinfo
= static_cast<bluetooth_devices_tapinfo_t
*>(tapinfo_ptr
);
259 BluetoothDevicesDialog
*dialog
= static_cast<BluetoothDevicesDialog
*>(tapinfo
->ui
);
260 bluetooth_device_tap_t
*tap_device
= static_cast<bluetooth_device_tap_t
*>(const_cast<void *>(data
));
264 QTreeWidgetItem
*item
= NULL
;
266 if (dialog
->file_closed_
)
267 return TAP_PACKET_DONT_REDRAW
;
269 if (pinfo
->rec
->rec_type
!= REC_TYPE_PACKET
)
270 return TAP_PACKET_DONT_REDRAW
;
272 if (pinfo
->rec
->presence_flags
& WTAP_HAS_INTERFACE_ID
) {
274 const char *interface_name
;
276 unsigned section_number
= pinfo
->rec
->presence_flags
& WTAP_HAS_SECTION_NUMBER
? pinfo
->rec
->section_number
: 0;
277 interface_name
= epan_get_interface_name(pinfo
->epan
, pinfo
->rec
->rec_header
.packet_header
.interface_id
, section_number
);
278 interface
= wmem_strdup_printf(pinfo
->pool
, "%u: %s", pinfo
->rec
->rec_header
.packet_header
.interface_id
, interface_name
);
280 if (dialog
->ui
->interfaceComboBox
->findText(interface
) == -1)
281 dialog
->ui
->interfaceComboBox
->addItem(interface
);
283 if (interface
&& dialog
->ui
->interfaceComboBox
->currentIndex() > 0) {
284 if (dialog
->ui
->interfaceComboBox
->currentText() != interface
)
285 return TAP_PACKET_REDRAW
;
289 if (tap_device
->has_bd_addr
) {
290 for (int i
= 0; i
< 6; ++i
) {
291 bd_addr
+= QStringLiteral("%1:").arg(tap_device
->bd_addr
[i
], 2, 16, QChar('0'));
293 bd_addr
.chop(1); // remove extra character ":" from the end of the string
294 manuf
= get_ether_name(tap_device
->bd_addr
);
298 bd_addr_oui
= QString(manuf
);
299 pos
= static_cast<int>(bd_addr_oui
.indexOf('_'));
303 bd_addr_oui
.remove(pos
, bd_addr_oui
.size());
311 if (dialog
->ui
->showInformationStepsCheckBox
->checkState() != Qt::Checked
) {
312 QTreeWidgetItemIterator
i_item(dialog
->ui
->tableTreeWidget
);
315 QTreeWidgetItem
*current_item
= static_cast<QTreeWidgetItem
*>(*i_item
);
316 bluetooth_item_data_t
*item_data
= VariantPointer
<bluetooth_item_data_t
>::asPtr(current_item
->data(0, Qt::UserRole
));
318 if ((tap_device
->has_bd_addr
&& current_item
->text(column_number_bd_addr
) == bd_addr
) ||
319 (tap_device
->is_local
&&
320 item_data
->interface_id
== tap_device
->interface_id
&&
321 item_data
->adapter_id
== tap_device
->adapter_id
&&
322 !current_item
->text(column_number_is_local_adapter
).isEmpty())) {
331 item
= new QTreeWidgetItem(dialog
->ui
->tableTreeWidget
);
332 item
->setText(column_number_bd_addr
, bd_addr
);
333 item
->setText(column_number_bd_addr_oui
, bd_addr_oui
);
334 if (tap_device
->is_local
) {
335 item
->setText(column_number_is_local_adapter
, tr("true"));
338 bluetooth_item_data_t
*item_data
= wmem_new(wmem_file_scope(), bluetooth_item_data_t
);
339 item_data
->interface_id
= tap_device
->interface_id
;
340 item_data
->adapter_id
= tap_device
->adapter_id
;
341 item_data
->frame_number
= pinfo
->num
;
342 item
->setData(0, Qt::UserRole
, VariantPointer
<bluetooth_item_data_t
>::asQVariant(item_data
));
345 if (tap_device
->type
== BLUETOOTH_DEVICE_BD_ADDR
) {
346 item
->setText(column_number_bd_addr
, bd_addr
);
347 item
->setText(column_number_bd_addr_oui
, bd_addr_oui
);
350 if (tap_device
->type
== BLUETOOTH_DEVICE_NAME
) {
351 item
->setText(column_number_name
, tap_device
->data
.name
);
354 if (tap_device
->type
== BLUETOOTH_DEVICE_LOCAL_ADAPTER
)
355 item
->setText(column_number_is_local_adapter
, tr("true"));
357 if (tap_device
->type
== BLUETOOTH_DEVICE_LOCAL_VERSION
) {
358 item
->setText(column_number_hci_version
, val_to_str_const(tap_device
->data
.local_version
.hci_version
, bthci_evt_hci_version
, "Unknown 0x%02x"));
359 item
->setText(column_number_hci_revision
, QString::number(tap_device
->data
.local_version
.hci_revision
));
360 item
->setText(column_number_lmp_version
, val_to_str_const(tap_device
->data
.local_version
.lmp_version
, bthci_evt_lmp_version
, "Unknown 0x%02x"));
361 item
->setText(column_number_lmp_subversion
, QString::number(tap_device
->data
.local_version
.lmp_subversion
));
362 item
->setText(column_number_manufacturer
, val_to_str_ext_const(tap_device
->data
.local_version
.manufacturer
, &bluetooth_company_id_vals_ext
, "Unknown 0x%04x"));
364 if (tap_device
->type
== BLUETOOTH_DEVICE_REMOTE_VERSION
) {
365 item
->setText(column_number_lmp_version
, val_to_str_const(tap_device
->data
.remote_version
.lmp_version
, bthci_evt_lmp_version
, "Unknown 0x%02x"));
366 item
->setText(column_number_lmp_subversion
, QString::number(tap_device
->data
.remote_version
.lmp_subversion
));
367 item
->setText(column_number_manufacturer
, val_to_str_ext_const(tap_device
->data
.remote_version
.manufacturer
, &bluetooth_company_id_vals_ext
, "Unknown 0x%04x"));
370 for (int i
= 0; i
< dialog
->ui
->tableTreeWidget
->columnCount(); i
++) {
371 dialog
->ui
->tableTreeWidget
->resizeColumnToContents(i
);
374 dialog
->ui
->hintLabel
->setText(tr("%1 items; Right click for more option; Double click for device details").arg(dialog
->ui
->tableTreeWidget
->topLevelItemCount()));
376 return TAP_PACKET_REDRAW
;
379 void BluetoothDevicesDialog::interfaceCurrentIndexChanged(int)
381 cap_file_
.retapPackets();
384 void BluetoothDevicesDialog::showInformationStepsChanged(int)
386 cap_file_
.retapPackets();
389 void BluetoothDevicesDialog::on_tableTreeWidget_itemActivated(QTreeWidgetItem
*item
, int)
394 bluetooth_item_data_t
*item_data
= VariantPointer
<bluetooth_item_data_t
>::asPtr(item
->data(0, Qt::UserRole
));
396 emit
goToPacket(item_data
->frame_number
);
400 void BluetoothDevicesDialog::on_actionCopy_All_triggered()
402 QClipboard
*clipboard
= QApplication::clipboard();
404 QTreeWidgetItemIterator
i_item(ui
->tableTreeWidget
);
405 QTreeWidgetItem
*item
;
407 item
= ui
->tableTreeWidget
->headerItem();
409 copy
+= QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9\n")
410 .arg(item
->text(column_number_bd_addr
), -20)
411 .arg(item
->text(column_number_bd_addr_oui
), -20)
412 .arg(item
->text(column_number_name
), -30)
413 .arg(item
->text(column_number_lmp_version
), -20)
414 .arg(item
->text(column_number_lmp_subversion
), -20)
415 .arg(item
->text(column_number_manufacturer
), -30)
416 .arg(item
->text(column_number_hci_version
), -20)
417 .arg(item
->text(column_number_hci_revision
), -20)
418 .arg(item
->text(column_number_is_local_adapter
), -20);
421 item
= static_cast<QTreeWidgetItem
*>(*i_item
);
422 copy
+= QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9\n")
423 .arg(item
->text(column_number_bd_addr
), -20)
424 .arg(item
->text(column_number_bd_addr_oui
), -20)
425 .arg(item
->text(column_number_name
), -30)
426 .arg(item
->text(column_number_lmp_version
), -20)
427 .arg(item
->text(column_number_lmp_subversion
), -20)
428 .arg(item
->text(column_number_manufacturer
), -30)
429 .arg(item
->text(column_number_hci_version
), -20)
430 .arg(item
->text(column_number_hci_revision
), -20)
431 .arg(item
->text(column_number_is_local_adapter
), -20);
435 clipboard
->setText(copy
);
438 void BluetoothDevicesDialog::on_actionSave_as_image_triggered()
442 QString fileName
= WiresharkFileDialog::getSaveFileName(this,
443 tr("Save Table Image"),
444 "bluetooth_devices_table.png",
445 tr("PNG Image (*.png)"));
447 if (fileName
.isEmpty()) return;
449 image
= ui
->tableTreeWidget
->grab();
450 image
.save(fileName
, "PNG");
453 void BluetoothDevicesDialog::on_buttonBox_clicked(QAbstractButton
*)
455 /* if (button == foo_button_) */