3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0+
12 #include "about_dialog.h"
13 #include <ui_about_dialog.h>
15 #include "wireshark_application.h"
16 #include <wsutil/filesystem.h>
19 #include <epan/oids.h>
22 #include <epan/geoip_db.h>
25 #include <epan/wslua/init_wslua.h>
29 #include "epan/register.h"
31 #include "ui/alert_box.h"
32 #include "ui/last_open_dir.h"
33 #include "ui/help_url.h"
34 #include "ui/text_import_scanner.h"
35 #include <wsutil/utf8_entities.h>
38 #include "wsutil/file_util.h"
39 #include "wsutil/tempfile.h"
40 #include "wsutil/plugins.h"
41 #include "wsutil/copyright_info.h"
42 #include "version_info.h"
48 #include <ui/qt/utils/qt_ui_utils.h>
49 #include <ui/qt/utils/variant_pointer.h>
50 #include <ui/qt/models/astringlist_list_model.h>
51 #include <ui/qt/models/url_link_delegate.h>
52 #include <ui/qt/models/html_text_delegate.h>
54 #include <QFontMetrics>
55 #include <QKeySequence>
56 #include <QTextStream>
59 #include <QAbstractItemModel>
61 #include <QDesktopServices>
66 AuthorListModel::AuthorListModel(QObject * parent) :
67 AStringListListModel(parent)
72 f_authors.setFileName(get_datafile_path("AUTHORS-SHORT"));
73 f_authors.open(QFile::ReadOnly | QFile::Text);
74 QTextStream ReadFile_authors(&f_authors);
75 ReadFile_authors.setCodec("UTF-8");
77 QRegExp rx("(.*)[<(]([\\s'a-zA-Z0-9._%+-]+(\\[[Aa][Tt]\\])?[a-zA-Z0-9._%+-]+)[>)]");
78 acknowledgement_.clear();
79 while (!ReadFile_authors.atEnd()) {
80 QString line = ReadFile_authors.readLine();
82 if ( ! readAck && line.trimmed().length() == 0 )
84 if ( line.startsWith("------") )
87 if ( line.compare(QStringLiteral("Acknowledgements") ) == 0 )
92 else if ( rx.indexIn(line) != -1 )
93 appendRow( QStringList() << rx.cap(1).trimmed() << rx.cap(2).trimmed());
96 acknowledgement_.append(QString("%1\n").arg(line));
102 AuthorListModel::~AuthorListModel() { }
104 QString AuthorListModel::acknowledgment() const
106 return acknowledgement_;
109 QStringList AuthorListModel::headerColumns() const
111 return QStringList() << tr("Name") << tr("E-Mail");
114 static void plugins_add_description(const char *name, const char *version,
115 const char *types, const char *filename,
118 QList<QStringList> *plugin_data = (QList<QStringList> *)user_data;
119 QStringList plugin_row = QStringList() << name << version << types << filename;
120 *plugin_data << plugin_row;
123 PluginListModel::PluginListModel(QObject * parent) : AStringListListModel(parent)
125 QList<QStringList> plugin_data;
127 plugins_get_descriptions(plugins_add_description, &plugin_data);
131 wslua_plugins_get_descriptions(plugins_add_description, &plugin_data);
135 GHashTable * tools = extcap_loaded_interfaces();
136 if (tools && g_hash_table_size(tools) > 0) {
137 GList * walker = g_list_first(g_hash_table_get_keys(tools));
138 while (walker && walker->data) {
139 extcap_info * tool = (extcap_info *)g_hash_table_lookup(tools, walker->data);
141 QStringList plugin_row = QStringList() << tool->basename << tool->version << "extcap" << tool->full_path;
142 plugin_data << plugin_row;
144 walker = g_list_next(walker);
149 typeNames_ << QString("");
150 foreach(QStringList row, plugin_data)
152 typeNames_ << row.at(2);
157 typeNames_.removeDuplicates();
160 QStringList PluginListModel::typeNames() const
165 QStringList PluginListModel::headerColumns() const
167 return QStringList() << tr("Name") << tr("Version") << tr("Type") << tr("Path");
170 ShortcutListModel::ShortcutListModel(QObject * parent):
171 AStringListListModel(parent)
173 QMap<QString, QPair<QString, QString> > shortcuts; // name -> (shortcut, description)
174 foreach (const QWidget *child, wsApp->mainWindow()->findChildren<QWidget *>()) {
175 // Recent items look funny here.
176 if (child->objectName().compare("menuOpenRecentCaptureFile") == 0) continue;
177 foreach (const QAction *action, child->actions()) {
179 if (!action->shortcut().isEmpty()) {
180 QString name = action->text();
181 name.replace('&', "");
182 shortcuts[name] = QPair<QString, QString>(action->shortcut().toString(QKeySequence::NativeText), action->toolTip());
187 QStringList names = shortcuts.keys();
189 foreach (const QString &name, names) {
191 row << shortcuts[name].first << name << shortcuts[name].second;
196 QStringList ShortcutListModel::headerColumns() const
198 return QStringList() << tr("Shortcut") << tr("Name") << tr("Description");
201 FolderListModel::FolderListModel(QObject * parent):
202 AStringListListModel(parent)
205 appendRow( QStringList() << "\"File\" dialogs" << get_last_open_dir() << "capture files");
208 appendRow( QStringList() << "Temp" << g_get_tmp_dir() << "untitled capture files");
211 appendRow( QStringList() << "Personal configuration"
212 << gchar_free_to_qstring(get_persconffile_path("", FALSE))
213 << "<i>dfilters</i>, <i>preferences</i>, <i>ethers</i>, " UTF8_HORIZONTAL_ELLIPSIS);
216 QString dirPath = get_datafile_dir();
217 if (! dirPath.isEmpty()) {
218 appendRow ( QStringList() << "Global configuration" << dirPath
219 << "<i>dfilters</i>, <i>preferences</i>, <i>manuf</i>, " UTF8_HORIZONTAL_ELLIPSIS);
223 appendRow( QStringList() << "System" << get_systemfile_dir() << "<i>ethers</i>, <i>ipxnets</i>");
226 appendRow( QStringList() << "Program" << get_progfile_dir() << "program files");
230 appendRow( QStringList() << "Personal Plugins" << get_plugins_pers_dir_with_version() << "binary plugins");
233 appendRow( QStringList() << "Global Plugins" << get_plugins_dir_with_version() << "binary plugins");
238 appendRow( QStringList() << "Personal Lua Plugins" << get_plugins_pers_dir() << "lua scripts");
241 appendRow( QStringList() << "Global Lua Plugins" << get_plugins_dir() << "lua scripts");
246 QStringList extPaths = QString(get_extcap_dir()).split(G_SEARCHPATH_SEPARATOR_S);
248 foreach(QString path, extPaths)
249 appendRow( QStringList() << "Extcap path" << path.trimmed() << "Extcap Plugins search path");
255 QStringList geoIpPaths = QString(geoip_db_get_paths()).split(G_SEARCHPATH_SEPARATOR_S);
256 foreach(QString path, geoIpPaths)
257 appendRow( QStringList() << "GeoIP path" << path.trimmed() << "GeoIP database search path");
262 QStringList smiPaths = QString(oid_get_default_mib_path()).split(G_SEARCHPATH_SEPARATOR_S);
263 foreach(QString path, smiPaths)
264 appendRow( QStringList() << "MIB/PIB path" << path.trimmed() << "SMI MIB/PIB search path");
268 QStringList FolderListModel::headerColumns() const
270 return QStringList() << tr("Name") << tr("Location") << tr("Typical Files");
274 // - Tweak and enhance ui...
276 AboutDialog::AboutDialog(QWidget *parent) :
278 ui(new Ui::AboutDialog)
281 setAttribute(Qt::WA_DeleteOnClose, true);
285 GString *comp_info_str = get_compiled_version_info(get_wireshark_qt_compiled_info,
286 get_gui_compiled_info);
287 GString *runtime_info_str = get_runtime_version_info(get_wireshark_runtime_info);
290 AuthorListModel * authorModel = new AuthorListModel(this);
291 AStringListListSortFilterProxyModel * proxyAuthorModel = new AStringListListSortFilterProxyModel(this);
292 proxyAuthorModel->setSourceModel(authorModel);
293 proxyAuthorModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
294 proxyAuthorModel->setColumnToFilter(0);
295 proxyAuthorModel->setColumnToFilter(1);
296 ui->tblAuthors->setModel(proxyAuthorModel);
297 ui->tblAuthors->setRootIsDecorated(false);
298 ui->pte_Authors->clear();
299 ui->pte_Authors->appendPlainText(authorModel->acknowledgment());
300 ui->pte_Authors->moveCursor(QTextCursor::Start);
302 ui->tblAuthors->setContextMenuPolicy(Qt::CustomContextMenu);
303 connect(ui->tblAuthors, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu);
304 connect(ui->searchAuthors, SIGNAL(textChanged(QString)), proxyAuthorModel, SLOT(setFilter(QString)));
308 /* Construct the message string */
318 "Wireshark is Open Source Software released under the GNU General Public License.\n"
320 "Check the man page and http://www.wireshark.org for more information.")
321 .arg(get_ws_vcs_version_info(), get_copyright_info(), comp_info_str->str, runtime_info_str->str);
323 ui->label_wireshark->setTextInteractionFlags(Qt::TextSelectableByMouse);
324 ui->label_wireshark->setText(message);
326 /* Check if it is a dev release... (VERSION_MINOR is odd in dev release) */
327 #if VERSION_MINOR & 1
328 ui->label_logo->setPixmap(QPixmap(":/about/wssplash_dev.png"));
332 FolderListModel * folderModel = new FolderListModel(this);
333 AStringListListSortFilterProxyModel * folderProxyModel = new AStringListListSortFilterProxyModel(this);
334 folderProxyModel->setSourceModel(folderModel);
335 folderProxyModel->setColumnToFilter(1);
336 folderProxyModel->setFilterType(AStringListListSortFilterProxyModel::FilterByStart);
337 AStringListListUrlProxyModel * folderDisplayModel = new AStringListListUrlProxyModel(this);
338 folderDisplayModel->setSourceModel(folderProxyModel);
339 folderDisplayModel->setUrlColumn(1);
340 ui->tblFolders->setModel(folderDisplayModel);
341 ui->tblFolders->setRootIsDecorated(false);
342 ui->tblFolders->setItemDelegateForColumn(1, new UrlLinkDelegate(this));
343 ui->tblFolders->setItemDelegateForColumn(2, new HTMLTextDelegate(this));
344 ui->tblFolders->setContextMenuPolicy(Qt::CustomContextMenu);
345 connect(ui->tblFolders, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu);
346 connect(ui->searchFolders, SIGNAL(textChanged(QString)), folderProxyModel, SLOT(setFilter(QString)));
347 connect(ui->tblFolders, &QTreeView::clicked, this, &AboutDialog::urlClicked );
351 #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) || defined(HAVE_EXTCAP)
353 PluginListModel * pluginModel = new PluginListModel(this);
354 AStringListListSortFilterProxyModel * pluginFilterModel = new AStringListListSortFilterProxyModel(this);
355 pluginFilterModel->setSourceModel(pluginModel);
356 pluginFilterModel->setColumnToFilter(0);
357 AStringListListSortFilterProxyModel * pluginTypeModel = new AStringListListSortFilterProxyModel(this);
358 pluginTypeModel->setSourceModel(pluginFilterModel);
359 pluginTypeModel->setColumnToFilter(2);
360 ui->tblPlugins->setModel(pluginTypeModel);
361 ui->tblPlugins->setRootIsDecorated(false);
362 ui->cmbType->addItems(pluginModel->typeNames());
363 ui->tblPlugins->setContextMenuPolicy(Qt::CustomContextMenu);
364 connect(ui->tblPlugins, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu);
365 connect(ui->searchPlugins, SIGNAL(textChanged(QString)), pluginFilterModel, SLOT(setFilter(QString)));
366 connect(ui->cmbType, SIGNAL(currentIndexChanged(QString)), pluginTypeModel, SLOT(setFilter(QString)));
369 ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tab_plugins));
373 ShortcutListModel * shortcutModel = new ShortcutListModel(this);
374 AStringListListSortFilterProxyModel * shortcutProxyModel = new AStringListListSortFilterProxyModel(this);
375 shortcutProxyModel->setSourceModel(shortcutModel);
376 shortcutProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
377 shortcutProxyModel->setColumnToFilter(1);
378 shortcutProxyModel->setColumnToFilter(2);
379 ui->tblShortcuts->setModel(shortcutProxyModel);
380 ui->tblShortcuts->setRootIsDecorated(false);
381 ui->tblShortcuts->setContextMenuPolicy(Qt::CustomContextMenu);
382 connect(ui->tblShortcuts, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu);
383 connect(ui->searchShortcuts, SIGNAL(textChanged(QString)), shortcutProxyModel, SLOT(setFilter(QString)));
387 f_license.setFileName(get_datafile_path("COPYING.txt"));
389 f_license.setFileName(get_datafile_path("COPYING"));
392 f_license.open(QFile::ReadOnly | QFile::Text);
393 QTextStream ReadFile_license(&f_license);
395 ui->pte_License->setFont(wsApp->monospaceFont());
396 ui->pte_License->insertPlainText(ReadFile_license.readAll());
397 ui->pte_License->moveCursor(QTextCursor::Start);
401 AboutDialog::~AboutDialog()
406 void AboutDialog::resizeEvent(QResizeEvent * event)
408 QList<QWidget *> pages;
410 pages << ui->tab_authors << ui->tab_folders << ui->tab_plugins << ui->tab_shortcuts;
412 foreach ( QWidget * tabPage, pages )
414 QList<QTreeView *> childs = tabPage->findChildren<QTreeView*>();
415 if ( childs.count() == 0 )
418 QTreeView * tree = childs.at(0);
420 int columnCount = tree->model()->columnCount();
421 for ( int cnt = 0; cnt < columnCount; cnt++ )
422 tree->setColumnWidth(cnt, tabPage->width() / columnCount);
423 tree->header()->setStretchLastSection(true);
426 QDialog::resizeEvent(event);
429 void AboutDialog::urlClicked(const QModelIndex &idx)
431 QTreeView * table = qobject_cast<QTreeView *>(sender());
435 QString urlText = table->model()->data(idx).toString();
436 if ( urlText.isEmpty() )
439 QFileInfo fi (urlText);
440 if ( fi.isDir() && fi.exists() )
442 QUrl url = QUrl::fromLocalFile(urlText);
444 QDesktopServices::openUrl(url);
448 void AboutDialog::handleCopyMenu(QPoint pos)
450 QTreeView * tree = qobject_cast<QTreeView *>(sender());
454 QModelIndex index = tree->indexAt(pos);
455 if ( ! index.isValid() )
458 QMenu * menu = new QMenu(this);
460 QAction * copyColumnAction = menu->addAction(tr("Copy"));
461 copyColumnAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
462 connect(copyColumnAction, &QAction::triggered, this, &AboutDialog::copyActionTriggered);
464 QAction * copyRowAction = menu->addAction(tr("Copy Row(s)"));
465 copyRowAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
466 connect(copyRowAction, &QAction::triggered, this, &AboutDialog::copyRowActionTriggered);
468 menu->popup(tree->viewport()->mapToGlobal(pos));
471 void AboutDialog::copyRowActionTriggered()
473 copyActionTriggered(true);
476 void AboutDialog::copyActionTriggered(bool copyRow)
478 QAction * sendingAction = qobject_cast<QAction *>(sender());
479 if ( ! sendingAction )
482 QTreeView * tree = VariantPointer<QTreeView>::asPtr(sendingAction->data());
484 QModelIndexList selIndeces = tree->selectionModel()->selectedIndexes();
489 QMenu * menu = qobject_cast<QMenu *>(sendingAction->parentWidget());
492 QPoint menuPosOnTable = tree->mapFromGlobal(menu->pos());
493 QModelIndex clickedIndex = tree->indexAt(menuPosOnTable);
494 if ( clickedIndex.isValid() )
495 copyColumn = clickedIndex.column();
500 if ( selIndeces.count() > 0 )
502 int columnCount = tree->model()->columnCount();
503 QList<int> visitedRows;
505 foreach(QModelIndex index, selIndeces)
507 if ( visitedRows.contains(index.row()) )
513 for ( int cnt = 0; cnt < columnCount; cnt++ )
515 QModelIndex dataIdx = tree->model()->index(index.row(), cnt);
516 row << tree->model()->data(dataIdx).toString();
521 if ( copyColumn < 0 )
522 copyColumn = index.column();
524 QModelIndex dataIdx = tree->model()->index(index.row(), copyColumn);
525 row << tree->model()->data(dataIdx).toString();
528 clipdata.append(row.join("\t\t").append("\n"));
530 visitedRows << index.row();
533 QClipboard * clipBoard = QApplication::clipboard();
534 clipBoard->setText(clipdata);
543 * indent-tabs-mode: nil
546 * ex: set shiftwidth=4 tabstop=8 expandtab:
547 * :indentSize=4:tabSize=8:noTabs=true: