Kerberos: add kerberos_inject_longterm_key() helper function
[wireshark-sm.git] / ui / qt / extcap_options_dialog.cpp
blob889a264cf9d9f116832f5e9561bab41d6580bf2d
1 /* extcap_options_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
10 #include <config.h>
12 #include <extcap_options_dialog.h>
13 #include <ui_extcap_options_dialog.h>
15 #include <main_application.h>
17 #include <QMessageBox>
18 #include <QHash>
19 #include <QHBoxLayout>
20 #include <QVBoxLayout>
21 #include <QGridLayout>
22 #include <QUrl>
23 #include <QDesktopServices>
24 #include <QTabWidget>
26 #include "ringbuffer.h"
27 #include "ui/capture_ui_utils.h"
28 #include "ui/capture_globals.h"
29 #include "ui/iface_lists.h"
31 #include "ui/ws_ui_util.h"
32 #include "ui/util.h"
33 #include <wsutil/utf8_entities.h>
35 #include <cstdio>
36 #include <epan/addr_resolv.h>
37 #include <wsutil/filesystem.h>
39 #include <extcap.h>
40 #include <extcap_parser.h>
42 #include <ui/qt/utils/qt_ui_utils.h>
44 #include <epan/prefs.h>
45 #include <ui/preference_utils.h>
47 #include <ui/qt/main_application.h>
48 #include <ui/qt/utils/stock_icon.h>
49 #include <ui/qt/utils/variant_pointer.h>
51 #include <ui/qt/extcap_argument.h>
52 #include <ui/qt/extcap_argument_file.h>
53 #include <ui/qt/extcap_argument_multiselect.h>
55 ExtcapOptionsDialog::ExtcapOptionsDialog(bool startCaptureOnClose, QWidget *parent) :
56 QDialog(parent),
57 ui(new Ui::ExtcapOptionsDialog),
58 device_name(""),
59 device_idx(0),
60 defaultValueIcon_(StockIcon("x-reset"))
62 ui->setupUi(this);
64 setWindowTitle(mainApp->windowTitleString(tr("Interface Options")));
66 ui->checkSaveOnStart->setCheckState(prefs.extcap_save_on_start ? Qt::Checked : Qt::Unchecked);
68 ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start"));
69 if (startCaptureOnClose) {
70 // This dialog was spawned because the user wanted to start a capture
71 // immediately but a mandatory parameter was not configured.
72 ui->buttonBox->button(QDialogButtonBox::Save)->hide();
76 ExtcapOptionsDialog * ExtcapOptionsDialog::createForDevice(QString &dev_name, bool startCaptureOnClose, QWidget *parent)
78 interface_t *device;
79 ExtcapOptionsDialog * resultDialog = NULL;
80 bool dev_found = false;
81 unsigned if_idx;
83 if (dev_name.length() == 0)
84 return NULL;
86 for (if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++)
88 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx);
89 if (dev_name.compare(QString(device->name)) == 0 && device->if_info.type == IF_EXTCAP)
91 dev_found = true;
92 break;
96 if (! dev_found)
97 return NULL;
99 resultDialog = new ExtcapOptionsDialog(startCaptureOnClose, parent);
100 resultDialog->device_name = QString(dev_name);
101 resultDialog->device_idx = if_idx;
103 resultDialog->setWindowTitle(mainApp->windowTitleString(tr("Interface Options") + ": " + device->display_name));
105 resultDialog->updateWidgets();
107 /* mark required fields */
108 resultDialog->anyValueChanged();
110 return resultDialog;
114 ExtcapOptionsDialog::~ExtcapOptionsDialog()
116 delete ui;
119 void ExtcapOptionsDialog::anyValueChanged()
121 bool allowStart = true;
123 ExtcapArgumentList::const_iterator iter;
125 /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
126 for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
128 /* The dynamic casts are necessary, because we come here using the Signal/Slot system
129 * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
130 * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
131 * need here an explicit cast for the check functionality */
132 if (dynamic_cast<ExtArgBool *>((*iter)) != NULL)
134 if (! ((ExtArgBool *)*iter)->isValid())
135 allowStart = false;
137 else if (dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
139 if (! ((ExtArgRadio *)*iter)->isValid())
140 allowStart = false;
142 else if (dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
144 if (! ((ExtArgSelector *)*iter)->isValid())
145 allowStart = false;
147 else if (dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
149 if (! ((ExtArgMultiSelect *)*iter)->isValid())
150 allowStart = false;
152 else if (dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
154 if (! ((ExtcapArgumentFileSelection *)*iter)->isValid())
155 allowStart = false;
157 else if (dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
159 if (! ((ExtArgNumber *)*iter)->isValid())
160 allowStart = false;
162 else if (dynamic_cast<ExtArgText *>((*iter)) != NULL)
164 if (! ((ExtArgText *)*iter)->isValid())
165 allowStart = false;
167 else if (dynamic_cast<ExtArgTimestamp *>((*iter)) != NULL)
169 if (! ((ExtArgTimestamp *)*iter)->isValid())
170 allowStart = false;
172 else
173 if (! (*iter)->isValid())
174 allowStart = false;
177 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
180 void ExtcapOptionsDialog::loadArguments()
182 GList * arguments = Q_NULLPTR, * walker = Q_NULLPTR, * item = Q_NULLPTR;
183 ExtcapArgument * argument = Q_NULLPTR;
185 if (device_name.length() == 0 )
186 return;
188 extcapArguments.clear();
190 arguments = g_list_first(extcap_get_if_configuration(device_name.toUtf8().constData()));
192 ExtcapArgumentList required;
193 ExtcapArgumentList optional;
195 walker = arguments;
196 while (walker != Q_NULLPTR)
198 item = g_list_first(gxx_list_data(GList *, walker));
199 while (item != Q_NULLPTR)
201 argument = ExtcapArgument::create(gxx_list_data(extcap_arg *, item), this);
202 if (argument != Q_NULLPTR)
204 if (argument->isRequired())
205 required << argument;
206 else
207 optional << argument;
210 item = item->next;
212 walker = gxx_list_next(walker);
215 if (required.length() > 0)
216 extcapArguments << required;
218 if (optional.length() > 0)
219 extcapArguments << optional;
221 /* argument items are now owned by ExtcapArgument. Only free the lists */
222 extcap_free_if_configuration(arguments, false);
225 void ExtcapOptionsDialog::updateWidgets()
227 QWidget * lblWidget = NULL, *editWidget = NULL;
228 ExtcapArgument * argument = NULL;
229 bool allowStart = true;
231 unsigned int counter = 0;
233 if (device_name.length() == 0 )
234 return;
236 /* find existing layout */
237 if (ui->verticalLayout->children().count() > 0)
239 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
240 QWidget * item = ui->verticalLayout->itemAt(0)->widget();
241 if (item)
243 ui->verticalLayout->removeItem(ui->verticalLayout->itemAt(0));
244 delete item;
248 QHash<QString, QWidget *> layouts;
250 /* Load all extcap arguments */
251 loadArguments();
253 /* exit if no arguments have been found. This is a precaution, it should
254 * never happen, that this dialog get's called without any arguments */
255 if (extcapArguments.count() == 0)
257 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
258 return;
260 ui->checkSaveOnStart->setText(tr("Save parameter(s) on capture start", "", static_cast<int>(extcapArguments.count())));
262 QStringList groupKeys;
263 QString defaultKeyName(tr("Default"));
264 /* QMap sorts keys, therefore the groups are sorted by appearance */
265 QMap<int, QString> groups;
267 /* Look for all necessary tabs */
268 ExtcapArgumentList::iterator iter = extcapArguments.begin();
269 while (iter != extcapArguments.end())
271 argument = (ExtcapArgument *)(*iter);
272 QString groupKey = argument->group();
273 if (groupKey.length() > 0)
275 if (! groups.values().contains(groupKey))
276 groups.insert(argument->argNr(), groupKey);
278 else if (! groups.keys().contains(0))
280 groups.insert(0, defaultKeyName);
281 groupKey = defaultKeyName;
284 if (! layouts.keys().contains(groupKey))
286 QWidget * tabWidget = new QWidget(this);
287 QGridLayout * tabLayout = new QGridLayout(tabWidget);
288 tabWidget->setLayout(tabLayout);
290 layouts.insert(groupKey, tabWidget);
293 ++iter;
295 groupKeys << groups.values();
297 /* Iterate over all arguments and do the following:
298 * 1. create the label for each element
299 * 2. create an editor for each element
300 * 3. add both to the layout for the tab widget
302 iter = extcapArguments.begin();
303 while (iter != extcapArguments.end())
305 argument = (ExtcapArgument *)(*iter);
306 QString groupKey = defaultKeyName;
307 if (argument->group().length() > 0)
308 groupKey = argument->group();
310 /* Skip non-assigned group keys, this happens if the configuration of the extcap is faulty */
311 if (! layouts.keys().contains(groupKey))
313 ++iter;
314 continue;
317 QGridLayout * layout = ((QGridLayout *)layouts[groupKey]->layout());
318 lblWidget = argument->createLabel((QWidget *)this);
319 if (lblWidget != NULL)
321 layout->addWidget(lblWidget, counter, 0, Qt::AlignVCenter);
322 editWidget = argument->createEditor((QWidget *) this);
323 if (editWidget != NULL)
325 editWidget->setProperty("extcap", VariantPointer<ExtcapArgument>::asQVariant(argument));
326 layout->addWidget(editWidget, counter, 1, Qt::AlignVCenter);
328 if (argument->isSetDefaultValueSupported())
330 QPushButton *button = new QPushButton(defaultValueIcon_,"");
331 button->setToolTip(tr("Restore default value of the item"));
332 layout->addWidget(button, counter, 2, Qt::AlignVCenter);
333 connect(button, SIGNAL(clicked()), argument, SLOT(setDefaultValue()));
337 if (argument->isRequired() && ! argument->isValid())
338 allowStart = false;
340 connect(argument, &ExtcapArgument::valueChanged, this, &ExtcapOptionsDialog::anyValueChanged);
342 counter++;
344 ++iter;
347 if (counter > 0)
349 setStyleSheet ("QLabel[isRequired=\"true\"] { font-weight: bold; } ");
351 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
353 QWidget * mainWidget = Q_NULLPTR;
355 /* We should never display the dialog, if no settings are present */
356 Q_ASSERT(layouts.count() > 0);
358 if (layouts.count() > 1)
360 QTabWidget * tabs = new QTabWidget(this);
361 foreach (QString key, groupKeys)
363 layouts[key]->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding));
364 tabs->addTab(layouts[key], key);
367 tabs->setCurrentIndex(0);
368 mainWidget = tabs;
370 else if (layouts.count() == 1)
371 mainWidget = layouts[layouts.keys().at(0)];
373 ui->verticalLayout->addWidget(mainWidget);
374 ui->verticalLayout->addSpacerItem(new QSpacerItem(20, 100, QSizePolicy::Minimum, QSizePolicy::Expanding));
376 else
378 QList<QString> keys = layouts.keys();
379 foreach (QString key, keys)
380 delete(layouts[key]);
384 void ExtcapOptionsDialog::on_buttonBox_helpRequested()
386 interface_t *device;
387 QString interface_help = NULL;
389 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
390 interface_help = QString(extcap_get_help_for_ifname(device->name));
391 /* The extcap interface didn't provide an help. Let's go with the default */
392 if (interface_help.isEmpty()) {
393 mainApp->helpTopicAction(HELP_EXTCAP_OPTIONS_DIALOG);
394 return;
397 QUrl help_url(interface_help);
399 /* Check the existence for a local file */
400 if (help_url.isLocalFile()) {
401 QString local_path = help_url.toLocalFile();
402 QFileInfo help_file(local_path);
403 if (!help_file.exists()) {
404 QMessageBox::warning(this, tr("Extcap Help cannot be found"),
405 tr("The help for the extcap interface %1 cannot be found. Given file: %2")
406 .arg(device->name).arg(QDir::toNativeSeparators(local_path)),
407 QMessageBox::Ok);
408 return;
412 /* We have an actual url or an existing local file. Let's open it. */
413 QDesktopServices::openUrl(help_url);
416 bool ExtcapOptionsDialog::saveOptionToCaptureInfo()
418 GHashTable * ret_args;
419 interface_t *device;
421 device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
422 ret_args = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
424 ExtcapArgumentList::const_iterator iter;
426 for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
428 QString call = (*iter)->call();
429 QString value = (*iter)->value();
430 QString prefValue = (*iter)->prefValue();
432 if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG && value.length() == 0)
433 continue;
435 if (call.length() <= 0) {
436 continue;
439 if (value.compare((*iter)->defaultValue()) == 0) {
440 // What _does_ required and also has a default mean (And how is
441 // it different, if at all, from has a default and also placeholder
442 // text)? Will the extcap use the default if we don't pass the
443 // argument (so it's not really required)?
444 // To be safe we can pass the default explicitly.
445 if (!(*iter)->isRequired())
446 continue;
449 char * call_string = qstring_strdup(call);
450 char * value_string = NULL;
451 if (value.length() > 0)
452 value_string = qstring_strdup(value);
454 g_hash_table_insert(ret_args, call_string, value_string);
457 if (device->external_cap_args_settings != NULL)
458 g_hash_table_unref(device->external_cap_args_settings);
459 device->external_cap_args_settings = ret_args;
460 return true;
463 void ExtcapOptionsDialog::on_buttonBox_clicked(QAbstractButton *button)
465 /* Only the save button has the ActionRole */
466 switch (ui->buttonBox->buttonRole(button)) {
467 case QDialogButtonBox::ResetRole:
468 resetValues();
469 break;
470 case QDialogButtonBox::RejectRole:
471 case QDialogButtonBox::DestructiveRole:
472 /* entries are only saved if saveOptionToCaptureInfo() is called,
473 * so do nothing. */
474 reject();
475 break;
476 case QDialogButtonBox::AcceptRole:
477 if (saveOptionToCaptureInfo()) {
478 /* Starting a new capture with those values */
479 prefs.extcap_save_on_start = ui->checkSaveOnStart->checkState() == Qt::Checked;
481 /* XXX - If extcap_save_on_start is the only preference that has
482 * changed, or if it changed from true to false, we should write
483 * out a new preference file with its new value, but don't.
485 if (ui->buttonBox->standardButton(button) == QDialogButtonBox::Save) {
486 storeValues();
487 /* Reject the dialog, because we don't want to start a capture. */
488 reject();
489 } else {
490 /* Start */
491 if (prefs.extcap_save_on_start) {
492 storeValues();
494 accept();
497 break;
498 default:
499 break;
503 void ExtcapOptionsDialog::resetValues()
505 int count = ui->verticalLayout->count();
506 if (count > 0)
508 QList<QLayout *> layouts;
510 /* Find all layouts */
511 if (qobject_cast<QTabWidget *>(ui->verticalLayout->itemAt(0)->widget()))
513 QTabWidget * tabs = qobject_cast<QTabWidget *>(ui->verticalLayout->itemAt(0)->widget());
514 for (int cnt = 0; cnt < tabs->count(); cnt++)
516 layouts.append(tabs->widget(cnt)->layout());
519 else
520 layouts.append(ui->verticalLayout->itemAt(0)->layout());
522 /* Loop over all layouts */
523 for (int cnt = 0; cnt < layouts.count(); cnt++)
525 QGridLayout * layout = qobject_cast<QGridLayout *>(layouts.at(cnt));
526 if (! layout)
527 continue;
529 /* Loop over all widgets in column 1 on layout */
530 for (int row = 0; row < layout->rowCount(); row++)
532 QWidget * child = Q_NULLPTR;
533 if (layout->itemAtPosition(row, 1))
534 child = qobject_cast<QWidget *>(layout->itemAtPosition(row, 1)->widget());
536 if (child)
538 /* Don't need labels, the edit widget contains the extcapargument property value */
539 ExtcapArgument * arg = 0;
540 QVariant prop = child->property("extcap");
542 if (prop.isValid())
544 arg = VariantPointer<ExtcapArgument>::asPtr(prop);
546 /* value<> can fail */
547 if (arg)
549 arg->setDefaultValue();
557 /* Values are stored when dialog is committed, just check validity */
558 anyValueChanged();
562 GHashTable *ExtcapOptionsDialog::getArgumentSettings(bool useCallsAsKey, bool includeEmptyValues)
564 GHashTable * entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
565 ExtcapArgumentList::const_iterator iter;
567 QString value;
569 /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
570 for (iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
572 ExtcapArgument * argument = (ExtcapArgument *)(*iter);
573 bool isBoolflag = false;
575 /* The dynamic casts are necessary, because we come here using the Signal/Slot system
576 * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
577 * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
578 * need here an explicit cast for the check functionality */
579 if (dynamic_cast<ExtArgBool *>((*iter)) != NULL)
581 value = ((ExtArgBool *)*iter)->prefValue();
582 // For boolflag there should be no value
583 if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG)
584 isBoolflag = true;
586 else if (dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
588 value = ((ExtArgRadio *)*iter)->prefValue();
590 else if (dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
592 value = ((ExtArgSelector *)*iter)->prefValue();
594 else if (dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
596 value = ((ExtArgMultiSelect *)*iter)->prefValue();
598 else if (dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
600 value = ((ExtcapArgumentFileSelection *)*iter)->prefValue();
602 else if (dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
604 value = ((ExtArgNumber *)*iter)->prefValue();
606 else if (dynamic_cast<ExtArgText *>((*iter)) != NULL)
608 value = ((ExtArgText *)*iter)->prefValue();
610 else if (dynamic_cast<ExtArgTimestamp *>((*iter)) != NULL)
612 value = ((ExtArgTimestamp *)*iter)->prefValue();
614 else
615 value = (*iter)->prefValue();
617 QString key = argument->prefKey(device_name);
618 if (useCallsAsKey)
619 key = argument->call();
621 if ((key.length() > 0) && (includeEmptyValues || isBoolflag || value.length() > 0) )
623 char * val = qstring_strdup(value);
625 g_hash_table_insert(entries, qstring_strdup(key), val);
629 return entries;
632 void ExtcapOptionsDialog::storeValues()
634 GHashTable * entries = getArgumentSettings();
636 if (g_hash_table_size(entries) > 0)
638 if (prefs_store_ext_multiple("extcap", entries))
639 mainApp->emitAppSignal(MainApplication::PreferencesChanged);
643 g_hash_table_unref(entries);
646 ExtcapValueList ExtcapOptionsDialog::loadValuesFor(int argNum, QString argumentName, QString parent)
648 ExtcapValueList elements;
649 GList * walker = 0, * values = 0;
650 extcap_value * v;
652 QList<QWidget *> children = findChildren<QWidget *>();
653 foreach (QWidget * child, children)
654 child->setEnabled(false);
656 QString argcall = argumentName;
657 if (argcall.startsWith("--"))
658 argcall = argcall.right(argcall.size()-2);
660 GHashTable * entries = getArgumentSettings(true, false);
662 values = extcap_get_if_configuration_values(this->device_name.toStdString().c_str(), argcall.toStdString().c_str(), entries);
664 for (walker = g_list_first((GList *)(values)); walker != NULL ; walker = walker->next)
666 v = (extcap_value *) walker->data;
667 if (v == NULL || v->display == NULL || v->call == NULL)
668 break;
670 /* Only accept values for this argument */
671 if (v->arg_num != argNum)
672 break;
674 QString valParent = QString().fromUtf8(v->parent);
676 if (parent.compare(valParent) == 0)
679 QString display = QString().fromUtf8(v->display);
680 QString call = QString().fromUtf8(v->call);
682 ExtcapValue element = ExtcapValue(display, call,
683 v->enabled == true, v->is_default == true);
685 #if 0
686 /* TODO: Disabled due to wrong parent handling. It leads to an infinite loop for now. To implement this properly, other things
687 will be needed, like new arguments for setting the parent in the call to the extcap utility*/
688 if (!call.isEmpty())
689 element.setChildren(this->loadValuesFor(argumentName, call));
690 #endif
692 elements.append(element);
696 foreach (QWidget * child, children)
697 child->setEnabled(true);
699 return elements;