1 /* bluetooth_att_server_attributes_dialog.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "bluetooth_att_server_attributes_dialog.h"
11 #include <ui_bluetooth_att_server_attributes_dialog.h>
12
13 #include <ui/qt/utils/color_utils.h>
14
15 #include "epan/epan.h"
16 #include "epan/to_str.h"
17 #include "epan/epan_dissect.h"
18 #include "epan/prefs.h"
19 #include "epan/dissectors/packet-bluetooth.h"
20 #include "epan/dissectors/packet-btatt.h"
21
22 #include "ui/simple_dialog.h"
23
24 #include "ui/qt/widgets/wireshark_file_dialog.h"
25
26 #include <QClipboard>
27 #include <QContextMenuEvent>
28 #include <QPushButton>
29 #include <QTreeWidget>
30
31 static const int column_number_handle = 0;
32 static const int column_number_uuid = 1;
33 static const int column_number_uuid_name = 2;
34
35 static tap_packet_status
btatt_handle_tap_packet(void * tapinfo_ptr,packet_info * pinfo,epan_dissect_t * edt,const void * data)36 btatt_handle_tap_packet(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *edt, const void* data)
37 {
38 tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr;
39
40 if (tapinfo->tap_packet)
41 tapinfo->tap_packet(tapinfo, pinfo, edt, data);
42
43 return TAP_PACKET_REDRAW;
44 }
45
46 static void
btatt_handle_tap_reset(void * tapinfo_ptr)47 btatt_handle_tap_reset(void *tapinfo_ptr)
48 {
49 tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr;
50
51 if (tapinfo->tap_reset)
52 tapinfo->tap_reset(tapinfo);
53 }
54
BluetoothAttServerAttributesDialog(QWidget & parent,CaptureFile & cf)55 BluetoothAttServerAttributesDialog::BluetoothAttServerAttributesDialog(QWidget &parent, CaptureFile &cf) :
56 WiresharkDialog(parent, cf),
57 ui(new Ui::BluetoothAttServerAttributesDialog)
58 {
59 ui->setupUi(this);
60 loadGeometry(parent.width() * 4 / 5, parent.height() * 2 / 3);
61
62 connect(ui->tableTreeWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(tableContextMenu(const QPoint &)));
63 connect(ui->interfaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(interfaceCurrentIndexChanged(int)));
64 connect(ui->deviceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(deviceCurrentIndexChanged(int)));
65 connect(ui->removeDuplicatesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(removeDuplicatesStateChanged(int)));
66
67 ui->tableTreeWidget->sortByColumn(column_number_handle, Qt::AscendingOrder);
68
69 ui->tableTreeWidget->setStyleSheet("QTreeView::item:hover{background-color:lightyellow; color:black;}");
70
71 context_menu_.addActions(QList<QAction *>() << ui->actionMark_Unmark_Cell);
72 context_menu_.addActions(QList<QAction *>() << ui->actionMark_Unmark_Row);
73 context_menu_.addActions(QList<QAction *>() << ui->actionCopy_Cell);
74 context_menu_.addActions(QList<QAction *>() << ui->actionCopy_Rows);
75 context_menu_.addActions(QList<QAction *>() << ui->actionCopy_All);
76 context_menu_.addActions(QList<QAction *>() << ui->actionSave_as_image);
77
78 tapinfo_.tap_packet = tapPacket;
79 tapinfo_.tap_reset = tapReset;
80 tapinfo_.ui = this;
81
82 registerTapListener("btatt.handles", &tapinfo_, NULL,
83 0,
84 btatt_handle_tap_reset,
85 btatt_handle_tap_packet,
86 NULL
87 );
88
89 cap_file_.retapPackets();
90 }
91
92
~BluetoothAttServerAttributesDialog()93 BluetoothAttServerAttributesDialog::~BluetoothAttServerAttributesDialog()
94 {
95 delete ui;
96 }
97
98
captureFileClosed()99 void BluetoothAttServerAttributesDialog::captureFileClosed()
100 {
101 ui->interfaceComboBox->setEnabled(FALSE);
102 ui->deviceComboBox->setEnabled(FALSE);
103 ui->removeDuplicatesCheckBox->setEnabled(FALSE);
104
105 WiresharkDialog::captureFileClosed();
106 }
107
108
changeEvent(QEvent * event)109 void BluetoothAttServerAttributesDialog::changeEvent(QEvent *event)
110 {
111 if (0 != event)
112 {
113 switch (event->type())
114 {
115 case QEvent::LanguageChange:
116 ui->retranslateUi(this);
117 break;
118 default:
119 break;
120 }
121 }
122 QDialog::changeEvent(event);
123 }
124
125
keyPressEvent(QKeyEvent * event)126 void BluetoothAttServerAttributesDialog::keyPressEvent(QKeyEvent *event)
127 {
128 /* NOTE: Do nothing*, but in real it "takes focus" from button_box so allow user
129 * to use Enter button to jump to frame from tree widget */
130 /* * - reimplement shortcuts from contex menu */
131
132 if (event->modifiers() & Qt::ControlModifier && event->key()== Qt::Key_M)
133 on_actionMark_Unmark_Row_triggered();
134 }
135
136
tableContextMenu(const QPoint & pos)137 void BluetoothAttServerAttributesDialog::tableContextMenu(const QPoint &pos)
138 {
139 context_menu_.exec(ui->tableTreeWidget->viewport()->mapToGlobal(pos));
140 }
141
142
on_actionMark_Unmark_Cell_triggered()143 void BluetoothAttServerAttributesDialog::on_actionMark_Unmark_Cell_triggered()
144 {
145 QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem();
146 if (!current_item)
147 return;
148
149 QBrush fg;
150 QBrush bg;
151
152 if (current_item->background(ui->tableTreeWidget->currentColumn()) == QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg))) {
153 fg = QBrush();
154 bg = QBrush();
155 } else {
156 fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg));
157 bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg));
158 }
159
160 current_item->setForeground(ui->tableTreeWidget->currentColumn(), fg);
161 current_item->setBackground(ui->tableTreeWidget->currentColumn(), bg);
162 }
163
164
on_actionMark_Unmark_Row_triggered()165 void BluetoothAttServerAttributesDialog::on_actionMark_Unmark_Row_triggered()
166 {
167 QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem();
168 if (!current_item)
169 return;
170
171 QBrush fg;
172 QBrush bg;
173 bool is_marked = TRUE;
174
175 for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) {
176 if (current_item->background(i) != QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg)))
177 is_marked = FALSE;
178 }
179
180 if (is_marked) {
181 fg = QBrush();
182 bg = QBrush();
183 } else {
184 fg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_fg));
185 bg = QBrush(ColorUtils::fromColorT(&prefs.gui_marked_bg));
186 }
187
188 for (int i = 0; i < ui->tableTreeWidget->columnCount(); i += 1) {
189 current_item->setForeground(i, fg);
190 current_item->setBackground(i, bg);
191 }
192 }
193
194
on_actionCopy_Cell_triggered()195 void BluetoothAttServerAttributesDialog::on_actionCopy_Cell_triggered()
196 {
197 QTreeWidgetItem *current_item = ui->tableTreeWidget->currentItem();
198 if (!current_item)
199 return;
200
201 QClipboard *clipboard = QApplication::clipboard();
202 QString copy;
203
204 copy = QString(current_item->text(ui->tableTreeWidget->currentColumn()));
205
206 clipboard->setText(copy);
207 }
208
209
on_actionCopy_Rows_triggered()210 void BluetoothAttServerAttributesDialog::on_actionCopy_Rows_triggered()
211 {
212 QClipboard *clipboard = QApplication::clipboard();
213 QString copy;
214 QList<QTreeWidgetItem *> items;
215 QList<QTreeWidgetItem *>::iterator i_item;
216
217 items = ui->tableTreeWidget->selectedItems();
218
219 for (i_item = items.begin(); i_item != items.end(); ++i_item) {
220 copy += QString("%1 %2 %3\n")
221 .arg((*i_item)->text(column_number_handle), -6)
222 .arg((*i_item)->text(column_number_uuid), -32)
223 .arg((*i_item)->text(column_number_uuid_name));
224
225 }
226
227 clipboard->setText(copy);
228 }
229
tapReset(void * tapinfo_ptr)230 void BluetoothAttServerAttributesDialog::tapReset(void *tapinfo_ptr)
231 {
232 tapinfo_t *tapinfo = (tapinfo_t *) tapinfo_ptr;
233 BluetoothAttServerAttributesDialog *bluetooth_att_server_attributes_dialog = static_cast<BluetoothAttServerAttributesDialog *>(tapinfo->ui);
234
235
236 bluetooth_att_server_attributes_dialog->ui->tableTreeWidget->clear();
237 }
238
239
tapPacket(void * tapinfo_ptr,packet_info * pinfo,epan_dissect_t *,const void * data)240 tap_packet_status BluetoothAttServerAttributesDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *data)
241 {
242 tapinfo_t *tapinfo = static_cast<tapinfo_t *>(tapinfo_ptr);
243 BluetoothAttServerAttributesDialog *dialog = static_cast<BluetoothAttServerAttributesDialog *>(tapinfo->ui);
244 tap_handles_t *tap_handles = static_cast<tap_handles_t *>(const_cast<void *>(data));
245 QString handle;
246 QString uuid;
247 QString uuid_name;
248 gchar *addr = NULL;
249
250 if (dialog->file_closed_)
251 return TAP_PACKET_DONT_REDRAW;
252
253 if (pinfo->rec->rec_type != REC_TYPE_PACKET)
254 return TAP_PACKET_DONT_REDRAW;
255
256 if (pinfo->rec->presence_flags & WTAP_HAS_INTERFACE_ID) {
257 gchar *interface;
258 const char *interface_name;
259
260 interface_name = epan_get_interface_name(pinfo->epan, pinfo->rec->rec_header.packet_header.interface_id);
261 interface = wmem_strdup_printf(pinfo->pool, "%u: %s", pinfo->rec->rec_header.packet_header.interface_id, interface_name);
262
263 if (dialog->ui->interfaceComboBox->findText(interface) == -1)
264 dialog->ui->interfaceComboBox->addItem(interface);
265
266 if (interface && dialog->ui->interfaceComboBox->currentIndex() > 0) {
267 if (dialog->ui->interfaceComboBox->currentText() != interface)
268 return TAP_PACKET_REDRAW;
269 }
270 }
271
272 if (pinfo->p2p_dir == P2P_DIR_SENT || pinfo->p2p_dir == P2P_DIR_RECV)
273 addr = address_to_str(pinfo->pool, &pinfo->src);
274
275 if (addr && dialog->ui->deviceComboBox->findText(addr) == -1) {
276 dialog->ui->deviceComboBox->addItem(addr);
277 }
278
279 if (addr && dialog->ui->deviceComboBox->currentIndex() > 0) {
280 if (dialog->ui->deviceComboBox->currentText() != addr)
281 return TAP_PACKET_REDRAW;
282 }
283
284 handle = QString("0x%1").arg(tap_handles->handle, 4, 16, QChar('0'));
285 uuid = QString(print_numeric_uuid(&tap_handles->uuid));
286 uuid_name = QString(print_uuid(&tap_handles->uuid));
287
288 if (dialog->ui->removeDuplicatesCheckBox->checkState() == Qt::Checked) {
289 QTreeWidgetItemIterator i_item(dialog->ui->tableTreeWidget);
290
291 while (*i_item) {
292 QTreeWidgetItem *item = static_cast<QTreeWidgetItem*>(*i_item);
293
294 if (item->text(column_number_handle) == handle &&
295 item->text(column_number_uuid) == uuid &&
296 item->text(column_number_uuid_name) == uuid_name)
297 return TAP_PACKET_REDRAW;
298 ++i_item;
299 }
300 }
301
302 QTreeWidgetItem *item = new QTreeWidgetItem(dialog->ui->tableTreeWidget);
303 item->setText(column_number_handle, handle);
304 item->setText(column_number_uuid, uuid);
305 item->setText(column_number_uuid_name, uuid_name);
306 item->setData(0, Qt::UserRole, QVariant::fromValue(pinfo->num));
307
308 for (int i = 0; i < dialog->ui->tableTreeWidget->columnCount(); i++) {
309 dialog->ui->tableTreeWidget->resizeColumnToContents(i);
310 }
311
312 return TAP_PACKET_REDRAW;
313 }
314
interfaceCurrentIndexChanged(int)315 void BluetoothAttServerAttributesDialog::interfaceCurrentIndexChanged(int)
316 {
317 cap_file_.retapPackets();
318 }
319
320
deviceCurrentIndexChanged(int)321 void BluetoothAttServerAttributesDialog::deviceCurrentIndexChanged(int)
322 {
323 cap_file_.retapPackets();
324 }
325
326
removeDuplicatesStateChanged(int)327 void BluetoothAttServerAttributesDialog::removeDuplicatesStateChanged(int)
328 {
329 cap_file_.retapPackets();
330 }
331
332
333
on_tableTreeWidget_itemActivated(QTreeWidgetItem * item,int)334 void BluetoothAttServerAttributesDialog::on_tableTreeWidget_itemActivated(QTreeWidgetItem *item, int)
335 {
336 if (file_closed_)
337 return;
338
339 guint32 frame_number = item->data(0, Qt::UserRole).value<guint32>();
340
341 emit goToPacket(frame_number);
342 }
343
344
on_actionCopy_All_triggered()345 void BluetoothAttServerAttributesDialog::on_actionCopy_All_triggered()
346 {
347 QClipboard *clipboard = QApplication::clipboard();
348 QString copy;
349 QTreeWidgetItemIterator i_item(ui->tableTreeWidget);
350
351 copy = QString("%1 %2 %3\n")
352 .arg(ui->tableTreeWidget->headerItem()->text(column_number_handle), -6)
353 .arg(ui->tableTreeWidget->headerItem()->text(column_number_uuid), -32)
354 .arg(ui->tableTreeWidget->headerItem()->text(column_number_uuid_name));
355
356 while (*i_item) {
357 QTreeWidgetItem *item = static_cast<QTreeWidgetItem*>(*i_item);
358 copy += QString("%1 %2 %3\n")
359 .arg(item->text(column_number_handle), -6)
360 .arg(item->text(column_number_uuid), -32)
361 .arg(item->text(column_number_uuid_name));
362 ++i_item;
363 }
364
365 clipboard->setText(copy);
366 }
367
on_actionSave_as_image_triggered()368 void BluetoothAttServerAttributesDialog::on_actionSave_as_image_triggered()
369 {
370 QPixmap image;
371
372 QString fileName = WiresharkFileDialog::getSaveFileName(this, tr("Save Table Image"),
373 "att_server_attributes_table.png",
374 tr("PNG Image (*.png)"));
375
376 if (fileName.isEmpty()) return;
377
378 image = ui->tableTreeWidget->grab();
379 image.save(fileName, "PNG");
380 }
381
on_buttonBox_clicked(QAbstractButton *)382 void BluetoothAttServerAttributesDialog::on_buttonBox_clicked(QAbstractButton *)
383 {
384 /* if (button == foo_button_) */
385 }
386