Rename ui_util.h -> ws_ui_util.h
[gd/wireshark/.git] / ui / qt / extcap_options_dialog.cpp
1 /* extcap_options_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <config.h>
23
24 #include <glib.h>
25
26 #include <extcap_options_dialog.h>
27 #include <ui_extcap_options_dialog.h>
28
29 #include <wireshark_application.h>
30
31 #ifdef HAVE_EXTCAP
32 #include <QMessageBox>
33 #include <QMap>
34 #include <QHBoxLayout>
35 #include <QVBoxLayout>
36 #include <QGridLayout>
37 #include <QUrl>
38 #include <QDesktopServices>
39
40 #include "ringbuffer.h"
41 #include "ui/capture_ui_utils.h"
42 #include "ui/capture_globals.h"
43 #include "ui/iface_lists.h"
44 #include "ui/last_open_dir.h"
45
46 #include "ui/ws_ui_util.h"
47 #include "ui/util.h"
48 #include <wsutil/utf8_entities.h>
49
50 #include <cstdio>
51 #include <epan/addr_resolv.h>
52 #include <wsutil/filesystem.h>
53
54 #include <extcap.h>
55 #include <extcap_parser.h>
56
57 #include <ui/qt/utils/qt_ui_utils.h>
58
59 #include <epan/prefs.h>
60 #include <ui/preference_utils.h>
61
62 #include <ui/qt/wireshark_application.h>
63 #include <ui/qt/utils/variant_pointer.h>
64
65 #include <ui/qt/extcap_argument.h>
66 #include <ui/qt/extcap_argument_file.h>
67 #include <ui/qt/extcap_argument_multiselect.h>
68
69 ExtcapOptionsDialog::ExtcapOptionsDialog(QWidget *parent) :
70     QDialog(parent),
71     ui(new Ui::ExtcapOptionsDialog),
72     device_name(""),
73     device_idx(0)
74 {
75     ui->setupUi(this);
76
77     setWindowTitle(wsApp->windowTitleString(tr("Interface Options")));
78
79     ui->checkSaveOnStart->setCheckState(prefs.extcap_save_on_start ? Qt::Checked : Qt::Unchecked);
80
81     ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start"));
82 }
83
84 ExtcapOptionsDialog * ExtcapOptionsDialog::createForDevice(QString &dev_name, QWidget *parent)
85 {
86     interface_t *device;
87     ExtcapOptionsDialog * resultDialog = NULL;
88     bool dev_found = false;
89     guint if_idx;
90
91     if ( dev_name.length() == 0 )
92         return NULL;
93
94     for (if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++)
95     {
96         device = &g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx);
97         if (dev_name.compare(QString(device->name)) == 0 && device->if_info.type == IF_EXTCAP)
98         {
99             dev_found = true;
100             break;
101         }
102     }
103
104     if ( ! dev_found )
105         return NULL;
106
107     resultDialog = new ExtcapOptionsDialog(parent);
108     resultDialog->device_name = QString(dev_name);
109     resultDialog->device_idx = if_idx;
110
111     resultDialog->setWindowTitle(wsApp->windowTitleString(tr("Interface Options") + ": " + device->display_name));
112
113     resultDialog->updateWidgets();
114
115     /* mark required fields */
116     resultDialog->anyValueChanged();
117
118     return resultDialog;
119 }
120
121
122 ExtcapOptionsDialog::~ExtcapOptionsDialog()
123 {
124     delete ui;
125 }
126
127 void ExtcapOptionsDialog::on_buttonBox_accepted()
128 {
129     if (saveOptionToCaptureInfo()) {
130         /* Starting a new capture with those values */
131         prefs.extcap_save_on_start = ui->checkSaveOnStart->checkState() == Qt::Checked;
132
133         if ( prefs.extcap_save_on_start )
134             storeValues();
135
136         accept();
137     }
138 }
139
140 void ExtcapOptionsDialog::anyValueChanged()
141 {
142     bool allowStart = true;
143
144     ExtcapArgumentList::const_iterator iter;
145
146     /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
147     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
148     {
149         /* The dynamic casts are necessary, because we come here using the Signal/Slot system
150          * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
151          * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
152          * need here an explicit cast for the check functionality */
153         if ( dynamic_cast<ExtArgBool *>((*iter)) != NULL)
154         {
155             if ( ! ((ExtArgBool *)*iter)->isValid() )
156                 allowStart = false;
157         }
158         else if ( dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
159         {
160             if ( ! ((ExtArgRadio *)*iter)->isValid() )
161                 allowStart = false;
162         }
163         else if ( dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
164         {
165             if ( ! ((ExtArgSelector *)*iter)->isValid() )
166                 allowStart = false;
167         }
168         else if ( dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
169         {
170             if ( ! ((ExtArgMultiSelect *)*iter)->isValid() )
171                 allowStart = false;
172         }
173         else if ( dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
174         {
175             if ( ! ((ExtcapArgumentFileSelection *)*iter)->isValid() )
176                 allowStart = false;
177         }
178         else if ( dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
179         {
180             if ( ! ((ExtArgNumber *)*iter)->isValid() )
181                 allowStart = false;
182         }
183         else if ( dynamic_cast<ExtArgText *>((*iter)) != NULL)
184         {
185             if ( ! ((ExtArgText *)*iter)->isValid() )
186                 allowStart = false;
187         }
188         else if ( dynamic_cast<ExtArgTimestamp *>((*iter)) != NULL)
189         {
190             if ( ! ((ExtArgTimestamp *)*iter)->isValid() )
191                 allowStart = false;
192         }
193         else
194             if ( ! (*iter)->isValid() )
195                 allowStart = false;
196     }
197
198     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
199 }
200
201 void ExtcapOptionsDialog::loadArguments()
202 {
203     GList * arguments = NULL, * walker = NULL, * item = NULL;
204     ExtcapArgument * argument = NULL;
205
206     if ( device_name.length() == 0  )
207         return;
208
209     extcapArguments.clear();
210
211     arguments = g_list_first(extcap_get_if_configuration((const char *)( device_name.toStdString().c_str() ) ));
212
213     ExtcapArgumentList required;
214     ExtcapArgumentList optional;
215
216     walker = arguments;
217     while ( walker != NULL )
218     {
219         item = g_list_first((GList *)(walker->data));
220         while ( item != NULL )
221         {
222             argument = ExtcapArgument::create((extcap_arg *)(item->data));
223             if ( argument != NULL )
224             {
225                 if ( argument->isRequired() )
226                     required << argument;
227                 else
228                     optional << argument;
229
230             }
231             item = item->next;
232         }
233         walker = g_list_next(walker);
234     }
235
236     if ( required.length() > 0 )
237         extcapArguments << required;
238
239     if ( optional.length() > 0 )
240         extcapArguments << optional;
241
242     /* argument items are now owned by ExtcapArgument. Only free the lists */
243     extcap_free_if_configuration(arguments, FALSE);
244 }
245
246 void ExtcapOptionsDialog::updateWidgets()
247 {
248     QWidget * lblWidget = NULL, *editWidget = NULL;
249     ExtcapArgument * argument = NULL;
250     bool allowStart = true;
251
252     unsigned int counter = 0;
253
254     if ( device_name.length() == 0  )
255         return;
256
257     /* find existing layout */
258     if (ui->verticalLayout->children().count() > 0)
259     {
260         QGridLayout * layout = (QGridLayout *)ui->verticalLayout->itemAt(0);
261         ui->verticalLayout->removeItem(layout);
262         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
263     }
264
265     QGridLayout * layout = new QGridLayout();
266
267     /* Load all extcap arguments */
268     loadArguments();
269
270
271     ExtcapArgumentList::iterator iter = extcapArguments.begin();
272     while ( iter != extcapArguments.end() )
273     {
274         argument = (ExtcapArgument *)(*iter);
275         lblWidget = argument->createLabel((QWidget *)this);
276         if ( lblWidget != NULL )
277         {
278             layout->addWidget(lblWidget, counter, 0, Qt::AlignVCenter);
279             editWidget = argument->createEditor((QWidget *) this);
280             if ( editWidget != NULL )
281             {
282                 editWidget->setProperty(QString("extcap").toLocal8Bit(), VariantPointer<ExtcapArgument>::asQVariant(argument));
283                 layout->addWidget(editWidget, counter, 1, Qt::AlignVCenter);
284             }
285
286             if ( argument->isRequired() && ! argument->isValid() )
287                 allowStart = false;
288
289             connect(argument, SIGNAL(valueChanged()), this, SLOT(anyValueChanged()));
290
291             counter++;
292         }
293         ++iter;
294     }
295
296     if ( counter > 0 )
297     {
298         setStyleSheet ( "QLabel[isRequired=\"true\"] { font-weight: bold; } ");
299
300         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
301
302         ui->verticalLayout->addLayout(layout);
303         ui->verticalLayout->addSpacerItem(new QSpacerItem(20, 100, QSizePolicy::Minimum, QSizePolicy::Expanding));
304     }
305     else
306     {
307         delete layout;
308     }
309 }
310
311 // Not sure why we have to do this manually.
312 void ExtcapOptionsDialog::on_buttonBox_rejected()
313 {
314     reject();
315 }
316
317 void ExtcapOptionsDialog::on_buttonBox_helpRequested()
318 {
319     interface_t *device;
320     QString interface_help = NULL;
321
322     device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
323     interface_help = QString(extcap_get_help_for_ifname(device->name));
324     /* The extcap interface didn't provide an help. Let's go with the default */
325     if (interface_help.isEmpty()) {
326         wsApp->helpTopicAction(HELP_EXTCAP_OPTIONS_DIALOG);
327         return;
328     }
329
330     QUrl help_url(interface_help);
331
332     /* The help is not a local file, open it and exit */
333     if (help_url.scheme().compare("file") != 0) {
334         QDesktopServices::openUrl(help_url);
335         return;
336     }
337
338     /* The help information is a file url and has been provided as-is by the extcap.
339        Before attempting to open the it, check if it actually exists.
340     */
341     QFileInfo help_file(help_url.path());
342     if ( !help_file.exists() )
343     {
344         QMessageBox::warning(this, tr("Extcap Help cannot be found"),
345                 QString(tr("The help for the extcap interface %1 cannot be found. Given file: %2"))
346                     .arg(device->name).arg(help_url.path()),
347                 QMessageBox::Ok);
348     }
349
350 }
351
352 bool ExtcapOptionsDialog::saveOptionToCaptureInfo()
353 {
354     GHashTable * ret_args;
355     interface_t *device;
356
357     device = &g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
358     ret_args = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
359
360     ExtcapArgumentList::const_iterator iter;
361
362     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
363     {
364         QString call = (*iter)->call();
365         QString value = (*iter)->value();
366
367         if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG && value.length() == 0)
368             continue;
369
370         if ( call.length() <= 0 )
371             continue;
372
373         if ( value.compare((*iter)->defaultValue()) == 0 )
374             continue;
375
376         gchar * call_string = g_strdup(call.toStdString().c_str());
377         gchar * value_string = g_strdup(value.toStdString().c_str());
378
379         g_hash_table_insert(ret_args, call_string, value_string );
380     }
381
382     if (device->external_cap_args_settings != NULL)
383       g_hash_table_unref(device->external_cap_args_settings);
384     device->external_cap_args_settings = ret_args;
385     return true;
386 }
387
388 void ExtcapOptionsDialog::on_buttonBox_clicked(QAbstractButton *button)
389 {
390     /* Only the save button has the ActionRole */
391     if ( ui->buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole )
392         resetValues();
393 }
394
395 void ExtcapOptionsDialog::resetValues()
396 {
397     ExtcapArgumentList::const_iterator iter;
398     QString value;
399
400     if (ui->verticalLayout->children().count() > 0)
401     {
402         QGridLayout * layout = (QGridLayout *)ui->verticalLayout->findChild<QGridLayout *>();
403
404         for ( int row = 0; row < layout->rowCount(); row++ )
405         {
406             QWidget * child = layout->itemAtPosition(row, 1)->widget();
407
408             if ( child )
409             {
410                 /* Don't need labels, the edit widget contains the extcapargument property value */
411                 ExtcapArgument * arg = 0;
412                 QVariant prop = child->property(QString("extcap").toLocal8Bit());
413
414                 if ( prop.isValid() )
415                 {
416                     arg = VariantPointer<ExtcapArgument>::asPtr(prop);
417
418                     /* value<> can fail */
419                     if (arg)
420                     {
421                         arg->resetValue();
422
423                         /* replacing the edit widget after resetting will lead to default value */
424                         layout->removeItem(layout->itemAtPosition(row, 1));
425                         QWidget * editWidget = arg->createEditor((QWidget *) this);
426                         if ( editWidget != NULL )
427                         {
428                             editWidget->setProperty(QString("extcap").toLocal8Bit(), VariantPointer<ExtcapArgument>::asQVariant(arg));
429                             layout->addWidget(editWidget, row, 1, Qt::AlignVCenter);
430                         }
431                     }
432                 }
433             }
434         }
435
436         /* this stores all values to the preferences */
437         storeValues();
438     }
439 }
440
441 void ExtcapOptionsDialog::storeValues()
442 {
443     GHashTable * entries = g_hash_table_new(g_str_hash, g_str_equal);
444     ExtcapArgumentList::const_iterator iter;
445
446     QString value;
447
448     /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
449     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
450     {
451         ExtcapArgument * argument = (ExtcapArgument *)(*iter);
452
453         /* The dynamic casts are necessary, because we come here using the Signal/Slot system
454          * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
455          * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
456          * need here an explicit cast for the check functionality */
457         if ( dynamic_cast<ExtArgBool *>((*iter)) != NULL)
458         {
459             value = ((ExtArgBool *)*iter)->prefValue();
460         }
461         else if ( dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
462         {
463             value = ((ExtArgRadio *)*iter)->prefValue();
464         }
465         else if ( dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
466         {
467             value = ((ExtArgSelector *)*iter)->prefValue();
468         }
469         else if ( dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
470         {
471             value = ((ExtArgMultiSelect *)*iter)->prefValue();
472         }
473         else if ( dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
474         {
475             value = ((ExtcapArgumentFileSelection *)*iter)->prefValue();
476         }
477         else if ( dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
478         {
479             value = ((ExtArgNumber *)*iter)->prefValue();
480         }
481         else if ( dynamic_cast<ExtArgText *>((*iter)) != NULL)
482         {
483             value = ((ExtArgText *)*iter)->prefValue();
484         }
485         else if ( dynamic_cast<ExtArgTimestamp *>((*iter)) != NULL)
486         {
487             value = ((ExtArgTimestamp *)*iter)->prefValue();
488         }
489         else
490             value = (*iter)->prefValue();
491
492         QString key = argument->prefKey(device_name);
493         if (key.length() > 0)
494         {
495             gchar * val = g_strdup(value.length() == 0 ? " " : value.toStdString().c_str());
496
497             g_hash_table_insert(entries, g_strdup(key.toStdString().c_str()), val);
498         }
499     }
500
501     if ( g_hash_table_size(entries) > 0 )
502     {
503         if ( prefs_store_ext_multiple("extcap", entries) )
504             wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
505
506     }
507 }
508
509
510 #endif /* HAVE_LIBPCAP */
511
512 /*
513  * Editor modelines
514  *
515  * Local Variables:
516  * c-basic-offset: 4
517  * tab-width: 8
518  * indent-tabs-mode: nil
519  * End:
520  *
521  * ex: set shiftwidth=4 tabstop=8 expandtab:
522  * :indentSize=4:tabSize=8:noTabs=true:
523  */