extcap: remove conditional compilation.
[metze/wireshark/wip.git] / ui / qt / models / interface_tree_cache_model.cpp
1 /* interface_tree_cache_model.cpp
2  * Model caching interface changes before sending them to global storage
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23
24 #include <ui/qt/models/interface_tree_cache_model.h>
25
26 #include "glib.h"
27
28 #include "epan/prefs.h"
29
30 #include <ui/qt/utils/qt_ui_utils.h>
31 #include "ui/capture_globals.h"
32 #include "wsutil/utf8_entities.h"
33
34 #include "wiretap/wtap.h"
35
36 #include "wireshark_application.h"
37
38 #include <QIdentityProxyModel>
39
40 InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject *parent) :
41     QIdentityProxyModel(parent)
42 {
43     /* ATTENTION: This cache model is not intended to be used with anything
44      * else then InterfaceTreeModel, and will break with anything else
45      * leading to unintended results. */
46     sourceModel = new InterfaceTreeModel(parent);
47
48     QIdentityProxyModel::setSourceModel(sourceModel);
49     storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
50
51     checkableColumns << IFTREE_COL_HIDDEN << IFTREE_COL_PROMISCUOUSMODE;
52 #ifdef HAVE_PCAP_CREATE
53     checkableColumns << IFTREE_COL_MONITOR_MODE;
54 #endif
55
56     editableColumns << IFTREE_COL_INTERFACE_COMMENT << IFTREE_COL_SNAPLEN << IFTREE_COL_PIPE_PATH;
57
58 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
59     editableColumns << IFTREE_COL_BUFFERLEN;
60 #endif
61 }
62
63 InterfaceTreeCacheModel::~InterfaceTreeCacheModel()
64 {
65 #ifdef HAVE_LIBPCAP
66     /* This list should only exist, if the dialog is closed, without calling save first */
67     newDevices.clear();
68 #endif
69
70     delete storage;
71     delete sourceModel;
72 }
73
74 QVariant InterfaceTreeCacheModel::getColumnContent(int idx, int col, int role)
75 {
76     return InterfaceTreeCacheModel::data(index(idx, col), role);
77 }
78
79 #ifdef HAVE_LIBPCAP
80 void InterfaceTreeCacheModel::reset(int row)
81 {
82     if ( row < 0 )
83     {
84         delete storage;
85         storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
86     }
87     else
88     {
89         if ( storage->count() > row )
90             storage->remove(storage->keys().at(row));
91     }
92 }
93
94 void InterfaceTreeCacheModel::saveNewDevices()
95 {
96     QList<interface_t>::const_iterator it = newDevices.constBegin();
97     /* idx is used for iterating only over the indices of the new devices. As all new
98      * devices are stored with an index higher then sourceModel->rowCount(), we start
99      * only with those storage indices.
100      * it is just the iterator over the new devices. A new device must not necessarily
101      * have storage, which will lead to that device not being stored in global_capture_opts */
102     for (int idx = sourceModel->rowCount(); it != newDevices.constEnd(); ++it, idx++)
103     {
104         interface_t *device = const_cast<interface_t *>(&(*it));
105         bool useDevice = false;
106
107         QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
108         /* When devices are being added, they are added using generic values. So only devices
109          * whose data have been changed should be used from here on out. */
110         if ( dataField != 0 )
111         {
112             if ( device->if_info.type != IF_PIPE )
113             {
114                 continue;
115             }
116
117             if ( device->if_info.type == IF_PIPE )
118             {
119                 QVariant saveValue = dataField->value(IFTREE_COL_PIPE_PATH);
120                 if ( saveValue.isValid() )
121                 {
122                     g_free(device->if_info.name);
123                     device->if_info.name = qstring_strdup(saveValue.toString());
124
125                     g_free(device->name);
126                     device->name = qstring_strdup(saveValue.toString());
127
128                     g_free(device->display_name);
129                     device->display_name = qstring_strdup(saveValue.toString());
130                     useDevice = true;
131                 }
132             }
133
134             if ( useDevice )
135                 g_array_append_val(global_capture_opts.all_ifaces, *device);
136
137         }
138
139         /* All entries of this new devices have been considered */
140         storage->remove(idx);
141         delete dataField;
142     }
143
144     newDevices.clear();
145 }
146
147 void InterfaceTreeCacheModel::save()
148 {
149     if ( storage->count() == 0 )
150         return;
151
152     QMap<char**, QStringList> prefStorage;
153
154     /* No devices are hidden until checking "Show" state */
155     prefStorage[&prefs.capture_devices_hide] = QStringList();
156
157     /* Storing new devices first including their changed values */
158     saveNewDevices();
159
160
161     for(unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++)
162     {
163         interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
164
165         if (! device->name )
166             continue;
167
168         /* Try to load a saved value row for this index */
169         QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
170
171         /* Handle the storing of values for this device here */
172         if ( dataField )
173         {
174             QMap<InterfaceTreeColumns, QVariant>::const_iterator it = dataField->constBegin();
175             while ( it != dataField->constEnd() )
176             {
177                 InterfaceTreeColumns col = it.key();
178                 QVariant saveValue = it.value();
179
180                 /* Setting the field values for each individual saved value cannot be generic, as the
181                  * struct cannot be accessed in a generic way. Therefore below, each individually changed
182                  * value has to be handled separately. Comments are stored only in the preference file
183                  * and applied to the data name during loading. Therefore comments are not handled here */
184
185                 if ( col == IFTREE_COL_HIDDEN )
186                 {
187                     device->hidden = saveValue.toBool();
188                 }
189                 else if ( device->if_info.type == IF_EXTCAP )
190                 {
191                     /* extcap interfaces do not have the following columns.
192                      * ATTENTION: all generic columns must be added, BEFORE this
193                      * if-clause, or they will be ignored for extcap interfaces */
194                 }
195                 else if ( col == IFTREE_COL_PROMISCUOUSMODE )
196                 {
197                     device->pmode = saveValue.toBool();
198                 }
199 #ifdef HAVE_PCAP_CREATE
200                 else if ( col == IFTREE_COL_MONITOR_MODE )
201                 {
202                     device->monitor_mode_enabled = saveValue.toBool();
203                 }
204 #endif
205                 else if ( col == IFTREE_COL_SNAPLEN )
206                 {
207                     int iVal = saveValue.toInt();
208                     if ( iVal != WTAP_MAX_PACKET_SIZE_STANDARD )
209                     {
210                         device->has_snaplen = true;
211                         device->snaplen = iVal;
212                     }
213                     else
214                     {
215                         device->has_snaplen = false;
216                         device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
217                     }
218                 }
219 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
220                 else if ( col == IFTREE_COL_BUFFERLEN )
221                 {
222                     device->buffer = saveValue.toInt();
223                 }
224 #endif
225                 ++it;
226             }
227         }
228
229         QVariant content = getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::CheckStateRole);
230         if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Unchecked )
231             prefStorage[&prefs.capture_devices_hide] << QString(device->name);
232
233         content = getColumnContent(idx, IFTREE_COL_INTERFACE_COMMENT);
234         if ( content.isValid() && content.toString().size() > 0 )
235             prefStorage[&prefs.capture_devices_descr] << QString("%1(%2)").arg(device->name).arg(content.toString());
236
237         bool allowExtendedColumns = true;
238
239         if ( device->if_info.type == IF_EXTCAP )
240             allowExtendedColumns = false;
241
242         if ( allowExtendedColumns )
243         {
244             content = getColumnContent(idx, IFTREE_COL_PROMISCUOUSMODE, Qt::CheckStateRole);
245             if ( content.isValid() )
246             {
247                 bool value = static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked;
248                 prefStorage[&prefs.capture_devices_pmode]  << QString("%1(%2)").arg(device->name).arg(value ? 1 : 0);
249             }
250
251 #ifdef HAVE_PCAP_CREATE
252             content = getColumnContent(idx, IFTREE_COL_MONITOR_MODE, Qt::CheckStateRole);
253             if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked )
254                     prefStorage[&prefs.capture_devices_monitor_mode] << QString(device->name);
255 #endif
256
257             content = getColumnContent(idx, IFTREE_COL_SNAPLEN);
258             if ( content.isValid() )
259             {
260                 int value = content.toInt();
261                 prefStorage[&prefs.capture_devices_snaplen]  <<
262                         QString("%1:%2(%3)").arg(device->name).
263                         arg(device->has_snaplen ? 1 : 0).
264                         arg(device->has_snaplen ? value : WTAP_MAX_PACKET_SIZE_STANDARD);
265             }
266
267 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
268             content = getColumnContent(idx, IFTREE_COL_BUFFERLEN);
269             if ( content.isValid() )
270             {
271                 int value = content.toInt();
272                 if ( value != -1 )
273                 {
274                     prefStorage[&prefs.capture_devices_buffersize]  <<
275                             QString("%1(%2)").arg(device->name).
276                             arg(value);
277                 }
278             }
279 #endif
280         }
281     }
282
283     QMap<char**, QStringList>::const_iterator it = prefStorage.constBegin();
284     while ( it != prefStorage.constEnd() )
285     {
286         char ** key = it.key();
287
288         g_free(*key);
289         *key = qstring_strdup(it.value().join(","));
290
291         ++it;
292     }
293
294     wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
295 }
296 #endif
297
298 int InterfaceTreeCacheModel::rowCount(const QModelIndex & parent) const
299 {
300     int totalCount = sourceModel->rowCount(parent);
301 #ifdef HAVE_LIBPCAP
302     totalCount += newDevices.size();
303 #endif
304     return totalCount;
305 }
306
307 bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col) const
308 {
309     if ( editableColumns.contains(col) || checkableColumns.contains(col) )
310         return true;
311     return false;
312 }
313
314 #ifdef HAVE_LIBPCAP
315 const interface_t * InterfaceTreeCacheModel::lookup(const QModelIndex &index) const
316 {
317     const interface_t * result = 0;
318
319     if ( ! index.isValid() )
320         return result;
321     if ( ! global_capture_opts.all_ifaces && newDevices.size() == 0 )
322         return result;
323
324     int idx = index.row();
325
326     if ( (unsigned int) idx >= global_capture_opts.all_ifaces->len )
327     {
328         idx = idx - global_capture_opts.all_ifaces->len;
329         if ( idx < newDevices.size() )
330             result = &newDevices[idx];
331     }
332     else
333     {
334         result = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
335     }
336
337     return result;
338 }
339 #endif
340
341 /* This checks if the column can be edited for the given index. This differs from
342  * isAvailableField in such a way, that it is only used in flags and not any
343  * other method.*/
344 bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex &index) const
345 {
346 #ifndef HAVE_LIBPCAP
347     Q_UNUSED(index);
348 #else
349     const interface_t * device = lookup(index);
350     if ( device == 0 )
351         return false;
352
353     InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
354     if ( device->if_info.type == IF_EXTCAP )
355     {
356         /* extcap interfaces do not have those settings */
357         if ( col == IFTREE_COL_PROMISCUOUSMODE || col == IFTREE_COL_SNAPLEN )
358             return false;
359 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
360         if ( col == IFTREE_COL_BUFFERLEN )
361             return false;
362 #endif
363     }
364 #endif
365     return true;
366 }
367
368 // Whether this field is available for modification and display.
369 bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex &index) const
370 {
371 #ifndef HAVE_LIBPCAP
372     Q_UNUSED(index);
373 #else
374     const interface_t * device = lookup(index);
375
376     if ( device == 0 )
377         return false;
378
379     InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
380     if ( col == IFTREE_COL_HIDDEN )
381     {
382         // Do not allow default capture interface to be hidden.
383         if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
384             return false;
385     }
386 #endif
387
388     return true;
389 }
390
391 Qt::ItemFlags InterfaceTreeCacheModel::flags(const QModelIndex &index) const
392 {
393     if ( ! index.isValid() )
394         return 0;
395
396     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
397
398     InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
399
400     if ( changeIsAllowed(col) && isAvailableField(index) && isAllowedToBeEdited(index) )
401     {
402         if ( checkableColumns.contains(col) )
403         {
404             flags |= Qt::ItemIsUserCheckable;
405         }
406         else
407         {
408             flags |= Qt::ItemIsEditable;
409         }
410     }
411
412     return flags;
413 }
414
415 bool InterfaceTreeCacheModel::setData(const QModelIndex &index, const QVariant &value, int role)
416 {
417     if ( ! index.isValid() )
418         return false;
419
420     if ( ! isAvailableField(index) )
421         return false;
422
423     int row = index.row();
424     InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
425
426     if ( role == Qt::CheckStateRole || role == Qt::EditRole )
427     {
428         if ( changeIsAllowed( col ) )
429         {
430             QVariant saveValue = value;
431
432             QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
433             /* obtain the list of already stored changes for this row. If none exist
434              * create a new storage row for this entry */
435             if ( ( dataField = storage->value(row, 0) ) == 0 )
436             {
437                 dataField = new QMap<InterfaceTreeColumns, QVariant>();
438                 storage->insert(row, dataField);
439             }
440
441             dataField->insert(col, saveValue);
442
443             return true;
444         }
445     }
446
447     return false;
448 }
449
450 QVariant InterfaceTreeCacheModel::data(const QModelIndex &index, int role) const
451 {
452     if ( ! index.isValid() )
453         return QVariant();
454
455     int row = index.row();
456
457     InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
458
459     if ( isAvailableField(index) && isAllowedToBeEdited(index) )
460     {
461         if ( ( ( role == Qt::DisplayRole || role == Qt::EditRole ) && editableColumns.contains(col) ) ||
462                 ( role == Qt::CheckStateRole && checkableColumns.contains(col) ) )
463         {
464             QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
465             if ( ( dataField = storage->value(row, 0) ) != 0 )
466             {
467                 if ( dataField->contains(col) )
468                 {
469                     return dataField->value(col, QVariant());
470                 }
471             }
472         }
473     }
474     else
475     {
476         if ( role == Qt::CheckStateRole )
477             return QVariant();
478         else if ( role == Qt::DisplayRole )
479             return QString(UTF8_EM_DASH);
480     }
481
482     if ( row < sourceModel->rowCount() )
483     {
484         return sourceModel->data(index, role);
485     }
486 #ifdef HAVE_LIBPCAP
487     else
488     {
489         /* Handle all fields, which will have to be displayed for new devices. Only pipes
490          * are supported at the moment, so the information to be displayed is pretty limited.
491          * After saving, the devices are stored in global_capture_opts and no longer
492          * classify as new devices. */
493         const interface_t * device = lookup(index);
494
495         if ( device != 0 )
496         {
497             if ( role == Qt::DisplayRole || role == Qt::EditRole )
498             {
499                 if ( col == IFTREE_COL_PIPE_PATH ||
500                         col == IFTREE_COL_NAME ||
501                         col == IFTREE_COL_INTERFACE_NAME )
502                 {
503
504                     QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
505                     if ( ( dataField = storage->value(row, 0) ) != 0 &&
506                             dataField->contains(IFTREE_COL_PIPE_PATH) )
507                     {
508                         return dataField->value(IFTREE_COL_PIPE_PATH, QVariant());
509                     }
510                     else
511                         return QString(device->name);
512                 }
513                 else if ( col == IFTREE_COL_TYPE )
514                 {
515                     return QVariant::fromValue((int)device->if_info.type);
516                 }
517             }
518             else if ( role == Qt::CheckStateRole )
519             {
520                 if ( col == IFTREE_COL_HIDDEN )
521                 {
522                     // Do not allow default capture interface to be hidden.
523                     if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
524                         return QVariant();
525
526                     /* Hidden is a de-selection, therefore inverted logic here */
527                     return device->hidden ? Qt::Unchecked : Qt::Checked;
528                 }
529             }
530         }
531     }
532 #endif
533
534     return QVariant();
535 }
536
537 #ifdef HAVE_LIBPCAP
538 QModelIndex InterfaceTreeCacheModel::index(int row, int column, const QModelIndex &parent) const
539 {
540     if ( row >= sourceModel->rowCount() && ( row - sourceModel->rowCount() ) < newDevices.count() )
541     {
542         return createIndex(row, column, (void *)0);
543     }
544
545     return sourceModel->index(row, column, parent);
546 }
547
548 void InterfaceTreeCacheModel::addDevice(const interface_t * newDevice)
549 {
550     emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
551     newDevices << *newDevice;
552     emit endInsertRows();
553 }
554
555 void InterfaceTreeCacheModel::deleteDevice(const QModelIndex &index)
556 {
557     if ( ! index.isValid() )
558         return;
559
560     emit beginRemoveRows(QModelIndex(), index.row(), index.row());
561
562     int row = index.row();
563
564     /* device is in newDevices */
565     if ( row >= sourceModel->rowCount() )
566     {
567         int newDeviceIdx = row - sourceModel->rowCount();
568
569         newDevices.removeAt(newDeviceIdx);
570         if ( storage->contains(index.row()) )
571             storage->remove(index.row());
572
573         /* The storage array has to be resorted, if the index, that was removed
574          * had been in the middle of the array. Can't start at index.row(), as
575          * it may not be contained in storage
576          * We must iterate using a list, not an iterator, otherwise the change
577          * will fold on itself. */
578         QList<int> storageKeys = storage->keys();
579         for ( int i = 0; i < storageKeys.size(); ++i )
580         {
581             int key = storageKeys.at(i);
582             if ( key > index.row() )
583             {
584                 storage->insert(key - 1, storage->value(key));
585                 storage->remove(key);
586             }
587         }
588
589         emit endRemoveRows();
590     }
591     else
592     {
593         interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, row);
594         capture_opts_free_interface_t(device);
595         global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, row);
596         emit endRemoveRows();
597         wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
598     }
599 }
600 #endif
601
602 /*
603  * Editor modelines
604  *
605  * Local Variables:
606  * c-basic-offset: 4
607  * tab-width: 8
608  * indent-tabs-mode: nil
609  * End:
610  *
611  * ex: set shiftwidth=4 tabstop=8 expandtab:
612  * :indentSize=4:tabSize=8:noTabs=true:
613  */