1 /* interface_tree_cache_model.cpp
2 * Model caching interface changes before sending them to global storage
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <ui/qt/models/interface_tree_cache_model.h>
14 #include "epan/prefs.h"
16 #include <ui/qt/utils/qt_ui_utils.h>
17 #include "ui/capture_globals.h"
18 #include "wsutil/utf8_entities.h"
20 #include "wiretap/wtap.h"
22 #include "main_application.h"
24 #include <QIdentityProxyModel>
26 InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject
*parent
) :
27 QIdentityProxyModel(parent
)
29 /* ATTENTION: This cache model is not intended to be used with anything
30 * else then InterfaceTreeModel, and will break with anything else
31 * leading to unintended results. */
32 sourceModel
= new InterfaceTreeModel(parent
);
34 QIdentityProxyModel::setSourceModel(sourceModel
);
35 storage
= new QMap
<int, QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > >();
37 checkableColumns
<< IFTREE_COL_HIDDEN
<< IFTREE_COL_PROMISCUOUSMODE
;
38 #ifdef HAVE_PCAP_CREATE
39 checkableColumns
<< IFTREE_COL_MONITOR_MODE
;
42 editableColumns
<< IFTREE_COL_COMMENT
<< IFTREE_COL_SNAPLEN
<< IFTREE_COL_PIPE_PATH
;
44 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
45 editableColumns
<< IFTREE_COL_BUFFERLEN
;
49 InterfaceTreeCacheModel::~InterfaceTreeCacheModel()
52 /* This list should only exist, if the dialog is closed, without calling save first */
60 QVariant
InterfaceTreeCacheModel::getColumnContent(int idx
, int col
, int role
)
62 return InterfaceTreeCacheModel::data(index(idx
, col
), role
);
66 void InterfaceTreeCacheModel::reset(int row
)
74 if (storage
->count() > row
)
75 storage
->remove(storage
->keys().at(row
));
79 void InterfaceTreeCacheModel::saveNewDevices()
81 QList
<interface_t
>::const_iterator it
= newDevices
.constBegin();
82 /* idx is used for iterating only over the indices of the new devices. As all new
83 * devices are stored with an index higher then sourceModel->rowCount(), we start
84 * only with those storage indices.
85 * it is just the iterator over the new devices. A new device must not necessarily
86 * have storage, which will lead to that device not being stored in global_capture_opts */
87 for (int idx
= sourceModel
->rowCount(); it
!= newDevices
.constEnd(); ++it
, idx
++)
89 interface_t
*device
= const_cast<interface_t
*>(&(*it
));
90 bool useDevice
= false;
92 QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > dataField
= storage
->value(idx
, 0);
93 /* When devices are being added, they are added using generic values. So only devices
94 * whose data have been changed should be used from here on out. */
97 if (device
->if_info
.type
!= IF_PIPE
)
102 if (device
->if_info
.type
== IF_PIPE
)
104 QVariant saveValue
= dataField
->value(IFTREE_COL_PIPE_PATH
);
105 if (saveValue
.isValid())
107 g_free(device
->if_info
.name
);
108 device
->if_info
.name
= qstring_strdup(saveValue
.toString());
110 g_free(device
->name
);
111 device
->name
= qstring_strdup(saveValue
.toString());
113 g_free(device
->display_name
);
114 device
->display_name
= qstring_strdup(saveValue
.toString());
120 g_array_append_val(global_capture_opts
.all_ifaces
, *device
);
124 /* All entries of this new devices have been considered */
125 storage
->remove(idx
);
131 void InterfaceTreeCacheModel::save()
133 if (storage
->count() == 0)
136 QMap
<char**, QStringList
> prefStorage
;
138 /* No devices are hidden until checking "Show" state */
139 prefStorage
[&prefs
.capture_devices_hide
] = QStringList();
141 /* Some of the columns we only add entries to the QStringList for
142 * interfaces that have a non-default value, so we need to ensure
143 * that we set the pref string to empty if no interface is set.
145 prefStorage
[&prefs
.capture_devices_descr
] = QStringList();
146 prefStorage
[&prefs
.capture_devices_monitor_mode
] << QStringList();
148 /* Storing new devices first including their changed values */
152 for (unsigned int idx
= 0; idx
< global_capture_opts
.all_ifaces
->len
; idx
++)
154 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
159 /* Try to load a saved value row for this index */
160 QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > dataField
= storage
->value(idx
, 0);
162 /* Handle the storing of values for this device here */
165 QMap
<InterfaceTreeColumns
, QVariant
>::const_iterator it
= dataField
->constBegin();
166 while (it
!= dataField
->constEnd())
168 InterfaceTreeColumns col
= it
.key();
169 QVariant saveValue
= it
.value();
171 /* Setting the field values for each individual saved value cannot be generic, as the
172 * struct cannot be accessed in a generic way. Therefore below, each individually changed
173 * value has to be handled separately. Comments are stored only in the preference file
174 * and applied to the data name during loading. Therefore comments are not handled here */
176 if (col
== IFTREE_COL_HIDDEN
)
178 /* Hidden is de-selection, therefore inverted logic here */
179 device
->hidden
= (saveValue
== Qt::Unchecked
);
181 else if (device
->if_info
.type
== IF_EXTCAP
)
183 /* extcap interfaces do not have the following columns.
184 * ATTENTION: all generic columns must be added, BEFORE this
185 * if-clause, or they will be ignored for extcap interfaces */
187 else if (col
== IFTREE_COL_PROMISCUOUSMODE
)
189 device
->pmode
= saveValue
.toBool();
191 #ifdef HAVE_PCAP_CREATE
192 else if (col
== IFTREE_COL_MONITOR_MODE
)
194 device
->monitor_mode_enabled
= saveValue
.toBool();
197 else if (col
== IFTREE_COL_SNAPLEN
)
199 int iVal
= saveValue
.toInt();
200 if (iVal
!= WTAP_MAX_PACKET_SIZE_STANDARD
)
202 device
->has_snaplen
= true;
203 device
->snaplen
= iVal
;
207 device
->has_snaplen
= false;
208 device
->snaplen
= WTAP_MAX_PACKET_SIZE_STANDARD
;
211 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
212 else if (col
== IFTREE_COL_BUFFERLEN
)
214 device
->buffer
= saveValue
.toInt();
221 QVariant content
= getColumnContent(idx
, IFTREE_COL_HIDDEN
, Qt::CheckStateRole
);
222 if (content
.isValid() && static_cast<Qt::CheckState
>(content
.toInt()) == Qt::Unchecked
)
223 prefStorage
[&prefs
.capture_devices_hide
] << QString(device
->name
);
225 content
= getColumnContent(idx
, IFTREE_COL_COMMENT
);
226 if (content
.isValid() && content
.toString().size() > 0)
227 prefStorage
[&prefs
.capture_devices_descr
] << QStringLiteral("%1(%2)").arg(device
->name
).arg(content
.toString());
229 bool allowExtendedColumns
= true;
231 if (device
->if_info
.type
== IF_EXTCAP
)
232 allowExtendedColumns
= false;
234 if (allowExtendedColumns
)
236 content
= getColumnContent(idx
, IFTREE_COL_PROMISCUOUSMODE
, Qt::CheckStateRole
);
237 if (content
.isValid())
239 bool value
= static_cast<Qt::CheckState
>(content
.toInt()) == Qt::Checked
;
240 prefStorage
[&prefs
.capture_devices_pmode
] << QStringLiteral("%1(%2)").arg(device
->name
).arg(value
? 1 : 0);
243 #ifdef HAVE_PCAP_CREATE
244 content
= getColumnContent(idx
, IFTREE_COL_MONITOR_MODE
, Qt::CheckStateRole
);
245 if (content
.isValid() && static_cast<Qt::CheckState
>(content
.toInt()) == Qt::Checked
)
246 prefStorage
[&prefs
.capture_devices_monitor_mode
] << QString(device
->name
);
249 content
= getColumnContent(idx
, IFTREE_COL_SNAPLEN
);
250 if (content
.isValid())
252 int value
= content
.toInt();
253 prefStorage
[&prefs
.capture_devices_snaplen
] <<
254 QStringLiteral("%1:%2(%3)").arg(device
->name
).
255 arg(device
->has_snaplen
? 1 : 0).
256 arg(device
->has_snaplen
? value
: WTAP_MAX_PACKET_SIZE_STANDARD
);
259 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
260 content
= getColumnContent(idx
, IFTREE_COL_BUFFERLEN
);
261 if (content
.isValid())
263 int value
= content
.toInt();
266 prefStorage
[&prefs
.capture_devices_buffersize
] <<
267 QStringLiteral("%1(%2)").arg(device
->name
).
275 QMap
<char**, QStringList
>::const_iterator it
= prefStorage
.constBegin();
276 while (it
!= prefStorage
.constEnd())
278 char ** key
= it
.key();
281 *key
= qstring_strdup(it
.value().join(","));
286 mainApp
->emitAppSignal(MainApplication::LocalInterfacesChanged
);
290 int InterfaceTreeCacheModel::rowCount(const QModelIndex
& parent
) const
292 int totalCount
= sourceModel
->rowCount(parent
);
294 totalCount
+= newDevices
.size();
299 bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col
) const
301 if (editableColumns
.contains(col
) || checkableColumns
.contains(col
))
307 const interface_t
* InterfaceTreeCacheModel::lookup(const QModelIndex
&index
) const
309 const interface_t
* result
= 0;
311 if (! index
.isValid() || ! global_capture_opts
.all_ifaces
)
314 int idx
= index
.row();
316 if ((unsigned int) idx
>= global_capture_opts
.all_ifaces
->len
)
318 idx
= idx
- global_capture_opts
.all_ifaces
->len
;
319 if (idx
< newDevices
.size())
320 result
= &newDevices
[idx
];
324 result
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
331 /* This checks if the column can be edited for the given index. This differs from
332 * isAvailableField in such a way, that it is only used in flags and not any
334 bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex
&index
) const
339 const interface_t
* device
= lookup(index
);
343 InterfaceTreeColumns col
= (InterfaceTreeColumns
) index
.column();
344 if (device
->if_info
.type
== IF_EXTCAP
)
346 /* extcap interfaces do not have those settings */
347 if (col
== IFTREE_COL_PROMISCUOUSMODE
|| col
== IFTREE_COL_SNAPLEN
)
349 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
350 if (col
== IFTREE_COL_BUFFERLEN
)
358 // Whether this field is available for modification and display.
359 bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex
&index
) const
364 const interface_t
* device
= lookup(index
);
369 InterfaceTreeColumns col
= (InterfaceTreeColumns
) index
.column();
370 if (col
== IFTREE_COL_HIDDEN
)
372 // Do not allow default capture interface to be hidden.
373 if (! g_strcmp0(prefs
.capture_device
, device
->display_name
))
381 Qt::ItemFlags
InterfaceTreeCacheModel::flags(const QModelIndex
&index
) const
383 if (! index
.isValid())
384 return Qt::ItemFlags();
386 Qt::ItemFlags flags
= Qt::ItemIsEnabled
| Qt::ItemIsSelectable
;
388 InterfaceTreeColumns col
= (InterfaceTreeColumns
) index
.column();
390 if (changeIsAllowed(col
) && isAvailableField(index
) && isAllowedToBeEdited(index
))
392 if (checkableColumns
.contains(col
))
394 flags
|= Qt::ItemIsUserCheckable
;
398 flags
|= Qt::ItemIsEditable
;
405 bool InterfaceTreeCacheModel::setData(const QModelIndex
&index
, const QVariant
&value
, int role
)
407 if (! index
.isValid())
410 if (! isAvailableField(index
))
413 int row
= index
.row();
414 InterfaceTreeColumns col
= (InterfaceTreeColumns
)index
.column();
416 if (role
== Qt::CheckStateRole
|| role
== Qt::EditRole
)
418 if (changeIsAllowed(col
) )
420 QVariant saveValue
= value
;
422 QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > dataField
= nullptr;
423 /* obtain the list of already stored changes for this row. If none exist
424 * create a new storage row for this entry */
425 if ((dataField
= storage
->value(row
, 0)) == nullptr)
427 dataField
= QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> >(new QMap
<InterfaceTreeColumns
, QVariant
>);
428 storage
->insert(row
, dataField
);
431 dataField
->insert(col
, saveValue
);
440 QVariant
InterfaceTreeCacheModel::data(const QModelIndex
&index
, int role
) const
442 if (! index
.isValid())
445 int row
= index
.row();
447 InterfaceTreeColumns col
= (InterfaceTreeColumns
)index
.column();
449 if (isAvailableField(index
) && isAllowedToBeEdited(index
))
451 if (((role
== Qt::DisplayRole
|| role
== Qt::EditRole
) && editableColumns
.contains(col
)) ||
452 (role
== Qt::CheckStateRole
&& checkableColumns
.contains(col
)) )
454 QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > dataField
= nullptr;
455 if ((dataField
= storage
->value(row
, 0)) != nullptr)
457 if (dataField
->contains(col
))
459 return dataField
->value(col
, QVariant());
466 if (role
== Qt::CheckStateRole
)
468 else if (role
== Qt::DisplayRole
)
469 return QString(UTF8_EM_DASH
);
472 if (row
< sourceModel
->rowCount())
474 return sourceModel
->data(index
, role
);
479 /* Handle all fields, which will have to be displayed for new devices. Only pipes
480 * are supported at the moment, so the information to be displayed is pretty limited.
481 * After saving, the devices are stored in global_capture_opts and no longer
482 * classify as new devices. */
483 const interface_t
* device
= lookup(index
);
487 if (role
== Qt::DisplayRole
|| role
== Qt::EditRole
)
489 if (col
== IFTREE_COL_PIPE_PATH
||
490 col
== IFTREE_COL_NAME
||
491 col
== IFTREE_COL_DESCRIPTION
)
494 QSharedPointer
<QMap
<InterfaceTreeColumns
, QVariant
> > dataField
= nullptr;
495 if ((dataField
= storage
->value(row
, 0)) != nullptr &&
496 dataField
->contains(IFTREE_COL_PIPE_PATH
))
498 return dataField
->value(IFTREE_COL_PIPE_PATH
, QVariant());
501 return QString(device
->name
);
503 else if (col
== IFTREE_COL_TYPE
)
505 return QVariant::fromValue((int)device
->if_info
.type
);
508 else if (role
== Qt::CheckStateRole
)
510 if (col
== IFTREE_COL_HIDDEN
)
512 // Do not allow default capture interface to be hidden.
513 if (! g_strcmp0(prefs
.capture_device
, device
->display_name
))
516 /* Hidden is a de-selection, therefore inverted logic here */
517 return device
->hidden
? Qt::Unchecked
: Qt::Checked
;
527 #ifdef HAVE_PCAP_REMOTE
528 bool InterfaceTreeCacheModel::isRemote(const QModelIndex
&index
) const
530 const interface_t
*device
= lookup(index
);
531 if (device
!= nullptr && device
->remote_opts
.src_type
== CAPTURE_IFREMOTE
) {
539 QModelIndex
InterfaceTreeCacheModel::index(int row
, int column
, const QModelIndex
&parent
) const
541 if (row
>= sourceModel
->rowCount() && (row
- sourceModel
->rowCount()) < newDevices
.count())
543 return createIndex(row
, column
, (void *)0);
546 return QIdentityProxyModel::index(row
, column
, parent
);
549 void InterfaceTreeCacheModel::addDevice(const interface_t
* newDevice
)
551 emit
beginInsertRows(QModelIndex(), rowCount(), rowCount());
552 newDevices
<< *newDevice
;
553 emit
endInsertRows();
556 void InterfaceTreeCacheModel::deleteDevice(const QModelIndex
&index
)
558 if (! index
.isValid())
561 emit
beginRemoveRows(QModelIndex(), index
.row(), index
.row());
563 int row
= index
.row();
565 /* device is in newDevices */
566 if (row
>= sourceModel
->rowCount())
568 int newDeviceIdx
= row
- sourceModel
->rowCount();
570 newDevices
.removeAt(newDeviceIdx
);
571 if (storage
->contains(index
.row()))
572 storage
->remove(index
.row());
574 /* The storage array has to be resorted, if the index, that was removed
575 * had been in the middle of the array. Can't start at index.row(), as
576 * it may not be contained in storage
577 * We must iterate using a list, not an iterator, otherwise the change
578 * will fold on itself. */
579 QList
<int> storageKeys
= storage
->keys();
580 for (int i
= 0; i
< storageKeys
.size(); ++i
)
582 int key
= storageKeys
.at(i
);
583 if (key
> index
.row())
585 storage
->insert(key
- 1, storage
->value(key
));
586 storage
->remove(key
);
590 emit
endRemoveRows();
594 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, row
);
595 capture_opts_free_interface_t(device
);
596 global_capture_opts
.all_ifaces
= g_array_remove_index(global_capture_opts
.all_ifaces
, row
);
597 emit
endRemoveRows();
598 mainApp
->emitAppSignal(MainApplication::LocalInterfacesChanged
);