1 /* expert_info_model.cpp
2  * Data model for Expert Info tap data.
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 "expert_info_model.h"
12 
13 #include "file.h"
14 
15 ExpertPacketItem::ExpertPacketItem(const expert_info_t& expert_info, column_info *cinfo, ExpertPacketItem* parent) :
16     packet_num_(expert_info.packet_num),
17     group_(expert_info.group),
18     severity_(expert_info.severity),
19     hf_id_(expert_info.hf_index),
20     protocol_(expert_info.protocol),
21     summary_(expert_info.summary),
22     parentItem_(parent)
23 {
24     if (cinfo) {
25         info_ = col_get_text(cinfo, COL_INFO);
26     }
27 }
28 
29 ExpertPacketItem::~ExpertPacketItem()
30 {
31     for (int row = 0; row < childItems_.count(); row++)
32     {
33         delete childItems_.value(row);
34     }
35 
36     childItems_.clear();
37 }
38 
39 QString ExpertPacketItem::groupKey(bool group_by_summary, int severity, int group, QString protocol, int expert_hf)
40 {
41     QString key = QString("%1|%2|%3")
42             .arg(severity)
43             .arg(group)
44             .arg(protocol);
45     if (group_by_summary) {
46         key += QString("|%1").arg(expert_hf);
47     }
48     return key;
49 }
50 
51 QString ExpertPacketItem::groupKey(bool group_by_summary) {
52     return groupKey(group_by_summary, severity_, group_, protocol_, hf_id_);
53 }
54 
55 void ExpertPacketItem::appendChild(ExpertPacketItem* child, QString hash)
56 {
57     childItems_.append(child);
58     hashChild_[hash] = child;
59 }
60 
61 ExpertPacketItem* ExpertPacketItem::child(int row)
62 {
63     return childItems_.value(row);
64 }
65 
66 ExpertPacketItem* ExpertPacketItem::child(QString hash)
67 {
68     return hashChild_[hash];
69 }
70 
71 int ExpertPacketItem::childCount() const
72 {
73     return childItems_.count();
74 }
75 
76 int ExpertPacketItem::row() const
77 {
78     if (parentItem_)
79         return parentItem_->childItems_.indexOf(const_cast<ExpertPacketItem*>(this));
80 
81     return 0;
82 }
83 
84 ExpertPacketItem* ExpertPacketItem::parentItem()
85 {
86     return parentItem_;
87 }
88 
89 
90 
91 
92 ExpertInfoModel::ExpertInfoModel(CaptureFile& capture_file, QObject *parent) :
93     QAbstractItemModel(parent),
94     capture_file_(capture_file),
95     group_by_summary_(true),
96     root_(createRootItem())
97 {
98 }
99 
100 ExpertInfoModel::~ExpertInfoModel()
101 {
102     delete root_;
103 }
104 
105 void ExpertInfoModel::clear()
106 {
107     emit beginResetModel();
108 
109     eventCounts_.clear();
110     delete root_;
111     root_ = createRootItem();
112 
113     emit endResetModel();
114 }
115 
116 ExpertPacketItem* ExpertInfoModel::createRootItem()
117 {
118     static const char* rootName = "ROOT";
119 DIAG_OFF_CAST_AWAY_CONST
120     static expert_info_t root_expert = { 0, -1, -1, -1, rootName, (gchar*)rootName, NULL };
121 DIAG_ON_CAST_AWAY_CONST
122 
123     return new ExpertPacketItem(root_expert, NULL, NULL);
124 }
125 
126 
127 
128 int ExpertInfoModel::numEvents(enum ExpertSeverity severity)
129 {
130     return eventCounts_[severity];
131 }
132 
133 QModelIndex ExpertInfoModel::index(int row, int column, const QModelIndex& parent) const
134 {
135     if (!hasIndex(row, column, parent))
136         return QModelIndex();
137 
138     ExpertPacketItem *parent_item, *child_item;
139 
140     if (!parent.isValid())
141         parent_item = root_;
142     else
143         parent_item = static_cast<ExpertPacketItem*>(parent.internalPointer());
144 
145     Q_ASSERT(parent_item);
146     if (group_by_summary_) {
147         //don't allow group layer
148         if (parent_item == root_) {
149             int row_count = 0;
150             ExpertPacketItem *grandchild_item;
151 
152             for (int subrow = 0; subrow < parent_item->childCount(); subrow++) {
153                 child_item = parent_item->child(subrow);
154                 //summary children are always stored in first child of group
155                 grandchild_item = child_item->child(0);
156 
157                 if (row_count+grandchild_item->childCount() > row) {
158                     return createIndex(row, column, grandchild_item->child(row-row_count));
159                 }
160                 row_count += grandchild_item->childCount();
161             }
162 
163             //shouldn't happen
164             return QModelIndex();
165         }
166 
167         int root_level = 0;
168         ExpertPacketItem *item = parent_item;
169         while (item != root_)
170         {
171             root_level++;
172             item = item->parentItem();
173         }
174 
175         if (root_level == 3) {
176             child_item = parent_item->child(row);
177             if (child_item) {
178                 return createIndex(row, column, child_item);
179             }
180         }
181 
182     } else {
183         child_item = parent_item->child(row);
184         if (child_item) {
185             //only allow 2 levels deep
186             if (((parent_item == root_) || (parent_item->parentItem() == root_)))
187                 return createIndex(row, column, child_item);
188         }
189     }
190     return QModelIndex();
191 }
192 
193 QModelIndex ExpertInfoModel::parent(const QModelIndex& index) const
194 {
195     if (!index.isValid())
196         return QModelIndex();
197 
198     ExpertPacketItem *item = static_cast<ExpertPacketItem*>(index.internalPointer());
199     ExpertPacketItem *parent_item = item->parentItem();
200 
201     if (group_by_summary_)
202     {
203         //don't allow group layer
204         int root_level = 0;
205         item = parent_item;
206         while ((item != root_) && (item != NULL))
207         {
208             root_level++;
209             item = item->parentItem();
210         }
211 
212         if (root_level == 3)
213             return createIndex(parent_item->row(), 0, parent_item);
214 
215     } else {
216         if (parent_item == root_)
217             return QModelIndex();
218 
219         return createIndex(parent_item->row(), 0, parent_item);
220     }
221 
222     return QModelIndex();
223 }
224 
225 #if 0
226 Qt::ItemFlags ExpertInfoModel::flags(const QModelIndex &index) const
227 {
228     if (!index.isValid())
229         return 0;
230 
231     ExpertPacketItem* item = static_cast<ExpertPacketItem*>(index.internalPointer());
232     Qt::ItemFlags flags = QAbstractTableModel::flags(index);
233 
234     //collapse???
235     return flags;
236 }
237 #endif
238 
239 QVariant ExpertInfoModel::data(const QModelIndex &index, int role) const
240 {
241     if (!index.isValid() || role != Qt::DisplayRole)
242         return QVariant();
243 
244     ExpertPacketItem* item = static_cast<ExpertPacketItem*>(index.internalPointer());
245     if (item == NULL)
246         return QVariant();
247 
248     switch ((enum ExpertColumn)index.column()) {
249     case colSeverity:
250         return QString(val_to_str_const(item->severity(), expert_severity_vals, "Unknown"));
251     case colSummary:
252         if (index.parent().isValid())
253         {
254             if (item->severity() == PI_COMMENT)
255                 return item->summary().simplified();
256             if (group_by_summary_)
257                 return item->colInfo().simplified();
258 
259             return item->summary().simplified();
260         }
261         else
262         {
263             if (group_by_summary_)
264             {
265                 if (item->severity() == PI_COMMENT)
266                     return "Packet comments listed below.";
267                 return item->summary().simplified();
268             }
269         }
270         return QVariant();
271     case colGroup:
272         return QString(val_to_str_const(item->group(), expert_group_vals, "Unknown"));
273     case colProtocol:
274         return item->protocol();
275     case colCount:
276         if (!index.parent().isValid())
277         {
278             return item->childCount();
279         }
280         break;
281     case colPacket:
282         return item->packetNum();
283     case colHf:
284         return item->hfId();
285     default:
286         break;
287     }
288 
289     return QVariant();
290 }
291 
292 //GUI helpers
293 void ExpertInfoModel::setGroupBySummary(bool group_by_summary)
294 {
295     emit beginResetModel();
296     group_by_summary_ = group_by_summary;
297     emit endResetModel();
298 }
299 
300 int ExpertInfoModel::rowCount(const QModelIndex &parent) const
301 {
302     ExpertPacketItem *parent_item;
303     if (parent.column() > 0)
304         return 0;
305 
306     if (!parent.isValid())
307         parent_item = root_;
308     else
309         parent_item = static_cast<ExpertPacketItem*>(parent.internalPointer());
310 
311     if (group_by_summary_) {
312         int row_count = 0;
313 
314         //don't allow group layer
315         if (parent_item == root_) {
316             ExpertPacketItem *child_item, *grandchild_item;
317 
318             for (int row = 0; row < parent_item->childCount(); row++) {
319                 child_item = parent_item->child(row);
320                 grandchild_item = child_item->child(0);
321                 row_count += grandchild_item->childCount();
322             }
323 
324             return row_count;
325         }
326 
327         return parent_item->childCount();
328 
329     } else {
330         //only allow 2 levels deep
331         if ((parent_item == root_) || (parent_item->parentItem() == root_))
332             return parent_item->childCount();
333     }
334 
335     return 0;
336 }
337 
338 int ExpertInfoModel::columnCount(const QModelIndex&) const
339 {
340     return colLast;
341 }
342 
343 void ExpertInfoModel::addExpertInfo(const struct expert_info_s& expert_info)
344 {
345     QString groupKey = ExpertPacketItem::groupKey(FALSE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index);
346     QString summaryKey = ExpertPacketItem::groupKey(TRUE, expert_info.severity, expert_info.group, QString(expert_info.protocol), expert_info.hf_index);
347 
348     ExpertPacketItem* expert_root = root_->child(groupKey);
349     if (expert_root == NULL) {
350         ExpertPacketItem *new_item = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), root_);
351 
352         root_->appendChild(new_item, groupKey);
353 
354         expert_root = new_item;
355     }
356 
357     ExpertPacketItem *expert = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_root);
358     expert_root->appendChild(expert, groupKey);
359 
360     //add the summary children off of the first child of the root children
361     ExpertPacketItem* summary_root = expert_root->child(0);
362 
363     //make a summary child
364     ExpertPacketItem* expert_summary_root = summary_root->child(summaryKey);
365     if (expert_summary_root == NULL) {
366         ExpertPacketItem *new_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), summary_root);
367 
368         summary_root->appendChild(new_summary, summaryKey);
369         expert_summary_root = new_summary;
370     }
371 
372     ExpertPacketItem *expert_summary = new ExpertPacketItem(expert_info, &(capture_file_.capFile()->cinfo), expert_summary_root);
373     expert_summary_root->appendChild(expert_summary, summaryKey);
374 }
375 
376 void ExpertInfoModel::tapReset(void *eid_ptr)
377 {
378     ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
379     if (!model)
380         return;
381 
382     model->clear();
383 }
384 
385 tap_packet_status ExpertInfoModel::tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data)
386 {
387     ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
388     const expert_info_t *expert_info = (const expert_info_t *) data;
389     tap_packet_status status = TAP_PACKET_DONT_REDRAW;
390 
391     if (!pinfo || !model || !expert_info)
392         return TAP_PACKET_DONT_REDRAW;
393 
394     model->addExpertInfo(*expert_info);
395 
396     if (model->numEvents((enum ExpertSeverity)expert_info->severity) < 1)
397         status = TAP_PACKET_REDRAW;
398 
399     model->eventCounts_[(enum ExpertSeverity)expert_info->severity]++;
400 
401     return status;
402 }
403 
404 void ExpertInfoModel::tapDraw(void *eid_ptr)
405 {
406     ExpertInfoModel *model = static_cast<ExpertInfoModel*>(eid_ptr);
407     if (!model)
408         return;
409 
410     emit model->beginResetModel();
411     emit model->endResetModel();
412 }
413