1 /* interface_toolbar.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 <errno.h>
13 
14 #include "interface_toolbar.h"
15 #include <ui/qt/widgets/interface_toolbar_lineedit.h>
16 #include "simple_dialog.h"
17 #include "wireshark_application.h"
18 #include <ui_interface_toolbar.h>
19 
20 #include "capture_opts.h"
21 #include "ui/capture_globals.h"
22 #include "sync_pipe.h"
23 #include "wsutil/file_util.h"
24 
25 #ifdef _WIN32
26 #include <wsutil/win32-utils.h>
27 #endif
28 
29 #include <QCheckBox>
30 #include <QComboBox>
31 #include <QDesktopServices>
32 #include <QLineEdit>
33 #include <QPushButton>
34 #include <QThread>
35 #include <QUrl>
36 
37 static const char *interface_type_property = "control_type";
38 static const char *interface_role_property = "control_role";
39 
40 // From interface control protocol.
41 enum InterfaceControlCommand {
42     commandControlInitialized  = 0,
43     commandControlSet          = 1,
44     commandControlAdd          = 2,
45     commandControlRemove       = 3,
46     commandControlEnable       = 4,
47     commandControlDisable      = 5,
48     commandStatusMessage       = 6,
49     commandInformationMessage  = 7,
50     commandWarningMessage      = 8,
51     commandErrorMessage        = 9
52 };
53 
54 // To do:
55 // - Move control pipe handling to extcap
56 
InterfaceToolbar(QWidget * parent,const iface_toolbar * toolbar)57 InterfaceToolbar::InterfaceToolbar(QWidget *parent, const iface_toolbar *toolbar) :
58     QFrame(parent),
59     ui(new Ui::InterfaceToolbar),
60     help_link_(toolbar->help),
61     use_spacer_(true)
62 {
63     ui->setupUi(this);
64 
65     // Fill inn interfaces list and initialize default interface values
66     for (GList *walker = toolbar->ifnames; walker; walker = walker->next)
67     {
68         QString ifname((gchar *)walker->data);
69         interface_[ifname].reader_thread = NULL;
70         interface_[ifname].out_fd = -1;
71     }
72 
73     initializeControls(toolbar);
74 
75 #ifdef Q_OS_MAC
76     foreach (QWidget *w, findChildren<QWidget *>())
77     {
78         w->setAttribute(Qt::WA_MacSmallSize, true);
79     }
80 #endif
81 
82     if (!use_spacer_)
83     {
84         ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
85     }
86 
87     updateWidgets();
88 }
89 
~InterfaceToolbar()90 InterfaceToolbar::~InterfaceToolbar()
91 {
92     foreach (QString ifname, interface_.keys())
93     {
94         foreach (int num, control_widget_.keys())
95         {
96             if (interface_[ifname].log_dialog.contains(num))
97             {
98                 interface_[ifname].log_dialog[num]->close();
99             }
100         }
101     }
102 
103     delete ui;
104 }
105 
initializeControls(const iface_toolbar * toolbar)106 void InterfaceToolbar::initializeControls(const iface_toolbar *toolbar)
107 {
108     for (GList *walker = toolbar->controls; walker; walker = walker->next)
109     {
110         iface_toolbar_control *control = (iface_toolbar_control *)walker->data;
111 
112         if (control_widget_.contains(control->num))
113         {
114             // Already have a widget with this number
115             continue;
116         }
117 
118         QWidget *widget = NULL;
119         switch (control->ctrl_type)
120         {
121             case INTERFACE_TYPE_BOOLEAN:
122                 widget = createCheckbox(control);
123                 break;
124 
125             case INTERFACE_TYPE_BUTTON:
126                 widget = createButton(control);
127                 break;
128 
129             case INTERFACE_TYPE_SELECTOR:
130                 widget = createSelector(control);
131                 break;
132 
133             case INTERFACE_TYPE_STRING:
134                 widget = createString(control);
135                 break;
136 
137             default:
138                 // Not supported
139                 break;
140         }
141 
142         if (widget)
143         {
144             widget->setProperty(interface_type_property, control->ctrl_type);
145             widget->setProperty(interface_role_property, control->ctrl_role);
146             control_widget_[control->num] = widget;
147         }
148     }
149 }
150 
setDefaultValue(int num,const QByteArray & value)151 void InterfaceToolbar::setDefaultValue(int num, const QByteArray &value)
152 {
153     foreach (QString ifname, interface_.keys())
154     {
155         // Adding default value to all interfaces
156         interface_[ifname].value[num] = value;
157     }
158     default_value_[num] = value;
159 }
160 
createCheckbox(iface_toolbar_control * control)161 QWidget *InterfaceToolbar::createCheckbox(iface_toolbar_control *control)
162 {
163     QCheckBox *checkbox = new QCheckBox(QString().fromUtf8(control->display));
164     checkbox->setToolTip(QString().fromUtf8(control->tooltip));
165 
166     if (control->default_value.boolean)
167     {
168         checkbox->setCheckState(Qt::Checked);
169         QByteArray default_value(1, 1);
170         setDefaultValue(control->num, default_value);
171     }
172 
173     connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxChanged(int)));
174 
175     ui->leftLayout->addWidget(checkbox);
176 
177     return checkbox;
178 }
179 
createButton(iface_toolbar_control * control)180 QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control)
181 {
182     QPushButton *button = new QPushButton(QString().fromUtf8((gchar *)control->display));
183     button->setMaximumHeight(27);
184     button->setToolTip(QString().fromUtf8(control->tooltip));
185 
186     switch (control->ctrl_role)
187     {
188         case INTERFACE_ROLE_CONTROL:
189             setDefaultValue(control->num, (gchar *)control->display);
190             connect(button, SIGNAL(clicked()), this, SLOT(onControlButtonClicked()));
191             break;
192 
193         case INTERFACE_ROLE_HELP:
194             connect(button, SIGNAL(clicked()), this, SLOT(onHelpButtonClicked()));
195             if (help_link_.isEmpty())
196             {
197                 // No help URL provided
198                 button->hide();
199             }
200             break;
201 
202         case INTERFACE_ROLE_LOGGER:
203             connect(button, SIGNAL(clicked()), this, SLOT(onLogButtonClicked()));
204             break;
205 
206         case INTERFACE_ROLE_RESTORE:
207             connect(button, SIGNAL(clicked()), this, SLOT(onRestoreButtonClicked()));
208             break;
209 
210         default:
211             // Not supported
212             break;
213     }
214 
215     ui->rightLayout->addWidget(button);
216 
217     return button;
218 }
219 
createSelector(iface_toolbar_control * control)220 QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control)
221 {
222     QLabel *label = new QLabel(QString().fromUtf8(control->display));
223     label->setToolTip(QString().fromUtf8(control->tooltip));
224     QComboBox *combobox = new QComboBox();
225     combobox->setToolTip(QString().fromUtf8(control->tooltip));
226     combobox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
227 
228     for (GList *walker = control->values; walker; walker = walker->next)
229     {
230         iface_toolbar_value *val = (iface_toolbar_value *)walker->data;
231         QString value = QString().fromUtf8((gchar *)val->value);
232         if (value.isEmpty())
233         {
234             // Invalid value
235             continue;
236         }
237         QString display = QString().fromUtf8((gchar *)val->display);
238         QByteArray interface_value;
239 
240         interface_value.append(value.toUtf8());
241         if (display.isEmpty())
242         {
243             display = value;
244         }
245         else
246         {
247             interface_value.append(QString('\0' + display).toUtf8());
248         }
249         combobox->addItem(display, value);
250         if (val->is_default)
251         {
252             combobox->setCurrentText(display);
253             setDefaultValue(control->num, value.toUtf8());
254         }
255         foreach (QString ifname, interface_.keys())
256         {
257             // Adding values to all interfaces
258             interface_[ifname].list[control->num].append(interface_value);
259         }
260         default_list_[control->num].append(interface_value);
261     }
262 
263     connect(combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboBoxChanged(int)));
264 
265     ui->leftLayout->addWidget(label);
266     ui->leftLayout->addWidget(combobox);
267     label_widget_[control->num] = label;
268 
269     return combobox;
270 }
271 
createString(iface_toolbar_control * control)272 QWidget *InterfaceToolbar::createString(iface_toolbar_control *control)
273 {
274     QLabel *label = new QLabel(QString().fromUtf8(control->display));
275     label->setToolTip(QString().fromUtf8(control->tooltip));
276     InterfaceToolbarLineEdit *lineedit = new InterfaceToolbarLineEdit(NULL, control->validation, control->is_required);
277     lineedit->setToolTip(QString().fromUtf8(control->tooltip));
278     lineedit->setPlaceholderText(QString().fromUtf8(control->placeholder));
279 
280     if (control->default_value.string)
281     {
282         lineedit->setText(QString().fromUtf8(control->default_value.string));
283         setDefaultValue(control->num, control->default_value.string);
284     }
285 
286     connect(lineedit, SIGNAL(editedTextApplied()), this, SLOT(onLineEditChanged()));
287 
288     ui->leftLayout->addWidget(label);
289     ui->leftLayout->addWidget(lineedit);
290     label_widget_[control->num] = label;
291     use_spacer_ = false;
292 
293     return lineedit;
294 }
295 
setWidgetValue(QWidget * widget,int command,QByteArray payload)296 void InterfaceToolbar::setWidgetValue(QWidget *widget, int command, QByteArray payload)
297 {
298     if (QComboBox *combobox = qobject_cast<QComboBox *>(widget))
299     {
300         combobox->blockSignals(true);
301         switch (command)
302         {
303             case commandControlSet:
304             {
305                 int idx = combobox->findData(payload);
306                 if (idx != -1)
307                 {
308                     combobox->setCurrentIndex(idx);
309                 }
310                 break;
311             }
312 
313             case commandControlAdd:
314             {
315                 QString value;
316                 QString display;
317                 if (payload.contains('\0'))
318                 {
319                     // The payload contains "value\0display"
320                     QList<QByteArray> values = payload.split('\0');
321                     value = values[0];
322                     display = values[1];
323                 }
324                 else
325                 {
326                     value = display = payload;
327                 }
328 
329                 int idx = combobox->findData(value);
330                 if (idx != -1)
331                 {
332                     // The value already exists, update item text
333                     combobox->setItemText(idx, display);
334                 }
335                 else
336                 {
337                     combobox->addItem(display, value);
338                 }
339                 break;
340             }
341 
342             case commandControlRemove:
343             {
344                 if (payload.size() == 0)
345                 {
346                     combobox->clear();
347                 }
348                 else
349                 {
350                     int idx = combobox->findData(payload);
351                     if (idx != -1)
352                     {
353                         combobox->removeItem(idx);
354                     }
355                 }
356                 break;
357             }
358 
359             default:
360                 break;
361         }
362         combobox->blockSignals(false);
363     }
364     else if (InterfaceToolbarLineEdit *lineedit = qobject_cast<InterfaceToolbarLineEdit *>(widget))
365     {
366         // We don't block signals here because changes are applied with enter or apply button,
367         // and we want InterfaceToolbarLineEdit to always syntax check the text.
368         switch (command)
369         {
370             case commandControlSet:
371                 lineedit->setText(payload);
372                 lineedit->disableApplyButton();
373                 break;
374 
375             default:
376                 break;
377         }
378     }
379     else if (QCheckBox *checkbox = qobject_cast<QCheckBox *>(widget))
380     {
381         checkbox->blockSignals(true);
382         switch (command)
383         {
384             case commandControlSet:
385             {
386                 Qt::CheckState state = Qt::Unchecked;
387                 if (payload.size() > 0 && payload.at(0) != 0)
388                 {
389                     state = Qt::Checked;
390                 }
391                 checkbox->setCheckState(state);
392                 break;
393             }
394 
395             default:
396                 break;
397         }
398         checkbox->blockSignals(false);
399     }
400     else if (QPushButton *button = qobject_cast<QPushButton *>(widget))
401     {
402         if ((command == commandControlSet) &&
403             widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
404         {
405             button->setText(payload);
406         }
407     }
408 }
409 
setInterfaceValue(QString ifname,QWidget * widget,int num,int command,QByteArray payload)410 void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int num, int command, QByteArray payload)
411 {
412     if (!widget) {
413         return;
414     }
415 
416     if (qobject_cast<QComboBox *>(widget))
417     {
418         switch (command)
419         {
420             case commandControlSet:
421                 if (interface_[ifname].value[num] != payload)
422                 {
423                     interface_[ifname].value_changed[num] = false;
424                 }
425                 foreach (QByteArray entry, interface_[ifname].list[num])
426                 {
427                     if (entry == payload || entry.startsWith(payload + '\0'))
428                     {
429                         interface_[ifname].value[num] = payload;
430                     }
431                 }
432                 break;
433 
434             case commandControlAdd:
435                 interface_[ifname].list[num].append(payload);
436                 break;
437 
438             case commandControlRemove:
439                 if (payload.size() == 0)
440                 {
441                     interface_[ifname].value[num].clear();
442                     interface_[ifname].list[num].clear();
443                 }
444                 else
445                 {
446                     foreach (QByteArray entry, interface_[ifname].list[num])
447                     {
448                         if (entry == payload || entry.startsWith(payload + '\0'))
449                         {
450                             interface_[ifname].list[num].removeAll(entry);
451                         }
452                     }
453                 }
454                 break;
455 
456             default:
457                 break;
458         }
459     }
460     else if (qobject_cast<InterfaceToolbarLineEdit *>(widget))
461     {
462         switch (command)
463         {
464             case commandControlSet:
465                 if (interface_[ifname].value[num] != payload)
466                 {
467                     interface_[ifname].value_changed[num] = false;
468                 }
469                 interface_[ifname].value[num] = payload;
470                 break;
471 
472             default:
473                 break;
474         }
475     }
476     else if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
477              (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_LOGGER))
478     {
479         if (command == commandControlSet)
480         {
481             if (interface_[ifname].log_dialog.contains(num))
482             {
483                 interface_[ifname].log_dialog[num]->clearText();
484             }
485             interface_[ifname].log_text.clear();
486         }
487         if (command == commandControlSet || command == commandControlAdd)
488         {
489             if (interface_[ifname].log_dialog.contains(num))
490             {
491                 interface_[ifname].log_dialog[num]->appendText(payload);
492             }
493             interface_[ifname].log_text[num].append(payload);
494         }
495     }
496     else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
497     {
498         // QCheckBox or QPushButton
499         switch (command)
500         {
501             case commandControlSet:
502                 if (interface_[ifname].value[num] != payload)
503                 {
504                     interface_[ifname].value_changed[num] = false;
505                 }
506                 interface_[ifname].value[num] = payload;
507                 break;
508 
509             default:
510                 break;
511         }
512     }
513 }
514 
controlReceived(QString ifname,int num,int command,QByteArray payload)515 void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QByteArray payload)
516 {
517     switch (command)
518     {
519         case commandControlSet:
520         case commandControlAdd:
521         case commandControlRemove:
522             if (control_widget_.contains(num))
523             {
524                 QWidget *widget = control_widget_[num];
525                 setInterfaceValue(ifname, widget, num, command, payload);
526 
527                 if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
528                 {
529                     setWidgetValue(widget, command, payload);
530                 }
531             }
532             break;
533 
534         case commandControlEnable:
535         case commandControlDisable:
536             if (control_widget_.contains(num))
537             {
538                 QWidget *widget = control_widget_[num];
539                 if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
540                 {
541                     bool enable = (command == commandControlEnable ? true : false);
542                     interface_[ifname].widget_disabled[num] = !enable;
543 
544                     if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
545                     {
546                         widget->setEnabled(enable);
547                         if (label_widget_.contains(num))
548                         {
549                             label_widget_[num]->setEnabled(enable);
550                         }
551                     }
552                 }
553             }
554             break;
555 
556         case commandStatusMessage:
557             wsApp->pushStatus(WiresharkApplication::TemporaryStatus, payload);
558             break;
559 
560         case commandInformationMessage:
561             simple_dialog_async(ESD_TYPE_INFO, ESD_BTN_OK, "%s", payload.data());
562             break;
563 
564         case commandWarningMessage:
565             simple_dialog_async(ESD_TYPE_WARN, ESD_BTN_OK, "%s", payload.data());
566             break;
567 
568         case commandErrorMessage:
569             simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", payload.data());
570             break;
571 
572         default:
573             // Unknown commands are silently ignored
574             break;
575     }
576 }
577 
controlSend(QString ifname,int num,int command,const QByteArray & payload=QByteArray ())578 void InterfaceToolbar::controlSend(QString ifname, int num, int command, const QByteArray &payload = QByteArray())
579 {
580     if (payload.length() > 65535)
581     {
582         // Not supported
583         return;
584     }
585 
586     if (ifname.isEmpty() || interface_[ifname].out_fd == -1)
587     {
588         // Does not have a control out channel
589         return;
590     }
591 
592     ssize_t payload_length = payload.length() + 2;
593     unsigned char high_nibble = (payload_length >> 16) & 0xFF;
594     unsigned char mid_nibble = (payload_length >> 8) & 0xFF;
595     unsigned char low_nibble = (payload_length >> 0) & 0xFF;
596 
597     QByteArray ba;
598 
599     ba.append(SP_TOOLBAR_CTRL);
600     ba.append(high_nibble);
601     ba.append(mid_nibble);
602     ba.append(low_nibble);
603     ba.append(num);
604     ba.append(command);
605     ba.append(payload);
606 
607     if (ws_write(interface_[ifname].out_fd, ba.data(), ba.length()) != ba.length())
608     {
609         simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK,
610                             "Unable to send control message:\n%s.",
611                             g_strerror(errno));
612     }
613 }
614 
onControlButtonClicked()615 void InterfaceToolbar::onControlButtonClicked()
616 {
617     const QString &ifname = ui->interfacesComboBox->currentText();
618     QPushButton *button = static_cast<QPushButton *>(sender());
619     int num = control_widget_.key(button);
620 
621     controlSend(ifname, num, commandControlSet);
622 }
623 
onCheckBoxChanged(int state)624 void InterfaceToolbar::onCheckBoxChanged(int state)
625 {
626     const QString &ifname = ui->interfacesComboBox->currentText();
627     QCheckBox *checkbox = static_cast<QCheckBox *>(sender());
628     int num = control_widget_.key(checkbox);
629 
630     QByteArray payload(1, state == Qt::Unchecked ? 0 : 1);
631     controlSend(ifname, num, commandControlSet, payload);
632     interface_[ifname].value[num] = payload;
633     interface_[ifname].value_changed[num] = true;
634 }
635 
onComboBoxChanged(int idx)636 void InterfaceToolbar::onComboBoxChanged(int idx)
637 {
638     const QString &ifname = ui->interfacesComboBox->currentText();
639     QComboBox *combobox = static_cast<QComboBox *>(sender());
640     int num = control_widget_.key(combobox);
641     QString value = combobox->itemData(idx).toString();
642 
643     QByteArray payload(value.toUtf8());
644     controlSend(ifname, num, commandControlSet, payload);
645     interface_[ifname].value[num] = payload;
646     interface_[ifname].value_changed[num] = true;
647 }
648 
onLineEditChanged()649 void InterfaceToolbar::onLineEditChanged()
650 {
651     const QString &ifname = ui->interfacesComboBox->currentText();
652     InterfaceToolbarLineEdit *lineedit = static_cast<InterfaceToolbarLineEdit *>(sender());
653     int num = control_widget_.key(lineedit);
654 
655     QByteArray payload(lineedit->text().toUtf8());
656     controlSend(ifname, num, commandControlSet, payload);
657     interface_[ifname].value[num] = payload;
658     interface_[ifname].value_changed[num] = true;
659 }
660 
onLogButtonClicked()661 void InterfaceToolbar::onLogButtonClicked()
662 {
663     const QString &ifname = ui->interfacesComboBox->currentText();
664     QPushButton *button = static_cast<QPushButton *>(sender());
665     int num = control_widget_.key(button);
666 
667     if (!interface_[ifname].log_dialog.contains(num))
668     {
669         interface_[ifname].log_dialog[num] = new FunnelTextDialog(ifname + " " + button->text());
670         connect(interface_[ifname].log_dialog[num], SIGNAL(accepted()), this, SLOT(closeLog()));
671         connect(interface_[ifname].log_dialog[num], SIGNAL(rejected()), this, SLOT(closeLog()));
672 
673         interface_[ifname].log_dialog[num]->setText(interface_[ifname].log_text[num]);
674     }
675 
676     interface_[ifname].log_dialog[num]->show();
677     interface_[ifname].log_dialog[num]->raise();
678     interface_[ifname].log_dialog[num]->activateWindow();
679 }
680 
onHelpButtonClicked()681 void InterfaceToolbar::onHelpButtonClicked()
682 {
683     QUrl help_url(help_link_);
684 
685     if (help_url.scheme().compare("file") != 0)
686     {
687         QDesktopServices::openUrl(help_url);
688     }
689 }
690 
closeLog()691 void InterfaceToolbar::closeLog()
692 {
693     FunnelTextDialog *log_dialog = static_cast<FunnelTextDialog *>(sender());
694 
695     foreach (QString ifname, interface_.keys())
696     {
697         foreach (int num, control_widget_.keys())
698         {
699             if (interface_[ifname].log_dialog.value(num) == log_dialog)
700             {
701                 interface_[ifname].log_dialog.remove(num);
702             }
703         }
704     }
705 }
706 
707 
startReaderThread(QString ifname,void * control_in)708 void InterfaceToolbar::startReaderThread(QString ifname, void *control_in)
709 {
710     QThread *thread = new QThread;
711     InterfaceToolbarReader *reader = new InterfaceToolbarReader(ifname, control_in);
712     reader->moveToThread(thread);
713 
714     connect(thread, SIGNAL(started()), reader, SLOT(loop()));
715     connect(reader, SIGNAL(finished()), thread, SLOT(quit()));
716     connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater()));
717     connect(thread, SIGNAL(finished()), reader, SLOT(deleteLater()));
718     connect(reader, SIGNAL(received(QString, int, int, QByteArray)),
719             this, SLOT(controlReceived(QString, int, int, QByteArray)));
720 
721     interface_[ifname].reader_thread = thread;
722 
723     thread->start();
724 }
725 
startCapture(GArray * ifaces)726 void InterfaceToolbar::startCapture(GArray *ifaces)
727 {
728     if (!ifaces || ifaces->len == 0)
729         return;
730 
731     const QString &selected_ifname = ui->interfacesComboBox->currentText();
732     QString first_capturing_ifname;
733     bool selected_found = false;
734 
735     for (guint i = 0; i < ifaces->len; i++)
736     {
737         interface_options *interface_opts = &g_array_index(ifaces, interface_options, i);
738         QString ifname(interface_opts->name);
739 
740         if (!interface_.contains(ifname))
741             // This interface is not for us
742             continue;
743 
744         if (first_capturing_ifname.isEmpty())
745             first_capturing_ifname = ifname;
746 
747         if (ifname.compare(selected_ifname) == 0)
748             selected_found = true;
749 
750         if (interface_[ifname].out_fd != -1)
751             // Already have control channels for this interface
752             continue;
753 
754         // Open control out channel
755 #ifdef _WIN32
756         startReaderThread(ifname, interface_opts->extcap_control_in_h);
757         // Duplicate control out handle and pass the duplicate handle to _open_osfhandle().
758         // This allows the C run-time file descriptor (out_fd) and the extcap_control_out_h to be closed independently.
759         // The duplicated handle will get closed at the same time the file descriptor is closed.
760         // The control out pipe will close when both out_fd and extcap_control_out_h are closed.
761         HANDLE duplicate_out_handle = INVALID_HANDLE_VALUE;
762         if (!DuplicateHandle(GetCurrentProcess(), interface_opts->extcap_control_out_h,
763                              GetCurrentProcess(), &duplicate_out_handle, 0, TRUE, DUPLICATE_SAME_ACCESS))
764         {
765             simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK,
766                                 "Failed to duplicate extcap control out handle: %s\n.",
767                                 win32strerror(GetLastError()));
768         }
769         else
770         {
771             interface_[ifname].out_fd = _open_osfhandle((intptr_t)duplicate_out_handle, O_APPEND | O_BINARY);
772         }
773 #else
774         startReaderThread(ifname, interface_opts->extcap_control_in);
775         interface_[ifname].out_fd = ws_open(interface_opts->extcap_control_out, O_WRONLY | O_BINARY, 0);
776 #endif
777         sendChangedValues(ifname);
778         controlSend(ifname, 0, commandControlInitialized);
779     }
780 
781     if (!selected_found && !first_capturing_ifname.isEmpty())
782     {
783         ui->interfacesComboBox->setCurrentText(first_capturing_ifname);
784     }
785     else
786     {
787         updateWidgets();
788     }
789 }
790 
stopCapture()791 void InterfaceToolbar::stopCapture()
792 {
793     foreach (QString ifname, interface_.keys())
794     {
795         if (interface_[ifname].reader_thread)
796         {
797             if (!interface_[ifname].reader_thread->isFinished())
798             {
799                 interface_[ifname].reader_thread->requestInterruption();
800             }
801             interface_[ifname].reader_thread = NULL;
802         }
803 
804         if (interface_[ifname].out_fd != -1)
805         {
806             ws_close (interface_[ifname].out_fd);
807             interface_[ifname].out_fd = -1;
808         }
809 
810         foreach (int num, control_widget_.keys())
811         {
812             // Reset disabled property for all widgets
813             interface_[ifname].widget_disabled[num] = false;
814 
815             QWidget *widget = control_widget_[num];
816             if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
817                 (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
818             {
819                 // Reset default value for control buttons
820                 interface_[ifname].value[num] = default_value_[num];
821 
822                 if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
823                 {
824                     setWidgetValue(widget, commandControlSet, default_value_[num]);
825                 }
826             }
827         }
828     }
829 
830     updateWidgets();
831 }
832 
sendChangedValues(QString ifname)833 void InterfaceToolbar::sendChangedValues(QString ifname)
834 {
835     // Send all values which has changed
836     foreach (int num, control_widget_.keys())
837     {
838         QWidget *widget = control_widget_[num];
839         if ((interface_[ifname].value_changed[num]) &&
840             (widget->property(interface_type_property).toInt() != INTERFACE_TYPE_BUTTON) &&
841             (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
842         {
843             controlSend(ifname, num, commandControlSet, interface_[ifname].value[num]);
844         }
845     }
846 }
847 
onRestoreButtonClicked()848 void InterfaceToolbar::onRestoreButtonClicked()
849 {
850     const QString &ifname = ui->interfacesComboBox->currentText();
851 
852     // Set default values to all widgets and interfaces
853     foreach (int num, control_widget_.keys())
854     {
855         QWidget *widget = control_widget_[num];
856         if (default_list_[num].size() > 0)
857         {
858             // This is a QComboBox.  Clear list and add new entries.
859             setWidgetValue(widget, commandControlRemove, QByteArray());
860             interface_[ifname].list[num].clear();
861 
862             foreach (QByteArray value, default_list_[num])
863             {
864                 setWidgetValue(widget, commandControlAdd, value);
865                 interface_[ifname].list[num].append(value);
866             }
867         }
868 
869         switch (widget->property(interface_role_property).toInt())
870         {
871             case INTERFACE_ROLE_CONTROL:
872                 setWidgetValue(widget, commandControlSet, default_value_[num]);
873                 interface_[ifname].value[num] = default_value_[num];
874                 interface_[ifname].value_changed[num] = false;
875                 break;
876 
877             case INTERFACE_ROLE_LOGGER:
878                 if (interface_[ifname].log_dialog.contains(num))
879                 {
880                     interface_[ifname].log_dialog[num]->clearText();
881                 }
882                 interface_[ifname].log_text[num].clear();
883                 break;
884 
885             default:
886                 break;
887         }
888     }
889 }
890 
hasInterface(QString ifname)891 bool InterfaceToolbar::hasInterface(QString ifname)
892 {
893     return interface_.contains(ifname);
894 }
895 
updateWidgets()896 void InterfaceToolbar::updateWidgets()
897 {
898     const QString &ifname = ui->interfacesComboBox->currentText();
899     bool is_capturing = (interface_[ifname].out_fd == -1 ? false : true);
900 
901     foreach (int num, control_widget_.keys())
902     {
903         QWidget *widget = control_widget_[num];
904         bool widget_enabled = true;
905 
906         if (ifname.isEmpty() &&
907             (widget->property(interface_role_property).toInt() != INTERFACE_ROLE_HELP))
908         {
909             // No interface selected, disable all but Help button
910             widget_enabled = false;
911         }
912         else if (!is_capturing &&
913                  (widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
914                  (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
915         {
916             widget_enabled = false;
917         }
918         else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
919         {
920             widget_enabled = !interface_[ifname].widget_disabled[num];
921         }
922 
923         widget->setEnabled(widget_enabled);
924         if (label_widget_.contains(num))
925         {
926             label_widget_[num]->setEnabled(widget_enabled);
927         }
928     }
929 
930     foreach (int num, control_widget_.keys())
931     {
932         QWidget *widget = control_widget_[num];
933         if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
934             (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESTORE))
935         {
936             widget->setEnabled(!is_capturing);
937         }
938     }
939 }
940 
interfaceListChanged()941 void InterfaceToolbar::interfaceListChanged()
942 {
943 #ifdef HAVE_LIBPCAP
944     const QString &selected_ifname = ui->interfacesComboBox->currentText();
945     bool keep_selected = false;
946 
947     ui->interfacesComboBox->blockSignals(true);
948     ui->interfacesComboBox->clear();
949 
950     for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++)
951     {
952         interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
953         if (device->hidden)
954             continue;
955 
956         if (interface_.keys().contains(device->name))
957         {
958             ui->interfacesComboBox->addItem(device->name);
959             if (selected_ifname.compare(device->name) == 0)
960             {
961                 // Keep selected interface
962                 ui->interfacesComboBox->setCurrentText(device->name);
963                 keep_selected = true;
964             }
965         }
966     }
967 
968     ui->interfacesComboBox->blockSignals(false);
969 
970     if (!keep_selected)
971     {
972         // Select the first interface
973         on_interfacesComboBox_currentIndexChanged(ui->interfacesComboBox->currentText());
974     }
975 
976     updateWidgets();
977 #endif
978 }
979 
on_interfacesComboBox_currentIndexChanged(const QString & ifname)980 void InterfaceToolbar::on_interfacesComboBox_currentIndexChanged(const QString &ifname)
981 {
982     foreach (int num, control_widget_.keys())
983     {
984         QWidget *widget = control_widget_[num];
985         if (interface_[ifname].list[num].size() > 0)
986         {
987             // This is a QComboBox.  Clear list and add new entries.
988             setWidgetValue(widget, commandControlRemove, QByteArray());
989 
990             foreach (QByteArray value, interface_[ifname].list[num])
991             {
992                 setWidgetValue(widget, commandControlAdd, value);
993             }
994         }
995 
996         if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
997         {
998             setWidgetValue(widget, commandControlSet, interface_[ifname].value[num]);
999         }
1000     }
1001 
1002     updateWidgets();
1003 }
1004