1 /* filter_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include <config.h>
11 
12 #include <errno.h>
13 
14 #include <glib.h>
15 
16 #include <ui/filter_files.h>
17 
18 #include <wsutil/filesystem.h>
19 
20 #include "filter_dialog.h"
21 #include <ui_filter_dialog.h>
22 
23 #include <QMessageBox>
24 #include <QThread>
25 #include <QUrl>
26 #include <QSortFilterProxyModel>
27 
28 #include <ui/qt/utils/qt_ui_utils.h>
29 #include <ui/qt/widgets/capture_filter_edit.h>
30 #include <ui/qt/widgets/display_filter_edit.h>
31 #include "wireshark_application.h"
32 
33 FilterDialog::FilterDialog(QWidget *parent, FilterType filter_type, QString new_filter_) :
34     GeometryStateDialog(parent),
35     ui(new Ui::FilterDialog),
36     filter_type_(filter_type),
37     filter_tree_delegate_(new FilterTreeDelegate(this, filter_type))
38 {
39     ui->setupUi(this);
40 
41     if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 2 / 3);
42     setWindowIcon(wsApp->normalIcon());
43 
44     ui->newToolButton->setStockIcon("list-add");
45     ui->deleteToolButton->setStockIcon("list-remove");
46     ui->copyToolButton->setStockIcon("list-copy");
47 
48 #ifdef Q_OS_MAC
49     ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true);
50     ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true);
51     ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true);
52     ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true);
53 #endif
54 
55 #if 0
56     ui->filterTreeWidget->setDragEnabled(true);
57     ui->filterTreeWidget->viewport()->setAcceptDrops(true);
58     ui->filterTreeWidget->setDropIndicatorShown(true);
59     ui->filterTreeWidget->setDragDropMode(QAbstractItemView::InternalMove);
60 #endif
61     ui->filterTreeView->setDragEnabled(true);
62     ui->filterTreeView->setAcceptDrops(true);
63     ui->filterTreeView->setDropIndicatorShown(true);
64 
65     const gchar * filename = NULL;
66     QString newFilterText;
67     if (filter_type == CaptureFilter) {
68         setWindowTitle(wsApp->windowTitleString(tr("Capture Filters")));
69         filename = CFILTER_FILE_NAME;
70         newFilterText = tr("New capture filter");
71         model_ = new FilterListModel(FilterListModel::Capture, this);
72     } else {
73         setWindowTitle(wsApp->windowTitleString(tr("Display Filters")));
74         filename = DFILTER_FILE_NAME;
75         newFilterText = tr("New display filter");
76         model_ = new FilterListModel(FilterListModel::Display, this);
77     }
78 
79     if (new_filter_.length() > 0)
80         model_->addFilter(newFilterText, new_filter_);
is_active()81 
82     ui->filterTreeView->setModel(model_);
83 
84     ui->filterTreeView->setItemDelegate(new FilterTreeDelegate(this, filter_type));
85 
86     ui->filterTreeView->resizeColumnToContents(FilterListModel::ColumnName);
87 
88     connect(ui->filterTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FilterDialog::selectionChanged);
89 
90     QString abs_path = gchar_free_to_qstring(get_persconffile_path(filename, TRUE));
91     if (file_exists(abs_path.toUtf8().constData())) {
92         ui->pathLabel->setText(abs_path);
93         ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString());
94         ui->pathLabel->setToolTip(tr("Open ") + filename);
95         ui->pathLabel->setEnabled(true);
96     }
97 }
98 
99 FilterDialog::~FilterDialog()
100 {
101     delete ui;
102 }
103 
104 void FilterDialog::addFilter(QString name, QString filter, bool start_editing)
105 {
106     if (model_)
107     {
108         QModelIndex idx = model_->addFilter(name, filter);
109         if (start_editing)
110             ui->filterTreeView->edit(idx);
111     }
112 }
113 
114 void FilterDialog::updateWidgets()
115 {
116     if (! ui->filterTreeView->selectionModel())
117         return;
get_closest_nodes(const HashString & id)118 
119     int num_selected = ui->filterTreeView->selectionModel()->selectedRows().count();
120 
121     ui->copyToolButton->setEnabled(num_selected == 1);
122     ui->deleteToolButton->setEnabled(num_selected > 0);
123 }
124 
125 void FilterDialog::selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
126 {
127     updateWidgets();
reset_statistics()128 }
129 
130 void FilterDialog::on_newToolButton_clicked()
131 {
132     QString name;
133     QString filter;
134 
135     if (filter_type_ == CaptureFilter) {
136         //: This text is automatically filled in when a new filter is created
137         name = tr("New capture filter");
138         filter = "ip host host.example.com";
139     } else {
140         //: This text is automatically filled in when a new filter is created
141         name = tr("New display filter");
142         filter = "ip.host == host.example.com";
143     }
144 
145     addFilter(name, filter, true);
146 }
147 
148 void FilterDialog::on_deleteToolButton_clicked()
149 {
150     QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows();
151     QList<int> rows;
152     foreach (QModelIndex idx, selected)
153     {
154         if (idx.isValid() && ! rows.contains(idx.row()))
155         {
156             rows << idx.row();
157             model_->removeFilter(idx);
158         }
159     }
160 }
161 
162 void FilterDialog::on_copyToolButton_clicked()
163 {
164     QModelIndexList selected = ui->filterTreeView->selectionModel()->selectedRows();
165     if (selected.count() <= 0)
166         return;
167 
168     int rowNr = selected.at(0).row();
169     QModelIndex row = selected.at(0).sibling(rowNr, FilterListModel::ColumnName);
170 
171     addFilter(row.data().toString(), row.sibling(rowNr, FilterListModel::ColumnExpression).data().toString(), true);
172 }
173 
174 void FilterDialog::on_buttonBox_accepted()
175 {
176     model_->saveList();
177 
178     if (filter_type_ == CaptureFilter) {
179         wsApp->emitAppSignal(WiresharkApplication::CaptureFilterListChanged);
180     } else {
181         wsApp->emitAppSignal(WiresharkApplication::DisplayFilterListChanged);
make_token(const rak::socket_address * sa,char * buffer)182     }
183 }
184 
185 void FilterDialog::on_buttonBox_helpRequested()
186 {
187     if (filter_type_ == CaptureFilter) {
188         wsApp->helpTopicAction(HELP_CAPTURE_FILTERS_DIALOG);
189     } else {
190         wsApp->helpTopicAction(HELP_DISPLAY_FILTERS_DIALOG);
191     }
192 }
193 
194 //
195 // FilterTreeDelegate
196 // Delegate for editing capture and display filters.
197 //
198 
199 FilterTreeDelegate::FilterTreeDelegate(QObject *parent, FilterDialog::FilterType filter_type) :
200     QStyledItemDelegate(parent),
201     filter_type_(filter_type)
202 {}
203 
204 QWidget *FilterTreeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
205 {
206     QWidget * w = Q_NULLPTR;
207     if (index.column() != FilterListModel::ColumnExpression) {
208         w = QStyledItemDelegate::createEditor(parent, option, index);
209     }
210     else
211     {
212         if (filter_type_ == FilterDialog::CaptureFilter) {
213             w = new CaptureFilterEdit(parent, true);
214         } else {
215             w = new DisplayFilterEdit(parent, DisplayFilterToEnter);
216         }
217     }
218 
219     if (qobject_cast<QLineEdit *>(w) && index.column() == FilterListModel::ColumnName)
220         qobject_cast<QLineEdit *>(w)->setValidator(new FilterValidator());
221 
222     return w;
223 }
224 
225 void FilterTreeDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
226 {
227     if (! editor || ! index.isValid())
228         return;
229 
230     QStyledItemDelegate::setEditorData(editor, index);
231 
232     if (qobject_cast<QLineEdit *>(editor))
233         qobject_cast<QLineEdit *>(editor)->setText(index.data().toString());
234 }
235 
236 QValidator::State FilterValidator::validate(QString & input, int & /*pos*/) const
237 {
238     /* Making this a list to be able to easily add additional values in the future */
239     QStringList invalidKeys = QStringList() << "\"";
240 
241     if (input.length() <= 0)
242         return QValidator::Intermediate;
243 
244     foreach (QString key, invalidKeys)
245         if (input.indexOf(key) >= 0)
246             return QValidator::Invalid;
247 
248     return QValidator::Acceptable;
249 }
250