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