1 /* capture_options_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 "config.h"
11 
12 #include <glib.h>
13 
14 #include "capture_options_dialog.h"
15 #include <ui/qt/widgets/capture_filter_combo.h>
16 #include <ui_capture_options_dialog.h>
17 #include "compiled_filter_output.h"
18 #include "manage_interfaces_dialog.h"
19 
20 #include "wireshark_application.h"
21 
22 #ifdef HAVE_LIBPCAP
23 
24 #include <QAbstractItemModel>
25 #include <QMessageBox>
26 #include <QTimer>
27 
28 #include "ringbuffer.h"
29 #include "ui/capture_ui_utils.h"
30 #include "ui/capture_globals.h"
31 #include "ui/iface_lists.h"
32 #include "ui/last_open_dir.h"
33 
34 #include "ui/ws_ui_util.h"
35 #include "ui/util.h"
36 #include <wsutil/utf8_entities.h>
37 #include "ui/preference_utils.h"
38 
39 #include <cstdio>
40 #include <epan/prefs.h>
41 #include <epan/prefs-int.h>
42 #include <epan/addr_resolv.h>
43 #include <wsutil/filesystem.h>
44 
45 #include <wiretap/wtap.h>
46 
47 #include <ui/qt/utils/qt_ui_utils.h>
48 #include <ui/qt/models/sparkline_delegate.h>
49 #include "ui/qt/widgets/wireshark_file_dialog.h"
50 
51 // To do:
52 // - Set a size hint for item delegates.
53 // - Make promiscuous and monitor mode checkboxes.
54 // - Fix InterfaceTreeDelegate method names.
55 // - You can edit filters via the main CaptureFilterCombo and via each
56 //   individual interface row. We should probably do one or the other.
57 
58 const int stat_update_interval_ = 1000; // ms
59 
60 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
61 #define SHOW_BUFFER_COLUMN 1
62 #endif
63 
64 #if defined(HAVE_PCAP_CREATE)
65 #define SHOW_MONITOR_COLUMN 1
66 #endif
67 
68 /*
69  * Symbolic names for column indices.
70  */
71 enum
72 {
73     col_interface_ = 0,
74     col_traffic_,
75     col_link_,
76     col_pmode_,
77     col_snaplen_,
78     col_buffer_,
79     col_monitor_,
80     col_filter_,
81     col_num_columns_
82 };
83 
find_device_by_if_name(const QString & interface_name)84 static interface_t *find_device_by_if_name(const QString &interface_name)
85 {
86     interface_t *device;
87     guint i;
88     for (i = 0; i < global_capture_opts.all_ifaces->len; i++) {
89         device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
90         if (!interface_name.compare(device->display_name) && !device->hidden && device->type != IF_PIPE) {
91             return device;
92         }
93     }
94     return NULL;
95 }
96 
97 class InterfaceTreeWidgetItem : public QTreeWidgetItem
98 {
99 public:
InterfaceTreeWidgetItem(QTreeWidget * tree)100     InterfaceTreeWidgetItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {}
101     bool operator< (const QTreeWidgetItem &other) const;
102     QVariant data(int column, int role) const;
103     void setData(int column, int role, const QVariant &value);
104     QList<int> points;
105 
updateInterfaceColumns(interface_t * device)106     void updateInterfaceColumns(interface_t *device)
107     {
108         if (!device) return;
109 
110         QString default_str = QObject::tr("default");
111 
112         // XXX - this is duplicated in InterfaceTreeModel::data;
113         // it should be done in common code somewhere.
114         QString linkname;
115         if (device->active_dlt == -1)
116             linkname = "Unknown";
117         else {
118             linkname = QObject::tr("DLT %1").arg(device->active_dlt);
119             for (GList *list = device->links; list != NULL; list = gxx_list_next(list)) {
120                 link_row *linkr = gxx_list_data(link_row *, list);
121                 if (linkr->dlt == device->active_dlt) {
122                     linkname = linkr->name;
123                     break;
124                 }
125             }
126         }
127         setText(col_link_, linkname);
128 
129         if (device->if_info.type == IF_EXTCAP) {
130             /* extcap interfaces does not have this settings */
131             setApplicable(col_pmode_, false);
132 
133             setApplicable(col_snaplen_, false);
134 #ifdef SHOW_BUFFER_COLUMN
135             setApplicable(col_buffer_, false);
136 #endif
137         } else {
138             setApplicable(col_pmode_, true);
139             setCheckState(col_pmode_, device->pmode ? Qt::Checked : Qt::Unchecked);
140 
141             QString snaplen_string = device->has_snaplen ? QString::number(device->snaplen) : default_str;
142             setText(col_snaplen_, snaplen_string);
143 #ifdef SHOW_BUFFER_COLUMN
144             setText(col_buffer_, QString::number(device->buffer));
145 #endif
146         }
147         setText(col_filter_, device->cfilter);
148 
149 #ifdef SHOW_MONITOR_COLUMN
150         if (device->monitor_mode_supported) {
151             setApplicable(col_monitor_, true);
152             setCheckState(col_monitor_, device->monitor_mode_enabled ? Qt::Checked : Qt::Unchecked);
153         } else {
154             setApplicable(col_monitor_, false);
155         }
156 #endif
157     }
158 
setApplicable(int column,bool applicable=false)159     void setApplicable(int column, bool applicable = false) {
160         QPalette palette = wsApp->palette();
161 
162         if (applicable) {
163             setText(column, QString());
164         } else {
165             setData(column, Qt::CheckStateRole, QVariant());
166             palette.setCurrentColorGroup(QPalette::Disabled);
167             setText(column, UTF8_EM_DASH);
168         }
169         setForeground(column, palette.text().color());
170     }
171 
172 };
173 
CaptureOptionsDialog(QWidget * parent)174 CaptureOptionsDialog::CaptureOptionsDialog(QWidget *parent) :
175     GeometryStateDialog(parent),
176     ui(new Ui::CaptureOptionsDialog)
177 {
178     ui->setupUi(this);
179     loadGeometry();
180     setWindowTitle(wsApp->windowTitleString(tr("Capture Options")));
181 
182     stat_timer_ = NULL;
183     stat_cache_ = NULL;
184 
185     ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start"));
186 
187     // Start out with the list *not* sorted, so they show up in the order
188     // in which they were provided
189     ui->interfaceTree->sortByColumn(-1, Qt::AscendingOrder);
190     ui->interfaceTree->setItemDelegateForColumn(col_interface_, &interface_item_delegate_);
191     ui->interfaceTree->setItemDelegateForColumn(col_traffic_, new SparkLineDelegate(this));
192     ui->interfaceTree->setItemDelegateForColumn(col_link_, &interface_item_delegate_);
193 
194     ui->interfaceTree->setItemDelegateForColumn(col_snaplen_, &interface_item_delegate_);
195 #ifdef SHOW_BUFFER_COLUMN
196     ui->interfaceTree->setItemDelegateForColumn(col_buffer_, &interface_item_delegate_);
197 #else
198     ui->interfaceTree->setColumnHidden(col_buffer_, true);
199 #endif
200 #ifndef SHOW_MONITOR_COLUMN
201     ui->interfaceTree->setColumnHidden(col_monitor_, true);
202 #endif
203     ui->interfaceTree->setItemDelegateForColumn(col_filter_, &interface_item_delegate_);
204 
205     interface_item_delegate_.setTree(ui->interfaceTree);
206 
207     ui->filenameLineEdit->setPlaceholderText(tr("Leave blank to use a temporary file"));
208 
209     ui->rbCompressionNone->setChecked(true);
210 
211     // Changes in interface selections or capture filters should be propagated
212     // to the main welcome screen where they will be applied to the global
213     // capture options.
214     connect(this, SIGNAL(interfacesChanged()), ui->captureFilterComboBox, SIGNAL(interfacesChanged()));
215     connect(ui->captureFilterComboBox, SIGNAL(captureFilterSyntaxChanged(bool)), this, SLOT(updateWidgets()));
216     connect(ui->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)),
217             this, SLOT(filterEdited()));
218     connect(ui->captureFilterComboBox->lineEdit(), SIGNAL(textEdited(QString)),
219             this, SIGNAL(captureFilterTextEdited(QString)));
220     connect(&interface_item_delegate_, SIGNAL(filterChanged(QString)),
221             ui->captureFilterComboBox->lineEdit(), SLOT(setText(QString)));
222     connect(&interface_item_delegate_, SIGNAL(filterChanged(QString)),
223             this, SIGNAL(captureFilterTextEdited(QString)));
224     connect(this, SIGNAL(ifsChanged()), this, SLOT(refreshInterfaceList()));
225     connect(wsApp, SIGNAL(localInterfaceListChanged()), this, SLOT(updateLocalInterfaces()));
226     connect(ui->browseButton, SIGNAL(clicked()), this, SLOT(browseButtonClicked()));
227 
228     updateWidgets();
229 }
230 
231 /* Update global device selections based on the TreeWidget selection. */
updateGlobalDeviceSelections()232 void CaptureOptionsDialog::updateGlobalDeviceSelections()
233 {
234 #ifdef HAVE_LIBPCAP
235     QTreeWidgetItemIterator iter(ui->interfaceTree);
236 
237     global_capture_opts.num_selected = 0;
238 
239     while (*iter) {
240         QString device_name = (*iter)->data(col_interface_, Qt::UserRole).value<QString>();
241         for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
242             interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
243             if (device_name.compare(QString().fromUtf8(device->name)) == 0) {
244                 if ((*iter)->isSelected()) {
245                     device->selected = TRUE;
246                     global_capture_opts.num_selected++;
247                 } else {
248                     device->selected = FALSE;
249                 }
250                 break;
251             }
252         }
253         ++iter;
254     }
255 #endif
256 }
257 
258 /* Update TreeWidget selection based on global device selections. */
updateFromGlobalDeviceSelections()259 void CaptureOptionsDialog::updateFromGlobalDeviceSelections()
260 {
261 #ifdef HAVE_LIBPCAP
262     QTreeWidgetItemIterator iter(ui->interfaceTree);
263 
264     // Prevent recursive interface interfaceSelected signals
265     ui->interfaceTree->blockSignals(true);
266 
267     while (*iter) {
268         QString device_name = (*iter)->data(col_interface_, Qt::UserRole).value<QString>();
269         for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
270             interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
271             if (device_name.compare(QString().fromUtf8(device->name)) == 0) {
272                 if ((bool)device->selected != (*iter)->isSelected()) {
273                     (*iter)->setSelected(device->selected);
274                 }
275                 break;
276             }
277         }
278         ++iter;
279     }
280 
281     ui->interfaceTree->blockSignals(false);
282 #endif
283 }
284 
interfaceSelected()285 void CaptureOptionsDialog::interfaceSelected()
286 {
287     if (sender() == ui->interfaceTree) {
288         // Local changes, propagate our changes
289         updateGlobalDeviceSelections();
290         emit interfacesChanged();
291     } else {
292         // Changes from the welcome screen, adjust to its state.
293         updateFromGlobalDeviceSelections();
294     }
295 
296     updateSelectedFilter();
297 
298     updateWidgets();
299 }
300 
filterEdited()301 void CaptureOptionsDialog::filterEdited()
302 {
303     QList<QTreeWidgetItem*> si = ui->interfaceTree->selectedItems();
304 
305     foreach (QTreeWidgetItem *ti, si) {
306         ti->setText(col_filter_, ui->captureFilterComboBox->lineEdit()->text());
307     }
308 
309     if (si.count() > 0) {
310         QModelIndex col_filter_idx = ui->interfaceTree->model()->index(ui->interfaceTree->indexOfTopLevelItem(si[0]), col_filter_);
311         ui->interfaceTree->scrollTo(col_filter_idx);
312     }
313 }
314 
updateWidgets()315 void CaptureOptionsDialog::updateWidgets()
316 {
317     SyntaxLineEdit *sle = qobject_cast<SyntaxLineEdit *>(ui->captureFilterComboBox->lineEdit());
318     if (!sle) {
319         return;
320     }
321 
322     bool can_capture = false;
323 
324     if (ui->interfaceTree->selectedItems().count() > 0 && sle->syntaxState() != SyntaxLineEdit::Invalid) {
325         can_capture = true;
326     }
327 
328     ui->compileBPF->setEnabled(can_capture);
329     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(can_capture);
330 }
331 
~CaptureOptionsDialog()332 CaptureOptionsDialog::~CaptureOptionsDialog()
333 {
334     delete ui;
335 }
336 
setTab(int idx)337 void CaptureOptionsDialog::setTab(int idx)
338 {
339     ui->tabWidget->setCurrentIndex(idx);
340 }
341 
on_capturePromModeCheckBox_toggled(bool checked)342 void CaptureOptionsDialog::on_capturePromModeCheckBox_toggled(bool checked)
343 {
344     interface_t *device;
345     prefs.capture_prom_mode = checked;
346     for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
347         InterfaceTreeWidgetItem *ti = dynamic_cast<InterfaceTreeWidgetItem *>(ui->interfaceTree->topLevelItem(row));
348         if (!ti) continue;
349 
350         QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
351         device = getDeviceByName(device_name);
352         if (!device) continue;
353         device->pmode = checked;
354         ti->updateInterfaceColumns(device);
355     }
356 }
357 
browseButtonClicked()358 void CaptureOptionsDialog::browseButtonClicked()
359 {
360     char *open_dir = NULL;
361 
362     switch (prefs.gui_fileopen_style) {
363 
364     case FO_STYLE_LAST_OPENED:
365         open_dir = get_last_open_dir();
366         break;
367 
368     case FO_STYLE_SPECIFIED:
369         if (prefs.gui_fileopen_dir[0] != '\0')
370             open_dir = prefs.gui_fileopen_dir;
371         break;
372     }
373     QString file_name = WiresharkFileDialog::getSaveFileName(this, tr("Specify a Capture File"), open_dir);
374     ui->filenameLineEdit->setText(file_name);
375 }
376 
interfaceItemChanged(QTreeWidgetItem * item,int column)377 void CaptureOptionsDialog::interfaceItemChanged(QTreeWidgetItem *item, int column)
378 {
379     QWidget* editor = ui->interfaceTree->indexWidget(ui->interfaceTree->currentIndex());
380     if (editor) {
381         ui->interfaceTree->closePersistentEditor(item, ui->interfaceTree->currentColumn());
382     }
383 
384     InterfaceTreeWidgetItem *ti = dynamic_cast<InterfaceTreeWidgetItem *>(item);
385     if (!ti) return;
386 
387     interface_t *device;
388     QString interface_name = ti->text(col_interface_);
389     device = find_device_by_if_name(interface_name);
390     if (!device) return;
391 
392     switch(column) {
393 
394     case col_pmode_:
395         device->pmode = item->checkState(col_pmode_) == Qt::Checked ? TRUE : FALSE;
396         ti->updateInterfaceColumns(device);
397         break;
398 
399 #ifdef SHOW_MONITOR_COLUMN
400     case col_monitor_:
401     {
402         gboolean monitor_mode = FALSE;
403         if (ti->checkState(col_monitor_) == Qt::Checked) monitor_mode = TRUE;
404 
405         if_capabilities_t *caps;
406         char *auth_str = NULL;
407         QString active_dlt_name;
408 
409         set_active_dlt(device, global_capture_opts.default_options.linktype);
410 
411     #ifdef HAVE_PCAP_REMOTE
412         if (device->remote_opts.remote_host_opts.auth_type == CAPTURE_AUTH_PWD) {
413             auth_str = g_strdup_printf("%s:%s", device->remote_opts.remote_host_opts.auth_username,
414                                        device->remote_opts.remote_host_opts.auth_password);
415         }
416     #endif
417         caps = capture_get_if_capabilities(device->name, monitor_mode, auth_str, NULL, NULL, main_window_update);
418         g_free(auth_str);
419 
420         if (caps != Q_NULLPTR) {
421 
422             for (int i = static_cast<int>(g_list_length(device->links)) - 1; i >= 0; i--) {
423                 GList* rem = g_list_nth(device->links, static_cast<guint>(i));
424                 device->links = g_list_remove_link(device->links, rem);
425                 g_list_free_1(rem);
426             }
427             device->active_dlt = -1;
428             device->monitor_mode_supported = caps->can_set_rfmon;
429             device->monitor_mode_enabled = monitor_mode;
430 
431             for (GList *lt_entry = caps->data_link_types; lt_entry != Q_NULLPTR; lt_entry = gxx_list_next(lt_entry)) {
432                 link_row *linkr = new link_row();
433                 data_link_info_t *data_link_info = gxx_list_data(data_link_info_t *, lt_entry);
434                 /*
435                  * For link-layer types libpcap/WinPcap/Npcap doesn't know
436                  * about, the name will be "DLT n", and the description will
437                  * be null.
438                  * We mark those as unsupported, and don't allow them to be
439                  * used - capture filters won't work on them, for example.
440                  */
441                 if (data_link_info->description != Q_NULLPTR) {
442                     linkr->dlt = data_link_info->dlt;
443                     if (active_dlt_name.isEmpty()) {
444                         device->active_dlt = data_link_info->dlt;
445                         active_dlt_name = data_link_info->description;
446                     }
447                     linkr->name = g_strdup(data_link_info->description);
448                 } else {
449                     gchar *str;
450                     /* XXX - should we just omit them? */
451                     str = g_strdup_printf("%s (not supported)", data_link_info->name);
452                     linkr->dlt = -1;
453                     linkr->name = g_strdup(str);
454                     g_free(str);
455                 }
456                 device->links = g_list_append(device->links, linkr);
457             }
458             free_if_capabilities(caps);
459         } else {
460             /* We don't know whether this supports monitor mode or not;
461                don't ask for monitor mode. */
462             device->monitor_mode_enabled = FALSE;
463             device->monitor_mode_supported = FALSE;
464         }
465 
466         ti->updateInterfaceColumns(device);
467 
468         break;
469     }
470 #endif // SHOW_MONITOR_COLUMN
471     default:
472         break;
473     }
474 }
475 
on_gbStopCaptureAuto_toggled(bool checked)476 void CaptureOptionsDialog::on_gbStopCaptureAuto_toggled(bool checked)
477 {
478     global_capture_opts.has_file_interval = checked;
479 }
480 
on_gbNewFileAuto_toggled(bool checked)481 void CaptureOptionsDialog::on_gbNewFileAuto_toggled(bool checked)
482 {
483     global_capture_opts.multi_files_on = checked;
484     ui->stopMBCheckBox->setEnabled(checked?false:true);
485     ui->stopMBSpinBox->setEnabled(checked?false:true);
486     ui->stopMBComboBox->setEnabled(checked?false:true);
487     ui->gbCompression->setEnabled(checked);
488     ui->rbCompressionNone->setEnabled(checked);
489     ui->rbCompressionGzip->setEnabled(checked);
490 }
491 
on_cbUpdatePacketsRT_toggled(bool checked)492 void CaptureOptionsDialog::on_cbUpdatePacketsRT_toggled(bool checked)
493 {
494     global_capture_opts.real_time_mode = checked;
495 }
496 
on_cbAutoScroll_toggled(bool checked)497 void CaptureOptionsDialog::on_cbAutoScroll_toggled(bool checked)
498 {
499     auto_scroll_live = checked;
500 }
501 
on_cbExtraCaptureInfo_toggled(bool checked)502 void CaptureOptionsDialog::on_cbExtraCaptureInfo_toggled(bool checked)
503 {
504     global_capture_opts.show_info = checked;
505 }
506 
on_cbResolveMacAddresses_toggled(bool checked)507 void CaptureOptionsDialog::on_cbResolveMacAddresses_toggled(bool checked)
508 {
509     gbl_resolv_flags.mac_name = checked;
510 }
511 
on_cbResolveNetworkNames_toggled(bool checked)512 void CaptureOptionsDialog::on_cbResolveNetworkNames_toggled(bool checked)
513 {
514     gbl_resolv_flags.network_name = checked;
515 }
516 
on_cbResolveTransportNames_toggled(bool checked)517 void CaptureOptionsDialog::on_cbResolveTransportNames_toggled(bool checked)
518 {
519     gbl_resolv_flags.transport_name = checked;
520 }
521 
on_buttonBox_accepted()522 void CaptureOptionsDialog::on_buttonBox_accepted()
523 {
524     if (saveOptionsToPreferences()) {
525         emit setFilterValid(true, ui->captureFilterComboBox->lineEdit()->text());
526         accept();
527     }
528 }
529 
530 // Not sure why we have to do this manually.
on_buttonBox_rejected()531 void CaptureOptionsDialog::on_buttonBox_rejected()
532 {
533     if (saveOptionsToPreferences()) {
534         reject();
535     }
536 }
537 
on_buttonBox_helpRequested()538 void CaptureOptionsDialog::on_buttonBox_helpRequested()
539 {
540     // Probably the wrong URL.
541     wsApp->helpTopicAction(HELP_CAPTURE_OPTIONS_DIALOG);
542 }
543 
updateInterfaces()544 void CaptureOptionsDialog::updateInterfaces()
545 {
546     if (prefs.capture_pcap_ng) {
547         ui->rbPcapng->setChecked(true);
548     } else {
549         ui->rbPcap->setChecked(true);
550     }
551     ui->capturePromModeCheckBox->setChecked(prefs.capture_prom_mode);
552 
553     if (global_capture_opts.saving_to_file) {
554         ui->filenameLineEdit->setText(QString(global_capture_opts.orig_save_file));
555     }
556 
557     ui->gbNewFileAuto->setChecked(global_capture_opts.multi_files_on);
558     ui->PktCheckBox->setChecked(global_capture_opts.has_file_packets);
559     if (global_capture_opts.has_file_packets) {
560         ui->PktSpinBox->setValue(global_capture_opts.file_packets);
561     }
562     ui->MBCheckBox->setChecked(global_capture_opts.has_autostop_filesize);
563     if (global_capture_opts.has_autostop_filesize) {
564         int value = global_capture_opts.autostop_filesize;
565         if (value > 1000000) {
566             if (global_capture_opts.multi_files_on) {
567                 ui->MBSpinBox->setValue(value / 1000000);
568                 ui->MBComboBox->setCurrentIndex(2);
569             } else {
570                 ui->stopMBCheckBox->setChecked(true);
571                 ui->stopMBSpinBox->setValue(value / 1000000);
572                 ui->stopMBComboBox->setCurrentIndex(2);
573             }
574         } else if (value > 1000 && value % 1000 == 0) {
575             if (global_capture_opts.multi_files_on) {
576                 ui->MBSpinBox->setValue(value / 1000);
577                 ui->MBComboBox->setCurrentIndex(1);
578             } else {
579                 ui->stopMBCheckBox->setChecked(true);
580                 ui->stopMBSpinBox->setValue(value / 1000);
581                 ui->stopMBComboBox->setCurrentIndex(1);
582             }
583         } else {
584             if (global_capture_opts.multi_files_on) {
585                 ui->MBSpinBox->setValue(value);
586                 ui->MBComboBox->setCurrentIndex(0);
587             } else {
588                 ui->stopMBCheckBox->setChecked(true);
589                 ui->stopMBSpinBox->setValue(value);
590                 ui->stopMBComboBox->setCurrentIndex(0);
591             }
592         }
593     }
594 
595     ui->SecsCheckBox->setChecked(global_capture_opts.has_file_duration);
596     if (global_capture_opts.has_file_duration) {
597         int value = global_capture_opts.file_duration;
598         if (value > 3600 && value % 3600 == 0) {
599             ui->SecsSpinBox->setValue(value / 3600);
600             ui->SecsComboBox->setCurrentIndex(2);
601         } else if (value > 60 && value % 60 == 0) {
602             ui->SecsSpinBox->setValue(value / 60);
603             ui->SecsComboBox->setCurrentIndex(1);
604         } else {
605             ui->SecsSpinBox->setValue(value);
606             ui->SecsComboBox->setCurrentIndex(0);
607         }
608     }
609 
610     ui->IntervalSecsCheckBox->setChecked(global_capture_opts.has_file_interval);
611     if (global_capture_opts.has_file_interval) {
612         int value = global_capture_opts.file_interval;
613         if (value > 3600 && value % 3600 == 0) {
614             ui->IntervalSecsSpinBox->setValue(value / 3600);
615             ui->IntervalSecsComboBox->setCurrentIndex(2);
616         } else if (value > 60 && value % 60 == 0) {
617             ui->IntervalSecsSpinBox->setValue(value / 60);
618             ui->IntervalSecsComboBox->setCurrentIndex(1);
619         } else {
620             ui->IntervalSecsSpinBox->setValue(value);
621             ui->IntervalSecsComboBox->setCurrentIndex(0);
622         }
623     }
624 
625     if (global_capture_opts.has_ring_num_files) {
626         ui->RbSpinBox->setValue(global_capture_opts.ring_num_files);
627         ui->RbCheckBox->setCheckState(Qt::Checked);
628     }
629 
630     if (global_capture_opts.has_autostop_duration) {
631         ui->stopSecsCheckBox->setChecked(true);
632         int value = global_capture_opts.file_interval;
633         if (value > 3600 && value % 3600 == 0) {
634             ui->stopSecsSpinBox->setValue(value / 3600);
635             ui->stopSecsComboBox->setCurrentIndex(2);
636         } else if (value > 60 && value % 60 == 0) {
637             ui->stopSecsSpinBox->setValue(value / 60);
638             ui->stopSecsComboBox->setCurrentIndex(1);
639         } else {
640             ui->stopSecsSpinBox->setValue(value);
641             ui->stopSecsComboBox->setCurrentIndex(0);
642         }
643     }
644 
645     if (global_capture_opts.has_autostop_packets) {
646         ui->stopPktCheckBox->setChecked(true);
647         ui->stopPktSpinBox->setValue(global_capture_opts.autostop_packets);
648     }
649 
650     if (global_capture_opts.has_autostop_files) {
651         ui->stopFilesCheckBox->setChecked(true);
652         ui->stopFilesSpinBox->setValue(global_capture_opts.autostop_files);
653     }
654 
655     ui->cbUpdatePacketsRT->setChecked(global_capture_opts.real_time_mode);
656     ui->cbAutoScroll->setChecked(true);
657     ui->cbExtraCaptureInfo->setChecked(global_capture_opts.show_info);
658 
659     ui->cbResolveMacAddresses->setChecked(gbl_resolv_flags.mac_name);
660     ui->cbResolveNetworkNames->setChecked(gbl_resolv_flags.network_name);
661     ui->cbResolveTransportNames->setChecked(gbl_resolv_flags.transport_name);
662 
663     // Rebuild the interface list without disturbing the main welcome screen.
664     disconnect(ui->interfaceTree, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected()));
665     ui->interfaceTree->clear();
666 
667 #ifdef SHOW_BUFFER_COLUMN
668     gint          buffer;
669 #endif
670     gint          snaplen;
671     gboolean      hassnap, pmode;
672     QList<QTreeWidgetItem *> selected_interfaces;
673 
674     disconnect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int)));
675 
676     if (global_capture_opts.all_ifaces->len > 0) {
677         interface_t *device;
678 
679         for (guint device_idx = 0; device_idx < global_capture_opts.all_ifaces->len; device_idx++) {
680             device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
681 
682             /* Continue if capture device is hidden */
683             if (device->hidden) {
684                 continue;
685             }
686 
687             // Traffic sparklines
688             InterfaceTreeWidgetItem *ti = new InterfaceTreeWidgetItem(ui->interfaceTree);
689             ti->setFlags(ti->flags() | Qt::ItemIsEditable);
690             ti->setData(col_interface_, Qt::UserRole, QString(device->name));
691             ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(ti->points));
692 
693             ti->setText(col_interface_, device->display_name);
694             if (device->no_addresses > 0) {
695                 QString addr_str = tr("%1: %2").arg(device->no_addresses > 1 ? tr("Addresses") : tr("Address")).arg(device->addresses);
696                 QTreeWidgetItem *addr_ti = new QTreeWidgetItem(ti);
697 
698                 addr_str.replace('\n', ", ");
699                 addr_ti->setText(0, addr_str);
700                 addr_ti->setFlags(addr_ti->flags() ^ Qt::ItemIsSelectable);
701                 addr_ti->setFirstColumnSpanned(true);
702                 addr_ti->setToolTip(col_interface_, QString("<span>%1</span>").arg(addr_str));
703                 ti->setToolTip(col_interface_, QString("<span>%1</span>").arg(addr_str));
704             } else {
705                 ti->setToolTip(col_interface_, tr("no addresses"));
706             }
707 
708             if (capture_dev_user_pmode_find(device->name, &pmode)) {
709                 device->pmode = pmode;
710             }
711             if (capture_dev_user_snaplen_find(device->name, &hassnap, &snaplen)) {
712                 /* Default snap length set in preferences */
713                 device->snaplen = snaplen;
714                 device->has_snaplen = snaplen == WTAP_MAX_PACKET_SIZE_STANDARD ? FALSE : hassnap;
715             } else {
716                 /* No preferences set yet, use default values */
717                 device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
718                 device->has_snaplen = FALSE;
719             }
720 
721 #ifdef SHOW_BUFFER_COLUMN
722             if (capture_dev_user_buffersize_find(device->name) != -1) {
723                 buffer = capture_dev_user_buffersize_find(device->name);
724                 device->buffer = buffer;
725             } else {
726                 device->buffer = DEFAULT_CAPTURE_BUFFER_SIZE;
727             }
728 #endif
729             ti->updateInterfaceColumns(device);
730 
731             if (device->selected) {
732                 selected_interfaces << ti;
733             }
734         }
735     }
736 
737     connect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int)));
738 
739     foreach (QTreeWidgetItem *ti, selected_interfaces) {
740         ti->setSelected(true);
741     }
742     connect(ui->interfaceTree, SIGNAL(itemSelectionChanged()), this, SLOT(interfaceSelected()));
743     updateSelectedFilter();
744 
745     // Manually or automatically size some columns as needed.
746     int one_em = fontMetrics().height();
747     for (int col = 0; col < ui->interfaceTree->topLevelItemCount(); col++) {
748         switch (col) {
749         case col_pmode_:
750             ui->interfaceTree->setColumnWidth(col, one_em * 3.25);
751             break;
752         case col_snaplen_:
753             ui->interfaceTree->setColumnWidth(col, one_em * 4.25);
754             break;
755         case col_buffer_:
756             ui->interfaceTree->setColumnWidth(col, one_em * 4.25);
757             break;
758         case col_monitor_:
759             ui->interfaceTree->setColumnWidth(col, one_em * 3.25);
760             break;
761         default:
762             ui->interfaceTree->resizeColumnToContents(col);
763         }
764 
765     }
766 
767     updateWidgets();
768 
769     if (!stat_timer_) {
770         updateStatistics();
771         stat_timer_ = new QTimer(this);
772         connect(stat_timer_, SIGNAL(timeout()), this, SLOT(updateStatistics()));
773         stat_timer_->start(stat_update_interval_);
774     }
775 }
776 
showEvent(QShowEvent *)777 void CaptureOptionsDialog::showEvent(QShowEvent *)
778 {
779     updateInterfaces();
780 }
781 
refreshInterfaceList()782 void CaptureOptionsDialog::refreshInterfaceList()
783 {
784     updateInterfaces();
785     emit interfaceListChanged();
786 }
787 
updateLocalInterfaces()788 void CaptureOptionsDialog::updateLocalInterfaces()
789 {
790     updateInterfaces();
791 }
792 
updateStatistics(void)793 void CaptureOptionsDialog::updateStatistics(void)
794 {
795     interface_t *device;
796 
797     disconnect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int)));
798     for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
799 
800         for (guint if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++) {
801             QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
802             if (!ti) {
803                 continue;
804             }
805             device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx);
806             QString device_name = ti->text(col_interface_);
807             if (device_name.compare(device->display_name) || device->hidden || device->type == IF_PIPE) {
808                 continue;
809             }
810             QList<int> points = ti->data(col_traffic_, Qt::UserRole).value<QList<int> >();
811             points.append(device->packet_diff);
812             ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(points));
813         }
814     }
815     connect(ui->interfaceTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(interfaceItemChanged(QTreeWidgetItem*,int)));
816     ui->interfaceTree->viewport()->update();
817 }
818 
on_compileBPF_clicked()819 void CaptureOptionsDialog::on_compileBPF_clicked()
820 {
821     QStringList interfaces;
822     foreach (QTreeWidgetItem *ti, ui->interfaceTree->selectedItems()) {
823         interfaces.append(ti->text(col_interface_));
824     }
825 
826     QString filter = ui->captureFilterComboBox->currentText();
827     CompiledFilterOutput *cfo = new CompiledFilterOutput(this, interfaces, filter);
828 
829     cfo->show();
830 }
831 
saveOptionsToPreferences()832 bool CaptureOptionsDialog::saveOptionsToPreferences()
833 {
834     if (ui->rbPcapng->isChecked()) {
835         global_capture_opts.use_pcapng = true;
836         prefs.capture_pcap_ng = true;
837     } else {
838         global_capture_opts.use_pcapng = false;
839         prefs.capture_pcap_ng = false;
840     }
841 
842     g_free(global_capture_opts.save_file);
843     g_free(global_capture_opts.orig_save_file);
844 
845     QString filename = ui->filenameLineEdit->text();
846     if (filename.length() > 0) {
847         /* User specified a file to which the capture should be written. */
848         global_capture_opts.saving_to_file = true;
849         global_capture_opts.save_file = qstring_strdup(filename);
850         global_capture_opts.orig_save_file = qstring_strdup(filename);
851         /* Save the directory name for future file dialogs. */
852         set_last_open_dir(get_dirname(filename.toUtf8().data()));
853     } else {
854         /* User didn't specify a file; save to a temporary file. */
855         global_capture_opts.saving_to_file = false;
856         global_capture_opts.save_file = NULL;
857         global_capture_opts.orig_save_file = NULL;
858     }
859 
860     global_capture_opts.has_ring_num_files = ui->RbCheckBox->isChecked();
861 
862     if (global_capture_opts.has_ring_num_files) {
863         global_capture_opts.ring_num_files = ui->RbSpinBox->value();
864         if (global_capture_opts.ring_num_files > RINGBUFFER_MAX_NUM_FILES)
865             global_capture_opts.ring_num_files = RINGBUFFER_MAX_NUM_FILES;
866 #if RINGBUFFER_MIN_NUM_FILES > 0
867         else if (global_capture_opts.ring_num_files < RINGBUFFER_MIN_NUM_FILES)
868             global_capture_opts.ring_num_files = RINGBUFFER_MIN_NUM_FILES;
869 #endif
870     }
871     global_capture_opts.multi_files_on = ui->gbNewFileAuto->isChecked();
872     if (global_capture_opts.multi_files_on) {
873         global_capture_opts.has_file_interval = ui->SecsCheckBox->isChecked();
874         if (global_capture_opts.has_file_interval) {
875             global_capture_opts.file_interval = ui->SecsSpinBox->value();
876             int index = ui->SecsComboBox->currentIndex();
877             switch (index) {
878             case 1: global_capture_opts.file_interval *= 60;
879                 break;
880             case 2: global_capture_opts.file_interval *= 3600;
881                 break;
882             }
883          }
884          global_capture_opts.has_file_packets = ui->PktCheckBox->isChecked();
885          if (global_capture_opts.has_file_packets) {
886              global_capture_opts.file_packets = ui->PktSpinBox->value();
887          }
888          global_capture_opts.has_autostop_filesize = ui->MBCheckBox->isChecked();
889          if (global_capture_opts.has_autostop_filesize) {
890              global_capture_opts.autostop_filesize = ui->MBSpinBox->value();
891              int index = ui->MBComboBox->currentIndex();
892              switch (index) {
893              case 1: if (global_capture_opts.autostop_filesize > 2000) {
894                  QMessageBox::warning(this, tr("Error"),
895                                           tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB."));
896                  return false;
897                  } else {
898                      global_capture_opts.autostop_filesize *= 1000;
899                  }
900                  break;
901              case 2: if (global_capture_opts.autostop_filesize > 2) {
902                      QMessageBox::warning(this, tr("Error"),
903                                               tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB."));
904                      return false;
905                      } else {
906                          global_capture_opts.autostop_filesize *= 1000000;
907                      }
908                  break;
909              }
910          }
911          /* test if the settings are ok for a ringbuffer */
912          if (global_capture_opts.save_file == NULL) {
913              QMessageBox::warning(this, tr("Error"),
914                                       tr("Multiple files: No capture file name given. You must specify a filename if you want to use multiple files."));
915              return false;
916          } else if (!global_capture_opts.has_autostop_filesize &&
917                     !global_capture_opts.has_file_interval &&
918                     !global_capture_opts.has_file_duration &&
919                     !global_capture_opts.has_file_packets) {
920              QMessageBox::warning(this, tr("Error"),
921                                       tr("Multiple files: No file limit given. You must specify a file size, interval, or number of packets for each file."));
922              g_free(global_capture_opts.save_file);
923              global_capture_opts.save_file = NULL;
924              return false;
925          }
926     } else {
927         global_capture_opts.has_autostop_filesize = ui->stopMBCheckBox->isChecked();
928         if (global_capture_opts.has_autostop_filesize) {
929             global_capture_opts.autostop_filesize = ui->stopMBSpinBox->value();
930             int index = ui->stopMBComboBox->currentIndex();
931             switch (index) {
932             case 1: if (global_capture_opts.autostop_filesize > 2000) {
933                 QMessageBox::warning(this, tr("Error"),
934                                          tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB."));
935                 return false;
936                 } else {
937                     global_capture_opts.autostop_filesize *= 1000;
938                 }
939                 break;
940             case 2: if (global_capture_opts.autostop_filesize > 2) {
941                     QMessageBox::warning(this, tr("Error"),
942                                              tr("Multiple files: Requested filesize too large. The filesize cannot be greater than 2 GiB."));
943                     return false;
944                     } else {
945                         global_capture_opts.autostop_filesize *= 1000000;
946                     }
947                 break;
948             }
949         }
950     }
951 
952     global_capture_opts.has_autostop_duration = ui->stopSecsCheckBox->isChecked();
953     if (global_capture_opts.has_autostop_duration) {
954         global_capture_opts.autostop_duration = ui->stopSecsSpinBox->value();
955         int index = ui->stopSecsComboBox->currentIndex();
956         switch (index) {
957         case 1: global_capture_opts.autostop_duration *= 60;
958             break;
959         case 2: global_capture_opts.autostop_duration *= 3600;
960             break;
961         }
962     }
963 
964     global_capture_opts.has_autostop_packets = ui->stopPktCheckBox->isChecked();
965     if (global_capture_opts.has_autostop_packets) {
966         global_capture_opts.autostop_packets = ui->stopPktSpinBox->value();
967     }
968 
969     global_capture_opts.has_autostop_files = ui->stopFilesCheckBox->isChecked();
970     if (global_capture_opts.has_autostop_files) {
971         global_capture_opts.autostop_files = ui->stopFilesSpinBox->value();
972     }
973 
974     interface_t *device;
975 
976     for (int col = col_link_; col <= col_filter_; col++) {
977         if (ui->interfaceTree->isColumnHidden(col)) {
978             continue;
979         }
980         /* All entries are separated by comma. There is also one before the first interface to be able to identify
981            word boundaries. As 'lo' is part of 'nflog' an exact match is necessary. */
982         switch (col) {
983         case col_link_:
984         {
985             QStringList link_list;
986 
987             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
988                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
989                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
990                 device = getDeviceByName(device_name);
991                 if (!device || device->active_dlt == -1) {
992                     continue;
993                 }
994                 link_list << QString("%1(%2)").arg(device->name).arg(device->active_dlt);
995             }
996             g_free(prefs.capture_devices_linktypes);
997             prefs.capture_devices_linktypes = qstring_strdup(link_list.join(","));
998             break;
999         }
1000 #ifdef SHOW_BUFFER_COLUMN
1001         case col_buffer_:
1002         {
1003             QStringList buffer_size_list;
1004 
1005             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
1006                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
1007                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
1008                 device = getDeviceByName(device_name);
1009                 if (!device || device->buffer == -1) {
1010                     continue;
1011                 }
1012                 buffer_size_list << QString("%1(%2)").arg(device->name).arg(device->buffer);
1013             }
1014             g_free(prefs.capture_devices_buffersize);
1015             prefs.capture_devices_buffersize = qstring_strdup(buffer_size_list.join(","));
1016             break;
1017         }
1018 #endif // HAVE_BUFFER_SETTING
1019         case col_snaplen_:
1020         {
1021             QStringList snaplen_list;
1022 
1023             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
1024                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
1025                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
1026                 device = getDeviceByName(device_name);
1027                 if (!device) continue;
1028                 snaplen_list << QString("%1:%2(%3)")
1029                                 .arg(device->name)
1030                                 .arg(device->has_snaplen)
1031                                 .arg(device->has_snaplen ? device->snaplen : WTAP_MAX_PACKET_SIZE_STANDARD);
1032             }
1033             g_free(prefs.capture_devices_snaplen);
1034             prefs.capture_devices_snaplen = qstring_strdup(snaplen_list.join(","));
1035             break;
1036         }
1037         case col_pmode_:
1038         {
1039             QStringList pmode_list;
1040 
1041             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
1042                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
1043                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
1044                 device = getDeviceByName(device_name);
1045                 if (!device || device->pmode == -1) {
1046                     continue;
1047                 }
1048                 pmode_list << QString("%1(%2)").arg(device->name).arg(device->pmode);
1049             }
1050             g_free(prefs.capture_devices_pmode);
1051             prefs.capture_devices_pmode = qstring_strdup(pmode_list.join(","));
1052             break;
1053         }
1054 
1055 #ifdef SHOW_MONITOR_COLUMN
1056         case col_monitor_:
1057         {
1058             QStringList monitor_list;
1059 
1060             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
1061                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
1062                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
1063                 device = getDeviceByName(device_name);
1064                 if (!device || !device->monitor_mode_supported || (device->monitor_mode_supported && !device->monitor_mode_enabled)) {
1065                     continue;
1066                 }
1067                 monitor_list << device->name;
1068             }
1069             g_free(prefs.capture_devices_monitor_mode);
1070             prefs.capture_devices_monitor_mode = qstring_strdup(monitor_list.join(","));
1071             break;
1072         }
1073 #endif // HAVE_MONITOR_SETTING
1074 
1075 #if 0
1076             // The device cfilter should have been applied at this point.
1077             // We shouldn't change it here.
1078         case col_filter_:
1079         {
1080             // XXX Update selected interfaces only?
1081             for (int row = 0; row < ui->interfaceTree->topLevelItemCount(); row++) {
1082                 QTreeWidgetItem *ti = ui->interfaceTree->topLevelItem(row);
1083                 QString device_name = ti->data(col_interface_, Qt::UserRole).toString();
1084                 device = getDeviceByName(device_name);
1085                 if (!device) continue;
1086                 g_free(device->cfilter);
1087                 if (ti->text(col_filter_).isEmpty()) {
1088                     device->cfilter = NULL;
1089                 } else {
1090                     device->cfilter = qstring_strdup(ti->text(col_filter_));
1091                 }
1092             }
1093         }
1094 #endif
1095         }
1096     }
1097 
1098     g_free(global_capture_opts.compress_type);
1099 
1100     if (ui->rbCompressionNone->isChecked() )  {
1101         global_capture_opts.compress_type = NULL;
1102     } else if (ui->rbCompressionGzip->isChecked() )  {
1103         global_capture_opts.compress_type = qstring_strdup("gzip");
1104     }  else {
1105         global_capture_opts.compress_type = NULL;
1106     }
1107 
1108     prefs_main_write();
1109     return true;
1110 }
1111 
updateSelectedFilter()1112 void CaptureOptionsDialog::updateSelectedFilter()
1113 {
1114     // Should match MainWelcome::interfaceSelected.
1115     QPair <const QString, bool> sf_pair = CaptureFilterEdit::getSelectedFilter();
1116     const QString user_filter = sf_pair.first;
1117     bool conflict = sf_pair.second;
1118 
1119     if (conflict) {
1120         ui->captureFilterComboBox->lineEdit()->clear();
1121         ui->captureFilterComboBox->setConflict(true);
1122     } else {
1123         ui->captureFilterComboBox->lineEdit()->setText(user_filter);
1124     }
1125 }
1126 
on_manageButton_clicked()1127 void CaptureOptionsDialog::on_manageButton_clicked()
1128 {
1129     if (saveOptionsToPreferences()) {
1130         ManageInterfacesDialog *dlg = new ManageInterfacesDialog(this);
1131         dlg->show();
1132     }
1133 }
1134 
changeEvent(QEvent * event)1135 void CaptureOptionsDialog::changeEvent(QEvent* event)
1136 {
1137     if (0 != event)
1138     {
1139         switch (event->type())
1140         {
1141         case QEvent::LanguageChange:
1142             ui->retranslateUi(this);
1143             break;
1144         default:
1145             break;
1146         }
1147     }
1148     QDialog::changeEvent(event);
1149 }
1150 
getDeviceByName(const QString device_name)1151 interface_t *CaptureOptionsDialog::getDeviceByName(const QString device_name)
1152 {
1153     for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
1154         interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
1155         if (device_name.compare(QString().fromUtf8(device->name)) == 0) {
1156             return device;
1157         }
1158     }
1159     return NULL;
1160 }
1161 
1162 //
1163 // InterfaceTreeItem
1164 //
operator <(const QTreeWidgetItem & other) const1165 bool InterfaceTreeWidgetItem::operator< (const QTreeWidgetItem &other) const {
1166     if (treeWidget()->sortColumn() == col_traffic_) {
1167         QList<int> points = data(col_traffic_, Qt::UserRole).value<QList<int> >();
1168         QList<int> other_points = other.data(col_traffic_, Qt::UserRole).value<QList<int> >();
1169         double avg = 0, other_avg = 0;
1170         foreach (int point, points) {
1171             avg += (double) point / points.length();
1172         }
1173         foreach (int point, other_points) {
1174             other_avg += (double) point / other_points.length();
1175         }
1176         return avg < other_avg;
1177     }
1178     return QTreeWidgetItem::operator<(other);
1179 }
1180 
data(int column,int role) const1181 QVariant InterfaceTreeWidgetItem::data(int column, int role) const
1182 {
1183     // See setData for the special col_traffic_ treatment.
1184     if (column == col_traffic_ && role == Qt::UserRole) {
1185         return QVariant::fromValue(points);
1186     }
1187 
1188     return QTreeWidgetItem::data(column, role);
1189 }
1190 
setData(int column,int role,const QVariant & value)1191 void InterfaceTreeWidgetItem::setData(int column, int role, const QVariant &value)
1192 {
1193     // Workaround for closing editors on updates to the points list: normally
1194     // QTreeWidgetItem::setData emits dataChanged when the value (list) changes.
1195     // We could store a pointer to the list, or just have this hack that does
1196     // not emit dataChanged.
1197     if (column == col_traffic_ && role == Qt::UserRole) {
1198         points = value.value<QList<int> >();
1199         return;
1200     }
1201 
1202     QTreeWidgetItem::setData(column, role, value);
1203 }
1204 
1205 //
1206 // InterfaceTreeDelegate
1207 //
1208 
1209 #include <QComboBox>
1210 
InterfaceTreeDelegate(QObject * parent)1211 InterfaceTreeDelegate::InterfaceTreeDelegate(QObject *parent)
1212     : QStyledItemDelegate(parent), tree_(NULL)
1213 {
1214 }
1215 
1216 
~InterfaceTreeDelegate()1217 InterfaceTreeDelegate::~InterfaceTreeDelegate()
1218 {
1219 }
1220 
1221 
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex & idx) const1222 QWidget* InterfaceTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &idx) const
1223 {
1224     QWidget *w = NULL;
1225 #ifdef SHOW_BUFFER_COLUMN
1226     gint buffer = DEFAULT_CAPTURE_BUFFER_SIZE;
1227 #endif
1228     guint snap = WTAP_MAX_PACKET_SIZE_STANDARD;
1229     GList *links = NULL;
1230 
1231     if (idx.column() > 1 && idx.data().toString().compare(UTF8_EM_DASH)) {
1232         QTreeWidgetItem *ti = tree_->topLevelItem(idx.row());
1233         QString interface_name = ti->text(col_interface_);
1234         interface_t *device = find_device_by_if_name(interface_name);
1235 
1236         if (device) {
1237 #ifdef SHOW_BUFFER_COLUMN
1238             buffer = device->buffer;
1239 #endif
1240             snap = device->snaplen;
1241             links = device->links;
1242         }
1243         switch (idx.column()) {
1244         case col_interface_:
1245         case col_traffic_:
1246             break;
1247         case col_link_:
1248         {
1249             GList *list;
1250             link_row *linkr;
1251             QStringList valid_link_types;
1252 
1253             // XXX The GTK+ UI fills in all link types, valid or not. We add
1254             // only the valid ones. If we *do* wish to include invalid link
1255             // types we'll have to jump through the hoops necessary to disable
1256             // QComboBox items.
1257 
1258             for (list = links; list != Q_NULLPTR; list = gxx_list_next(list)) {
1259                 linkr = gxx_list_data(link_row*, list);
1260                 if (linkr->dlt >= 0) {
1261                     valid_link_types << linkr->name;
1262                 }
1263             }
1264 
1265             if (valid_link_types.size() < 2) {
1266                 break;
1267             }
1268             QComboBox *cb = new QComboBox(parent);
1269             cb->addItems(valid_link_types);
1270 
1271             connect(cb, SIGNAL(currentIndexChanged(QString)), this, SLOT(linkTypeChanged(QString)));
1272             w = (QWidget*) cb;
1273             break;
1274         }
1275         case col_snaplen_:
1276         {
1277             QSpinBox *sb = new QSpinBox(parent);
1278             sb->setRange(1, WTAP_MAX_PACKET_SIZE_STANDARD);
1279             sb->setValue(snap);
1280             sb->setWrapping(true);
1281             connect(sb, SIGNAL(valueChanged(int)), this, SLOT(snapshotLengthChanged(int)));
1282             w = (QWidget*) sb;
1283             break;
1284         }
1285 #ifdef SHOW_BUFFER_COLUMN
1286         case col_buffer_:
1287         {
1288             QSpinBox *sb = new QSpinBox(parent);
1289             sb->setRange(1, WTAP_MAX_PACKET_SIZE_STANDARD);
1290             sb->setValue(buffer);
1291             sb->setWrapping(true);
1292             connect(sb, SIGNAL(valueChanged(int)), this, SLOT(bufferSizeChanged(int)));
1293             w = (QWidget*) sb;
1294             break;
1295         }
1296 #endif
1297         case col_filter_:
1298         {
1299             CaptureFilterCombo *cf = new CaptureFilterCombo(parent, true);
1300             connect(cf->lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(filterChanged(QString)));
1301             w = (QWidget*) cf;
1302         }
1303         default:
1304             break;
1305         }
1306     }
1307     if (w)
1308         w->setAutoFillBackground(true);
1309     return w;
1310 }
1311 
eventFilter(QObject * object,QEvent * event)1312 bool InterfaceTreeDelegate::eventFilter(QObject *object, QEvent *event)
1313 {
1314     QComboBox * comboBox = dynamic_cast<QComboBox*>(object);
1315     if (comboBox) {
1316         if (event->type() == QEvent::MouseButtonRelease) {
1317             comboBox->showPopup();
1318             return true;
1319         }
1320     } else {
1321         return QStyledItemDelegate::eventFilter(object, event);
1322     }
1323     return false;
1324 }
1325 
linkTypeChanged(QString selected_link_type)1326 void InterfaceTreeDelegate::linkTypeChanged(QString selected_link_type)
1327 {
1328     GList *list;
1329     link_row *temp;
1330     interface_t *device;
1331 
1332     QTreeWidgetItem *ti = tree_->currentItem();
1333     if (!ti) {
1334         return;
1335     }
1336     QString interface_name = ti->text(col_interface_);
1337     device = find_device_by_if_name(interface_name);
1338     if (!device) {
1339         return;
1340     }
1341     for (list = device->links; list != Q_NULLPTR; list = gxx_list_next(list)) {
1342         temp = gxx_list_data(link_row*, list);
1343         if (!selected_link_type.compare(temp->name)) {
1344             device->active_dlt = temp->dlt;
1345         }
1346     }
1347     // XXX We might want to verify that active_dlt is valid at this point.
1348 }
1349 
snapshotLengthChanged(int value)1350 void InterfaceTreeDelegate::snapshotLengthChanged(int value)
1351 {
1352     interface_t *device;
1353     QTreeWidgetItem *ti = tree_->currentItem();
1354     if (!ti) {
1355         return;
1356     }
1357     QString interface_name = ti->text(col_interface_);
1358     device = find_device_by_if_name(interface_name);
1359     if (!device) {
1360         return;
1361     }
1362     if (value != WTAP_MAX_PACKET_SIZE_STANDARD) {
1363         device->has_snaplen = true;
1364         device->snaplen = value;
1365     } else {
1366         device->has_snaplen = false;
1367         device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
1368     }
1369 }
1370 
1371 #ifdef SHOW_BUFFER_COLUMN
bufferSizeChanged(int value)1372 void InterfaceTreeDelegate::bufferSizeChanged(int value)
1373 {
1374     interface_t *device;
1375     QTreeWidgetItem *ti = tree_->currentItem();
1376     if (!ti) {
1377         return;
1378     }
1379     QString interface_name = ti->text(col_interface_);
1380     device = find_device_by_if_name(interface_name);
1381     if (!device) {
1382         return;
1383     }
1384     device->buffer = value;
1385 }
1386 #endif
1387 
1388 #endif /* HAVE_LIBPCAP */
1389