extcap: Fix misc memory leaks triggered by network interface changes
[metze/wireshark/wip.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/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 "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
64 #include <ui/qt/extcap_argument.h>
65 #include <ui/qt/extcap_argument_file.h>
66 #include <ui/qt/extcap_argument_multiselect.h>
67
68 ExtcapOptionsDialog::ExtcapOptionsDialog(QWidget *parent) :
69     QDialog(parent),
70     ui(new Ui::ExtcapOptionsDialog),
71     device_name(""),
72     device_idx(0)
73 {
74     ui->setupUi(this);
75
76     setWindowTitle(wsApp->windowTitleString(tr("Extcap Interface Options")));
77
78     ui->checkSaveOnStart->setCheckState(prefs.extcap_save_on_start ? Qt::Checked : Qt::Unchecked);
79
80     ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Start"));
81 }
82
83 ExtcapOptionsDialog * ExtcapOptionsDialog::createForDevice(QString &dev_name, QWidget *parent)
84 {
85     interface_t device;
86     ExtcapOptionsDialog * resultDialog = NULL;
87     bool dev_found = false;
88     guint if_idx;
89
90     if ( dev_name.length() == 0 )
91         return NULL;
92
93     for (if_idx = 0; if_idx < global_capture_opts.all_ifaces->len; if_idx++)
94     {
95         device = g_array_index(global_capture_opts.all_ifaces, interface_t, if_idx);
96         if (dev_name.compare(QString(device.name)) == 0 && device.if_info.type == IF_EXTCAP)
97         {
98             dev_found = true;
99             break;
100         }
101     }
102
103     if ( ! dev_found )
104         return NULL;
105
106     resultDialog = new ExtcapOptionsDialog(parent);
107     resultDialog->device_name = QString(dev_name);
108     resultDialog->device_idx = if_idx;
109
110     resultDialog->setWindowTitle(wsApp->windowTitleString(tr("Extcap Interface Options") + ": " + device.display_name));
111
112     resultDialog->updateWidgets();
113
114     /* mark required fields */
115     resultDialog->anyValueChanged();
116
117     return resultDialog;
118 }
119
120
121 ExtcapOptionsDialog::~ExtcapOptionsDialog()
122 {
123     delete ui;
124 }
125
126 void ExtcapOptionsDialog::on_buttonBox_accepted()
127 {
128     if (saveOptionToCaptureInfo()) {
129         /* Starting a new capture with those values */
130         prefs.extcap_save_on_start = ui->checkSaveOnStart->checkState() == Qt::Checked;
131
132         if ( prefs.extcap_save_on_start )
133             storeValues();
134
135         accept();
136     }
137 }
138
139 void ExtcapOptionsDialog::anyValueChanged()
140 {
141     bool allowStart = true;
142
143     ExtcapArgumentList::const_iterator iter;
144
145     /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
146     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
147     {
148         /* The dynamic casts are necessary, because we come here using the Signal/Slot system
149          * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
150          * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
151          * need here an explicit cast for the check functionality */
152         if ( dynamic_cast<ExtArgBool *>((*iter)) != NULL)
153         {
154             if ( ! ((ExtArgBool *)*iter)->isValid() )
155                 allowStart = false;
156         }
157         else if ( dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
158         {
159             if ( ! ((ExtArgRadio *)*iter)->isValid() )
160                 allowStart = false;
161         }
162         else if ( dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
163         {
164             if ( ! ((ExtArgSelector *)*iter)->isValid() )
165                 allowStart = false;
166         }
167         else if ( dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
168         {
169             if ( ! ((ExtArgMultiSelect *)*iter)->isValid() )
170                 allowStart = false;
171         }
172         else if ( dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
173         {
174             if ( ! ((ExtcapArgumentFileSelection *)*iter)->isValid() )
175                 allowStart = false;
176         }
177         else if ( dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
178         {
179             if ( ! ((ExtArgNumber *)*iter)->isValid() )
180                 allowStart = false;
181         }
182         else if ( dynamic_cast<ExtArgText *>((*iter)) != NULL)
183         {
184             if ( ! ((ExtArgText *)*iter)->isValid() )
185                 allowStart = false;
186         }
187         else
188             if ( ! (*iter)->isValid() )
189                 allowStart = false;
190     }
191
192     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
193 }
194
195 void ExtcapOptionsDialog::loadArguments()
196 {
197     GList * arguments = NULL, * walker = NULL, * item = NULL;
198     ExtcapArgument * argument = NULL;
199
200     if ( device_name.length() == 0  )
201         return;
202
203     extcapArguments.clear();
204
205     arguments = g_list_first(extcap_get_if_configuration((const char *)( device_name.toStdString().c_str() ) ));
206
207     ExtcapArgumentList required;
208     ExtcapArgumentList optional;
209
210     walker = arguments;
211     while ( walker != NULL )
212     {
213         item = g_list_first((GList *)(walker->data));
214         while ( item != NULL )
215         {
216             argument = ExtcapArgument::create((extcap_arg *)(item->data));
217             if ( argument != NULL )
218             {
219                 if ( argument->isRequired() )
220                     required << argument;
221                 else
222                     optional << argument;
223
224             }
225             item = item->next;
226         }
227         walker = g_list_next(walker);
228     }
229
230     if ( required.length() > 0 )
231         extcapArguments << required;
232
233     if ( optional.length() > 0 )
234         extcapArguments << optional;
235
236     /* argument items are now owned by ExtcapArgument. Only free the lists */
237     extcap_free_if_configuration(arguments, FALSE);
238 }
239
240 void ExtcapOptionsDialog::updateWidgets()
241 {
242     QWidget * lblWidget = NULL, *editWidget = NULL;
243     ExtcapArgument * argument = NULL;
244     bool allowStart = true;
245
246     unsigned int counter = 0;
247
248     if ( device_name.length() == 0  )
249         return;
250
251     /* find existing layout */
252     if (ui->verticalLayout->children().count() > 0)
253     {
254         QGridLayout * layout = (QGridLayout *)ui->verticalLayout->itemAt(0);
255         ui->verticalLayout->removeItem(layout);
256         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
257     }
258
259     QGridLayout * layout = new QGridLayout();
260
261     /* Load all extcap arguments */
262     loadArguments();
263
264
265     ExtcapArgumentList::iterator iter = extcapArguments.begin();
266     while ( iter != extcapArguments.end() )
267     {
268         argument = (ExtcapArgument *)(*iter);
269         lblWidget = argument->createLabel((QWidget *)this);
270         if ( lblWidget != NULL )
271         {
272             layout->addWidget(lblWidget, counter, 0, Qt::AlignVCenter);
273             editWidget = argument->createEditor((QWidget *) this);
274             if ( editWidget != NULL )
275             {
276                 editWidget->setProperty(QString("extcap").toLocal8Bit(), QVariant::fromValue(argument));
277                 layout->addWidget(editWidget, counter, 1, Qt::AlignVCenter);
278             }
279
280             if ( argument->isRequired() && ! argument->isValid() )
281                 allowStart = false;
282
283             connect(argument, SIGNAL(valueChanged()), this, SLOT(anyValueChanged()));
284
285             counter++;
286         }
287         ++iter;
288     }
289
290     if ( counter > 0 )
291     {
292         setStyleSheet ( "QLabel[isRequired=\"true\"] { font-weight: bold; } ");
293
294         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowStart);
295
296         ui->verticalLayout->addLayout(layout);
297         ui->verticalLayout->addSpacerItem(new QSpacerItem(20, 100, QSizePolicy::Minimum, QSizePolicy::Expanding));
298     }
299     else
300     {
301         delete layout;
302     }
303 }
304
305 // Not sure why we have to do this manually.
306 void ExtcapOptionsDialog::on_buttonBox_rejected()
307 {
308     reject();
309 }
310
311 void ExtcapOptionsDialog::on_buttonBox_helpRequested()
312 {
313     interface_t device;
314     gchar * interface_help = NULL;
315
316     device = g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
317     interface_help = extcap_get_help_for_ifname(device.name);
318
319     if (interface_help)
320     {
321         QUrl help_url = QString(interface_help);
322         QDesktopServices::openUrl(help_url);
323     }
324     else
325     {
326         wsApp->helpTopicAction(HELP_EXTCAP_OPTIONS_DIALOG);
327     }
328 }
329
330 bool ExtcapOptionsDialog::saveOptionToCaptureInfo()
331 {
332     GHashTable * ret_args;
333     interface_t device;
334
335     device = g_array_index(global_capture_opts.all_ifaces, interface_t, device_idx);
336     global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, device_idx);
337
338     ret_args = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
339
340     ExtcapArgumentList::const_iterator iter;
341
342     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
343     {
344         QString call = (*iter)->call();
345         QString value = (*iter)->value();
346
347         if ((*iter)->argument()->arg_type != EXTCAP_ARG_BOOLFLAG && value.length() == 0)
348             continue;
349
350         if ( call.length() <= 0 )
351             continue;
352
353         if ( value.compare((*iter)->defaultValue()) == 0 )
354             continue;
355
356         gchar * call_string = g_strdup(call.toStdString().c_str());
357         gchar * value_string = g_strdup(value.toStdString().c_str());
358
359         g_hash_table_insert(ret_args, call_string, value_string );
360     }
361
362     if (device.external_cap_args_settings != NULL)
363       g_hash_table_unref(device.external_cap_args_settings);
364     device.external_cap_args_settings = ret_args;
365
366     g_array_insert_val(global_capture_opts.all_ifaces, device_idx, device);
367
368     return true;
369 }
370
371 void ExtcapOptionsDialog::on_buttonBox_clicked(QAbstractButton *button)
372 {
373     /* Only the save button has the ActionRole */
374     if ( ui->buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole )
375         resetValues();
376 }
377
378 void ExtcapOptionsDialog::resetValues()
379 {
380     ExtcapArgumentList::const_iterator iter;
381     QString value;
382
383     if (ui->verticalLayout->children().count() > 0)
384     {
385         QGridLayout * layout = (QGridLayout *)ui->verticalLayout->findChild<QGridLayout *>();
386
387         for ( int row = 0; row < layout->rowCount(); row++ )
388         {
389             QWidget * child = layout->itemAtPosition(row, 1)->widget();
390
391             if ( child )
392             {
393                 /* Don't need labels, the edit widget contains the extcapargument property value */
394                 ExtcapArgument * arg = 0;
395                 QVariant prop = child->property(QString("extcap").toLocal8Bit());
396
397                 if ( prop.isValid() && prop.canConvert<ExtcapArgument *>())
398                 {
399                     arg = prop.value<ExtcapArgument *>();
400
401                     /* value<> can fail */
402                     if (arg)
403                     {
404                         arg->resetValue();
405
406                         /* replacing the edit widget after resetting will lead to default value */
407                         layout->removeItem(layout->itemAtPosition(row, 1));
408                         QWidget * editWidget = arg->createEditor((QWidget *) this);
409                         if ( editWidget != NULL )
410                         {
411                             editWidget->setProperty(QString("extcap").toLocal8Bit(), QVariant::fromValue(arg));
412                             layout->addWidget(editWidget, row, 1, Qt::AlignVCenter);
413                         }
414                     }
415                 }
416             }
417         }
418
419         /* this stores all values to the preferences */
420         storeValues();
421     }
422 }
423
424 void ExtcapOptionsDialog::storeValues()
425 {
426     GHashTable * entries = g_hash_table_new(g_str_hash, g_str_equal);
427     ExtcapArgumentList::const_iterator iter;
428
429     QString value;
430
431     /* All arguments are being iterated, to ensure, that any error handling catches all arguments */
432     for(iter = extcapArguments.constBegin(); iter != extcapArguments.constEnd(); ++iter)
433     {
434         ExtcapArgument * argument = (ExtcapArgument *)(*iter);
435
436         /* The dynamic casts are necessary, because we come here using the Signal/Slot system
437          * of Qt, and -in short- Q_OBJECT classes cannot be multiple inherited. Another possibility
438          * would be to use Q_INTERFACE, but this causes way more nightmares, and we really just
439          * need here an explicit cast for the check functionality */
440         if ( dynamic_cast<ExtArgBool *>((*iter)) != NULL)
441         {
442             value = ((ExtArgBool *)*iter)->prefValue();
443         }
444         else if ( dynamic_cast<ExtArgRadio *>((*iter)) != NULL)
445         {
446             value = ((ExtArgRadio *)*iter)->prefValue();
447         }
448         else if ( dynamic_cast<ExtArgSelector *>((*iter)) != NULL)
449         {
450             value = ((ExtArgSelector *)*iter)->prefValue();
451         }
452         else if ( dynamic_cast<ExtArgMultiSelect *>((*iter)) != NULL)
453         {
454             value = ((ExtArgMultiSelect *)*iter)->prefValue();
455         }
456         else if ( dynamic_cast<ExtcapArgumentFileSelection *>((*iter)) != NULL)
457         {
458             value = ((ExtcapArgumentFileSelection *)*iter)->prefValue();
459         }
460         else if ( dynamic_cast<ExtArgNumber *>((*iter)) != NULL)
461         {
462             value = ((ExtArgNumber *)*iter)->prefValue();
463         }
464         else if ( dynamic_cast<ExtArgText *>((*iter)) != NULL)
465         {
466             value = ((ExtArgText *)*iter)->prefValue();
467         }
468         else
469             value = (*iter)->prefValue();
470
471         QString key = argument->prefKey(device_name);
472         if (key.length() > 0)
473         {
474             gchar * val = g_strdup(value.length() == 0 ? " " : value.toStdString().c_str());
475
476             /* Setting the internally stored value for the preference to the new value */
477             extcap_pref_store((*iter)->argument(), val);
478
479             g_hash_table_insert(entries, g_strdup(key.toStdString().c_str()), val);
480         }
481     }
482
483     if ( g_hash_table_size(entries) > 0 )
484     {
485         if ( prefs_store_ext_multiple("extcap", entries) )
486             wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
487
488     }
489 }
490
491
492 #endif /* HAVE_LIBPCAP */
493
494 /*
495  * Editor modelines
496  *
497  * Local Variables:
498  * c-basic-offset: 4
499  * tab-width: 8
500  * indent-tabs-mode: nil
501  * End:
502  *
503  * ex: set shiftwidth=4 tabstop=8 expandtab:
504  * :indentSize=4:tabSize=8:noTabs=true:
505  */