1 /* filter_list_model.cpp
2 * Model for all filter types
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <glib.h>
12
13 #include <wsutil/filesystem.h>
14
15 #include <ui/qt/utils/qt_ui_utils.h>
16 #include <ui/qt/utils/wireshark_mime_data.h>
17 #include <ui/qt/models/filter_list_model.h>
18 #include <ui/qt/models/profile_model.h>
19
20 #include <QFile>
21 #include <QTextStream>
22 #include <QRegularExpression>
23 #include <QDir>
24 #include <QMimeData>
25
26 /*
27 * Old filter file name.
28 */
29 #define FILTER_FILE_NAME "filters"
30
31 /*
32 * Capture filter file name.
33 */
34 #define CFILTER_FILE_NAME "cfilters"
35
36 /*
37 * Display filter file name.
38 */
39 #define DFILTER_FILE_NAME "dfilters"
40
FilterListModel(QObject * parent)41 FilterListModel::FilterListModel(QObject * parent) :
42 QAbstractListModel(parent),
43 type_(FilterListModel::Display)
44 {
45 reload();
46 }
47
FilterListModel(FilterListModel::FilterListType type,QObject * parent)48 FilterListModel::FilterListModel(FilterListModel::FilterListType type, QObject * parent) :
49 QAbstractListModel(parent),
50 type_(type)
51 {
52 reload();
53 }
54
reload()55 void FilterListModel::reload()
56 {
57 storage.clear();
58
59 const char * cfile = (type_ == FilterListModel::Capture) ? CFILTER_FILE_NAME : DFILTER_FILE_NAME;
60
61 /* Try personal config file first */
62 QString fileName = gchar_free_to_qstring(get_persconffile_path(cfile, TRUE));
63 if (fileName.length() <= 0 || ! QFileInfo::exists(fileName))
64 fileName = gchar_free_to_qstring(get_persconffile_path(FILTER_FILE_NAME, TRUE));
65 if (fileName.length() <= 0 || ! QFileInfo::exists(fileName))
66 fileName = gchar_free_to_qstring(get_datafile_path(cfile));
67 if (fileName.length() <= 0 || ! QFileInfo::exists(fileName))
68 return;
69
70 QFile file(fileName);
71 /* Still can use the model, just have to start from an empty set */
72 if (! file.open(QIODevice::ReadOnly | QIODevice::Text))
73 return;
74
75 QTextStream in(&file);
76 QRegularExpression rx("\\s*\\\"(.*?)\\\"\\s(.*)");
77 while (!in.atEnd())
78 {
79 QString line = in.readLine().trimmed();
80 if (line.startsWith("#") || line.indexOf("\"") <= -1)
81 continue;
82
83 QRegularExpressionMatch match = rx.match(line);
84 if (match.hasMatch()) {
85 addFilter(match.captured(1), match.captured(2));
86 }
87 }
88 }
89
setFilterType(FilterListModel::FilterListType type)90 void FilterListModel::setFilterType(FilterListModel::FilterListType type)
91 {
92 type_ = type;
93 reload();
94 }
95
filterType() const96 FilterListModel::FilterListType FilterListModel::filterType() const
97 {
98 return type_;
99 }
100
rowCount(const QModelIndex &) const101 int FilterListModel::rowCount(const QModelIndex &/* parent */) const
102 {
103 return storage.count();
104 }
105
columnCount(const QModelIndex &) const106 int FilterListModel::columnCount(const QModelIndex &/* parent */) const
107 {
108 return 2;
109 }
110
headerData(int section,Qt::Orientation orientation,int role) const111 QVariant FilterListModel::headerData(int section, Qt::Orientation orientation, int role) const
112 {
113 if (section >= columnCount() || section < 0 || orientation != Qt::Horizontal)
114 return QVariant();
115
116 if (role == Qt::DisplayRole)
117 {
118 switch (section) {
119 case ColumnName:
120 return tr("Filter Name");
121 break;
122 case ColumnExpression:
123 return tr("Filter Expression");
124 break;
125 }
126 }
127
128 return QVariant();
129 }
130
data(const QModelIndex & index,int role) const131 QVariant FilterListModel::data(const QModelIndex &index, int role) const
132 {
133 if (! index.isValid() || index.row() >= rowCount())
134 return QVariant();
135
136 QStringList row = storage.at(index.row()).split("\n");
137 if (role == Qt::DisplayRole)
138 return row.at(index.column());
139
140 return QVariant();
141 }
142
setData(const QModelIndex & index,const QVariant & value,int role)143 bool FilterListModel::setData(const QModelIndex &index, const QVariant &value, int role)
144 {
145 if (! index.isValid() || index.row() >= rowCount() || role != Qt::EditRole)
146 return false;
147
148 QStringList row = storage.at(index.row()).split("\n");
149 if (row.count() <= index.column())
150 return false;
151
152 if (index.column() == FilterListModel::ColumnName && value.toString().contains("\""))
153 return false;
154
155 row[index.column()] = value.toString();
156 storage[index.row()] = row.join("\n");
157
158 return true;
159 }
160
flags(const QModelIndex & index) const161 Qt::ItemFlags FilterListModel::flags(const QModelIndex &index) const
162 {
163 Qt::ItemFlags fl = QAbstractListModel::flags(index);
164 fl |= Qt::ItemIsDropEnabled;
165
166 if (! index.isValid() || index.row() >= rowCount())
167 return fl;
168
169 fl |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
170
171 return fl;
172 }
addFilter(QString name,QString expression)173 QModelIndex FilterListModel::addFilter(QString name, QString expression)
174 {
175 if (name.length() == 0 || expression.length() == 0)
176 return QModelIndex();
177
178 beginInsertRows(QModelIndex(), rowCount(), rowCount());
179 storage << QString("%1\n%2").arg(name).arg(expression);
180 endInsertRows();
181
182 return index(rowCount() - 1, 0);
183 }
184
findByName(QString name)185 QModelIndex FilterListModel::findByName(QString name)
186 {
187 if (name.length() == 0)
188 return QModelIndex();
189
190 for (int cnt = 0; cnt < rowCount(); cnt++)
191 {
192 if (storage.at(cnt).startsWith(QString("%1\n").arg(name)))
193 return index(cnt, 0);
194 }
195
196 return QModelIndex();
197 }
198
findByExpression(QString expression)199 QModelIndex FilterListModel::findByExpression(QString expression)
200 {
201 if (expression.length() == 0)
202 return QModelIndex();
203
204 for (int cnt = 0; cnt < rowCount(); cnt++)
205 {
206 if (storage.at(cnt).endsWith(QString("\n%1").arg(expression)))
207 return index(cnt, 0);
208 }
209
210 return QModelIndex();
211 }
212
removeFilter(QModelIndex idx)213 void FilterListModel::removeFilter(QModelIndex idx)
214 {
215 if (! idx.isValid() || idx.row() >= rowCount())
216 return;
217
218 beginRemoveRows(QModelIndex(), idx.row(), idx.row());
219 storage.removeAt(idx.row());
220 endRemoveRows();
221 }
222
saveList()223 void FilterListModel::saveList()
224 {
225 QString filename = (type_ == FilterListModel::Capture) ? CFILTER_FILE_NAME : DFILTER_FILE_NAME;
226
227 filename = QString("%1%2%3").arg(ProfileModel::activeProfilePath()).arg("/").arg(filename);
228 QFile file(filename);
229
230 if (! file.open(QIODevice::WriteOnly | QIODevice::Text))
231 return;
232
233 QTextStream out(&file);
234 for (int row = 0; row < rowCount(); row++)
235 {
236 QString line = QString("\"%1\"").arg(index(row, ColumnName).data().toString());
237 line.append(QString(" %1").arg(index(row, ColumnExpression).data().toString()));
238
239 #ifdef _WIN32
240 line = line.append("\r\n");
241 #else
242 line = line.append("\n");
243 #endif
244 out << line;
245 }
246
247 file.close();
248 }
249
supportedDropActions() const250 Qt::DropActions FilterListModel::supportedDropActions() const
251 {
252 return Qt::MoveAction;
253 }
254
mimeTypes() const255 QStringList FilterListModel::mimeTypes() const
256 {
257 return QStringList() << WiresharkMimeData::FilterListMimeType;
258 }
259
mimeData(const QModelIndexList & indexes) const260 QMimeData *FilterListModel::mimeData(const QModelIndexList &indexes) const
261 {
262 QMimeData *mimeData = new QMimeData();
263 QStringList rows;
264
265 foreach (const QModelIndex &index, indexes)
266 {
267 if (! rows.contains(QString::number(index.row())))
268 rows << QString::number(index.row());
269 }
270
271 mimeData->setData(WiresharkMimeData::FilterListMimeType, rows.join(",").toUtf8());
272 return mimeData;
273 }
274
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int,const QModelIndex & parent)275 bool FilterListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int /* column */, const QModelIndex & parent)
276 {
277 if (action != Qt::MoveAction)
278 return true;
279
280 if (! data->hasFormat(WiresharkMimeData::FilterListMimeType))
281 return true;
282
283 QStringList rows = QString(data->data(WiresharkMimeData::FilterListMimeType)).split(",");
284
285 int insertRow = parent.isValid() ? parent.row() : row;
286
287 /* for now, only single rows can be selected */
288 if (rows.count() > 0)
289 {
290 bool ok = false;
291 int strow = rows[0].toInt(&ok);
292 if (ok)
293 {
294 int storeTo = insertRow;
295 if (storeTo < 0 || storeTo >= storage.count())
296 storeTo = storage.count() - 1;
297
298 beginResetModel();
299 storage.move(strow, storeTo);
300 endResetModel();
301 }
302 }
303
304 return true;
305 }
306