1 /* export_objects_model.cpp
2  * Data model for Export Objects.
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 "export_objects_model.h"
12 
13 #include <ui/qt/utils/qt_ui_utils.h>
14 #include <ui/qt/utils/variant_pointer.h>
15 #include <wsutil/filesystem.h>
16 #include <epan/prefs.h>
17 
18 #include <QDir>
19 
20 extern "C" {
21 
22 static void
object_list_add_entry(void * gui_data,export_object_entry_t * entry)23 object_list_add_entry(void *gui_data, export_object_entry_t *entry) {
24     export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data;
25 
26     if (object_list && object_list->model)
27         object_list->model->addObjectEntry(entry);
28 }
29 
30 static export_object_entry_t*
object_list_get_entry(void * gui_data,int row)31 object_list_get_entry(void *gui_data, int row) {
32     export_object_list_gui_t *object_list = (export_object_list_gui_t*)gui_data;
33 
34     if (object_list && object_list->model)
35         return object_list->model->objectEntry(row);
36 
37     return NULL;
38 }
39 
40 } // extern "C"
41 
42 
43 
44 
ExportObjectModel(register_eo_t * eo,QObject * parent)45 ExportObjectModel::ExportObjectModel(register_eo_t* eo, QObject *parent) :
46     QAbstractTableModel(parent),
47     eo_(eo)
48 {
49     eo_gui_data_.model = this;
50 
51     export_object_list_.add_entry = object_list_add_entry;
52     export_object_list_.get_entry = object_list_get_entry;
53     export_object_list_.gui_data = (void*)&eo_gui_data_;
54 }
55 
data(const QModelIndex & index,int role) const56 QVariant ExportObjectModel::data(const QModelIndex &index, int role) const
57 {
58     if ((!index.isValid()) || ((role != Qt::DisplayRole) && (role != Qt::UserRole))) {
59         return QVariant();
60     }
61 
62     if (role == Qt::DisplayRole)
63     {
64         export_object_entry_t *entry = VariantPointer<export_object_entry_t>::asPtr(objects_.value(index.row()));
65         if (entry == NULL)
66             return QVariant();
67 
68         switch(index.column())
69         {
70         case colPacket:
71             return QString::number(entry->pkt_num);
72         case colHostname:
73             return entry->hostname;
74         case colContent:
75             return entry->content_type;
76         case colSize:
77             return file_size_to_qstring(entry->payload_len);
78         case colFilename:
79             return entry->filename;
80         }
81     }
82     else if (role == Qt::UserRole)
83     {
84         return objects_.value(index.row());
85     }
86 
87     return QVariant();
88 }
89 
headerData(int section,Qt::Orientation orientation,int role) const90 QVariant ExportObjectModel::headerData(int section, Qt::Orientation orientation, int role) const
91 {
92     if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
93         return QVariant();
94 
95     switch (section) {
96     case colPacket:
97         return tr("Packet");
98     case colHostname:
99         return tr("Hostname");
100     case colContent:
101         return tr("Content Type");
102     case colSize:
103         return tr("Size");
104     case colFilename:
105         return tr("Filename");
106     }
107 
108     return QVariant();
109 }
110 
rowCount(const QModelIndex & parent) const111 int ExportObjectModel::rowCount(const QModelIndex &parent) const
112 {
113     // there are no children
114     if (parent.isValid()) {
115         return 0;
116     }
117 
118     return objects_.count();
119 }
120 
columnCount(const QModelIndex &) const121 int ExportObjectModel::columnCount(const QModelIndex&) const
122 {
123     return colExportObjectMax;
124 }
125 
addObjectEntry(export_object_entry_t * entry)126 void ExportObjectModel::addObjectEntry(export_object_entry_t *entry)
127 {
128     if (entry == NULL)
129         return;
130 
131     int count = objects_.count();
132     beginInsertRows(QModelIndex(), count, count);
133     objects_.append(VariantPointer<export_object_entry_t>::asQVariant(entry));
134     endInsertRows();
135 }
136 
objectEntry(int row)137 export_object_entry_t* ExportObjectModel::objectEntry(int row)
138 {
139     return VariantPointer<export_object_entry_t>::asPtr(objects_.value(row));
140 }
141 
saveEntry(QModelIndex & index,QString filename)142 bool ExportObjectModel::saveEntry(QModelIndex &index, QString filename)
143 {
144     if (!index.isValid() || filename.isEmpty())
145         return false;
146 
147     export_object_entry_t *entry = VariantPointer<export_object_entry_t>::asPtr(objects_.value(index.row()));
148     if (entry == NULL)
149         return false;
150 
151     if (filename.length() > 0) {
152         write_file_binary_mode(qUtf8Printable(filename), entry->payload_data, entry->payload_len);
153     }
154 
155     return true;
156 }
157 
saveAllEntries(QString path)158 void ExportObjectModel::saveAllEntries(QString path)
159 {
160     if (path.isEmpty())
161         return;
162 
163     QDir save_dir(path);
164     export_object_entry_t *entry;
165 
166     for (QList<QVariant>::iterator it = objects_.begin(); it != objects_.end(); ++it)
167     {
168         entry = VariantPointer<export_object_entry_t>::asPtr(*it);
169         if (entry == NULL)
170             continue;
171 
172         guint count = 0;
173         QString filename;
174 
175         do {
176             GString *safe_filename;
177 
178             if (entry->filename)
179                 safe_filename = eo_massage_str(entry->filename,
180                     EXPORT_OBJECT_MAXFILELEN, count);
181             else {
182                 char generic_name[EXPORT_OBJECT_MAXFILELEN+1];
183                 const char *ext;
184                 ext = eo_ct2ext(entry->content_type);
185                 g_snprintf(generic_name, sizeof(generic_name),
186                     "object%u%s%s", entry->pkt_num, ext ? "." : "",
187                     ext ? ext : "");
188                 safe_filename = eo_massage_str(generic_name,
189                     EXPORT_OBJECT_MAXFILELEN, count);
190             }
191             filename = QString::fromUtf8(safe_filename->str);
192             g_string_free(safe_filename, TRUE);
193         } while (save_dir.exists(filename) && ++count < prefs.gui_max_export_objects);
194         write_file_binary_mode(qUtf8Printable(save_dir.filePath(filename)),
195                                entry->payload_data, entry->payload_len);
196     }
197 }
198 
resetObjects()199 void ExportObjectModel::resetObjects()
200 {
201     export_object_gui_reset_cb reset_cb = get_eo_reset_func(eo_);
202 
203     emit beginResetModel();
204     objects_.clear();
205     emit endResetModel();
206 
207     if (reset_cb)
208         reset_cb();
209 }
210 
211 // Called by taps
212 /* Runs at the beginning of tapping only */
resetTap(void * tapdata)213 void ExportObjectModel::resetTap(void *tapdata)
214 {
215     export_object_list_t *tap_object = (export_object_list_t *)tapdata;
216     export_object_list_gui_t *object_list = (export_object_list_gui_t *)tap_object->gui_data;
217     if (object_list && object_list->model)
218         object_list->model->resetObjects();
219 }
220 
getTapListenerName()221 const char* ExportObjectModel::getTapListenerName()
222 {
223     return get_eo_tap_listener_name(eo_);
224 }
225 
getTapData()226 void* ExportObjectModel::getTapData()
227 {
228     return &export_object_list_;
229 }
230 
getTapPacketFunc()231 tap_packet_cb ExportObjectModel::getTapPacketFunc()
232 {
233     return get_eo_packet_func(eo_);
234 }
235 
removeTap()236 void ExportObjectModel::removeTap()
237 {
238     eo_gui_data_.model = NULL;
239 }
240 
241 
242 
ExportObjectProxyModel(QObject * parent)243 ExportObjectProxyModel::ExportObjectProxyModel(QObject * parent)
244     : QSortFilterProxyModel(parent)
245 {
246 
247 }
248 
lessThan(const QModelIndex & source_left,const QModelIndex & source_right) const249 bool ExportObjectProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
250 {
251     export_object_entry_t *left_entry = VariantPointer<export_object_entry_t>::asPtr(sourceModel()->data(source_left, Qt::UserRole)),
252                           *right_entry = VariantPointer<export_object_entry_t>::asPtr(sourceModel()->data(source_right, Qt::UserRole));
253 
254     if ((left_entry != NULL) && (right_entry != NULL))
255     {
256         switch (source_left.column())
257         {
258         case ExportObjectModel::colPacket:
259             return left_entry->pkt_num < right_entry->pkt_num;
260         case ExportObjectModel::colSize:
261             return left_entry->payload_len < right_entry->payload_len;
262         case ExportObjectModel::colFilename:
263             break;
264         }
265     }
266 
267     return QSortFilterProxyModel::lessThan(source_left, source_right);
268 }
269 
setContentFilterString(QString filter_)270 void ExportObjectProxyModel::setContentFilterString(QString filter_)
271 {
272     contentFilter_ = filter_;
273     invalidateFilter();
274 }
275 
setTextFilterString(QString filter_)276 void ExportObjectProxyModel::setTextFilterString(QString filter_)
277 {
278     textFilter_ = filter_;
279     invalidateFilter();
280 }
281 
filterAcceptsRow(int source_row,const QModelIndex &) const282 bool ExportObjectProxyModel::filterAcceptsRow(int source_row, const QModelIndex &/*source_parent*/) const
283 {
284     if (contentFilter_.length() > 0)
285     {
286         QModelIndex idx = sourceModel()->index(source_row, ExportObjectModel::colContent);
287         if (!idx.isValid())
288             return false;
289 
290         if (contentFilter_.compare(idx.data().toString()) != 0)
291             return false;
292     }
293 
294     if (textFilter_.length() > 0)
295     {
296         QModelIndex hostIdx = sourceModel()->index(source_row, ExportObjectModel::colHostname);
297         QModelIndex fileIdx = sourceModel()->index(source_row, ExportObjectModel::colFilename);
298         if (!hostIdx.isValid() || !fileIdx.isValid())
299             return false;
300 
301         QString host = hostIdx.data().toString();
302         QString file = fileIdx.data().toString();
303 
304         if (!host.contains(textFilter_) && !file.contains(textFilter_))
305             return false;
306     }
307 
308     return true;
309 }
310