/* about_dialog.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include "about_dialog.h" #include #include "wireshark_application.h" #include #ifdef HAVE_LIBSMI #include #endif #ifdef HAVE_GEOIP #include #endif #ifdef HAVE_LUA #include #endif #include "log.h" #include "epan/register.h" #include "ui/alert_box.h" #include "ui/last_open_dir.h" #include "ui/help_url.h" #include "ui/text_import_scanner.h" #include #include "file.h" #include "wsutil/file_util.h" #include "wsutil/tempfile.h" #include "wsutil/plugins.h" #include "wsutil/copyright_info.h" #include "version_info.h" #ifdef HAVE_EXTCAP #include "extcap.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include AuthorListModel::AuthorListModel(QObject * parent) : AStringListListModel(parent) { bool readAck = false; QFile f_authors; f_authors.setFileName(get_datafile_path("AUTHORS-SHORT")); f_authors.open(QFile::ReadOnly | QFile::Text); QTextStream ReadFile_authors(&f_authors); ReadFile_authors.setCodec("UTF-8"); QRegExp rx("(.*)[<(]([\\s'a-zA-Z0-9._%+-]+(\\[[Aa][Tt]\\])?[a-zA-Z0-9._%+-]+)[>)]"); acknowledgement_.clear(); while (!ReadFile_authors.atEnd()) { QString line = ReadFile_authors.readLine(); if ( ! readAck && line.trimmed().length() == 0 ) continue; if ( line.startsWith("------") ) continue; if ( line.compare(QStringLiteral("Acknowledgements") ) == 0 ) { readAck = true; continue; } else if ( rx.indexIn(line) != -1 ) appendRow( QStringList() << rx.cap(1).trimmed() << rx.cap(2).trimmed()); if ( readAck ) acknowledgement_.append(QString("%1\n").arg(line)); } f_authors.close(); } AuthorListModel::~AuthorListModel() { } QString AuthorListModel::acknowledgment() const { return acknowledgement_; } QStringList AuthorListModel::headerColumns() const { return QStringList() << tr("Name") << tr("E-Mail"); } static void plugins_add_description(const char *name, const char *version, const char *types, const char *filename, void *user_data) { QList *plugin_data = (QList *)user_data; QStringList plugin_row = QStringList() << name << version << types << filename; *plugin_data << plugin_row; } PluginListModel::PluginListModel(QObject * parent) : AStringListListModel(parent) { QList plugin_data; #ifdef HAVE_PLUGINS plugins_get_descriptions(plugins_add_description, &plugin_data); #endif #ifdef HAVE_LUA wslua_plugins_get_descriptions(plugins_add_description, &plugin_data); #endif #ifdef HAVE_EXTCAP GHashTable * tools = extcap_loaded_interfaces(); if (tools && g_hash_table_size(tools) > 0) { GList * walker = g_list_first(g_hash_table_get_keys(tools)); while (walker && walker->data) { extcap_info * tool = (extcap_info *)g_hash_table_lookup(tools, walker->data); if (tool) { QStringList plugin_row = QStringList() << tool->basename << tool->version << "extcap" << tool->full_path; plugin_data << plugin_row; } walker = g_list_next(walker); } } #endif typeNames_ << QString(""); foreach(QStringList row, plugin_data) { typeNames_ << row.at(2); appendRow(row); } typeNames_.sort(); typeNames_.removeDuplicates(); } QStringList PluginListModel::typeNames() const { return typeNames_; } QStringList PluginListModel::headerColumns() const { return QStringList() << tr("Name") << tr("Version") << tr("Type") << tr("Path"); } ShortcutListModel::ShortcutListModel(QObject * parent): AStringListListModel(parent) { QMap > shortcuts; // name -> (shortcut, description) foreach (const QWidget *child, wsApp->mainWindow()->findChildren()) { // Recent items look funny here. if (child->objectName().compare("menuOpenRecentCaptureFile") == 0) continue; foreach (const QAction *action, child->actions()) { if (!action->shortcut().isEmpty()) { QString name = action->text(); name.replace('&', ""); shortcuts[name] = QPair(action->shortcut().toString(QKeySequence::NativeText), action->toolTip()); } } } QStringList names = shortcuts.keys(); names.sort(); foreach (const QString &name, names) { QStringList row; row << shortcuts[name].first << name << shortcuts[name].second; appendRow(row); } } QStringList ShortcutListModel::headerColumns() const { return QStringList() << tr("Shortcut") << tr("Name") << tr("Description"); } FolderListModel::FolderListModel(QObject * parent): AStringListListModel(parent) { /* "file open" */ appendRow( QStringList() << "\"File\" dialogs" << get_last_open_dir() << "capture files"); /* temp */ appendRow( QStringList() << "Temp" << g_get_tmp_dir() << "untitled capture files"); /* pers conf */ appendRow( QStringList() << "Personal configuration" << gchar_free_to_qstring(get_persconffile_path("", FALSE)) << "dfilters, preferences, ethers, " UTF8_HORIZONTAL_ELLIPSIS); /* global conf */ QString dirPath = get_datafile_dir(); if (! dirPath.isEmpty()) { appendRow ( QStringList() << "Global configuration" << dirPath << "dfilters, preferences, manuf, " UTF8_HORIZONTAL_ELLIPSIS); } /* system */ appendRow( QStringList() << "System" << get_systemfile_dir() << "ethers, ipxnets"); /* program */ appendRow( QStringList() << "Program" << get_progfile_dir() << "program files"); #ifdef HAVE_PLUGINS /* pers plugins */ appendRow( QStringList() << "Personal Plugins" << get_plugins_pers_dir_with_version() << "binary plugins"); /* global plugins */ appendRow( QStringList() << "Global Plugins" << get_plugins_dir_with_version() << "binary plugins"); #endif #ifdef HAVE_LUA /* pers plugins */ appendRow( QStringList() << "Personal Lua Plugins" << get_plugins_pers_dir() << "lua scripts"); /* global plugins */ appendRow( QStringList() << "Global Lua Plugins" << get_plugins_dir() << "lua scripts"); #endif #ifdef HAVE_EXTCAP /* Extcap */ QStringList extPaths = QString(get_extcap_dir()).split(G_SEARCHPATH_SEPARATOR_S); foreach(QString path, extPaths) appendRow( QStringList() << "Extcap path" << path.trimmed() << "Extcap Plugins search path"); #endif #ifdef HAVE_GEOIP /* GeoIP */ QStringList geoIpPaths = QString(geoip_db_get_paths()).split(G_SEARCHPATH_SEPARATOR_S); foreach(QString path, geoIpPaths) appendRow( QStringList() << "GeoIP path" << path.trimmed() << "GeoIP database search path"); #endif #ifdef HAVE_LIBSMI /* SMI MIBs/PIBs */ QStringList smiPaths = QString(oid_get_default_mib_path()).split(G_SEARCHPATH_SEPARATOR_S); foreach(QString path, smiPaths) appendRow( QStringList() << "MIB/PIB path" << path.trimmed() << "SMI MIB/PIB search path"); #endif } QStringList FolderListModel::headerColumns() const { return QStringList() << tr("Name") << tr("Location") << tr("Typical Files"); } // To do: // - Tweak and enhance ui... AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); QFile f_license; QString message; GString *comp_info_str = get_compiled_version_info(get_wireshark_qt_compiled_info, get_gui_compiled_info); GString *runtime_info_str = get_runtime_version_info(get_wireshark_runtime_info); AuthorListModel * authorModel = new AuthorListModel(this); AStringListListSortFilterProxyModel * proxyAuthorModel = new AStringListListSortFilterProxyModel(this); proxyAuthorModel->setSourceModel(authorModel); proxyAuthorModel->setFilterCaseSensitivity(Qt::CaseInsensitive); proxyAuthorModel->setColumnToFilter(0); proxyAuthorModel->setColumnToFilter(1); ui->tblAuthors->setModel(proxyAuthorModel); ui->tblAuthors->setRootIsDecorated(false); ui->pte_Authors->clear(); ui->pte_Authors->appendPlainText(authorModel->acknowledgment()); ui->pte_Authors->moveCursor(QTextCursor::Start); ui->tblAuthors->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tblAuthors, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); connect(ui->searchAuthors, SIGNAL(textChanged(QString)), proxyAuthorModel, SLOT(setFilter(QString))); /* Wireshark tab */ /* Construct the message string */ message = QString( "Version %1\n" "\n" "%2" "\n" "%3" "\n" "%4" "\n" "Wireshark is Open Source Software released under the GNU General Public License.\n" "\n" "Check the man page and http://www.wireshark.org for more information.") .arg(get_ws_vcs_version_info(), get_copyright_info(), comp_info_str->str, runtime_info_str->str); ui->label_wireshark->setTextInteractionFlags(Qt::TextSelectableByMouse); ui->label_wireshark->setText(message); /* Check if it is a dev release... (VERSION_MINOR is odd in dev release) */ #if VERSION_MINOR & 1 ui->label_logo->setPixmap(QPixmap(":/about/wssplash_dev.png")); #endif /* Folders */ FolderListModel * folderModel = new FolderListModel(this); AStringListListSortFilterProxyModel * folderProxyModel = new AStringListListSortFilterProxyModel(this); folderProxyModel->setSourceModel(folderModel); folderProxyModel->setColumnToFilter(1); folderProxyModel->setFilterType(AStringListListSortFilterProxyModel::FilterByStart); AStringListListUrlProxyModel * folderDisplayModel = new AStringListListUrlProxyModel(this); folderDisplayModel->setSourceModel(folderProxyModel); folderDisplayModel->setUrlColumn(1); ui->tblFolders->setModel(folderDisplayModel); ui->tblFolders->setRootIsDecorated(false); ui->tblFolders->setItemDelegateForColumn(1, new UrlLinkDelegate(this)); ui->tblFolders->setItemDelegateForColumn(2, new HTMLTextDelegate(this)); ui->tblFolders->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tblFolders, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); connect(ui->searchFolders, SIGNAL(textChanged(QString)), folderProxyModel, SLOT(setFilter(QString))); connect(ui->tblFolders, &QTreeView::clicked, this, &AboutDialog::urlClicked ); /* Plugins */ #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) || defined(HAVE_EXTCAP) PluginListModel * pluginModel = new PluginListModel(this); AStringListListSortFilterProxyModel * pluginFilterModel = new AStringListListSortFilterProxyModel(this); pluginFilterModel->setSourceModel(pluginModel); pluginFilterModel->setColumnToFilter(0); AStringListListSortFilterProxyModel * pluginTypeModel = new AStringListListSortFilterProxyModel(this); pluginTypeModel->setSourceModel(pluginFilterModel); pluginTypeModel->setColumnToFilter(2); ui->tblPlugins->setModel(pluginTypeModel); ui->tblPlugins->setRootIsDecorated(false); ui->cmbType->addItems(pluginModel->typeNames()); ui->tblPlugins->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tblPlugins, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); connect(ui->searchPlugins, SIGNAL(textChanged(QString)), pluginFilterModel, SLOT(setFilter(QString))); connect(ui->cmbType, SIGNAL(currentIndexChanged(QString)), pluginTypeModel, SLOT(setFilter(QString))); #else ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tab_plugins)); #endif /* Shortcuts */ ShortcutListModel * shortcutModel = new ShortcutListModel(this); AStringListListSortFilterProxyModel * shortcutProxyModel = new AStringListListSortFilterProxyModel(this); shortcutProxyModel->setSourceModel(shortcutModel); shortcutProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); shortcutProxyModel->setColumnToFilter(1); shortcutProxyModel->setColumnToFilter(2); ui->tblShortcuts->setModel(shortcutProxyModel); ui->tblShortcuts->setRootIsDecorated(false); ui->tblShortcuts->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tblShortcuts, &QTreeView::customContextMenuRequested, this, &AboutDialog::handleCopyMenu); connect(ui->searchShortcuts, SIGNAL(textChanged(QString)), shortcutProxyModel, SLOT(setFilter(QString))); /* License */ #if defined(_WIN32) f_license.setFileName(get_datafile_path("COPYING.txt")); #else f_license.setFileName(get_datafile_path("COPYING")); #endif f_license.open(QFile::ReadOnly | QFile::Text); QTextStream ReadFile_license(&f_license); ui->pte_License->setFont(wsApp->monospaceFont()); ui->pte_License->insertPlainText(ReadFile_license.readAll()); ui->pte_License->moveCursor(QTextCursor::Start); } AboutDialog::~AboutDialog() { delete ui; } void AboutDialog::resizeEvent(QResizeEvent * event) { QList pages; pages << ui->tab_authors << ui->tab_folders << ui->tab_plugins << ui->tab_shortcuts; foreach ( QWidget * tabPage, pages ) { QList childs = tabPage->findChildren(); if ( childs.count() == 0 ) continue; QTreeView * tree = childs.at(0); int columnCount = tree->model()->columnCount(); for ( int cnt = 0; cnt < columnCount; cnt++ ) tree->setColumnWidth(cnt, tabPage->width() / columnCount); tree->header()->setStretchLastSection(true); } QDialog::resizeEvent(event); } void AboutDialog::urlClicked(const QModelIndex &idx) { QTreeView * table = qobject_cast(sender()); if ( ! table ) return; QString urlText = table->model()->data(idx).toString(); if ( urlText.isEmpty() ) return; QFileInfo fi (urlText); if ( fi.isDir() && fi.exists() ) { QUrl url = QUrl::fromLocalFile(urlText); if ( url.isValid() ) QDesktopServices::openUrl(url); } } void AboutDialog::handleCopyMenu(QPoint pos) { QTreeView * tree = qobject_cast(sender()); if ( ! tree ) return; QModelIndex index = tree->indexAt(pos); if ( ! index.isValid() ) return; QMenu * menu = new QMenu(this); QAction * copyColumnAction = menu->addAction(tr("Copy")); copyColumnAction->setData(VariantPointer::asQVariant(tree)); connect(copyColumnAction, &QAction::triggered, this, &AboutDialog::copyActionTriggered); QAction * copyRowAction = menu->addAction(tr("Copy Row(s)")); copyRowAction->setData(VariantPointer::asQVariant(tree)); connect(copyRowAction, &QAction::triggered, this, &AboutDialog::copyRowActionTriggered); menu->popup(tree->viewport()->mapToGlobal(pos)); } void AboutDialog::copyRowActionTriggered() { copyActionTriggered(true); } void AboutDialog::copyActionTriggered(bool copyRow) { QAction * sendingAction = qobject_cast(sender()); if ( ! sendingAction ) return; QTreeView * tree = VariantPointer::asPtr(sendingAction->data()); QModelIndexList selIndeces = tree->selectionModel()->selectedIndexes(); int copyColumn = -1; if ( ! copyRow ) { QMenu * menu = qobject_cast(sendingAction->parentWidget()); if ( menu ) { QPoint menuPosOnTable = tree->mapFromGlobal(menu->pos()); QModelIndex clickedIndex = tree->indexAt(menuPosOnTable); if ( clickedIndex.isValid() ) copyColumn = clickedIndex.column(); } } QString clipdata; if ( selIndeces.count() > 0 ) { int columnCount = tree->model()->columnCount(); QList visitedRows; foreach(QModelIndex index, selIndeces) { if ( visitedRows.contains(index.row()) ) continue; QStringList row; if ( copyRow ) { for ( int cnt = 0; cnt < columnCount; cnt++ ) { QModelIndex dataIdx = tree->model()->index(index.row(), cnt); row << tree->model()->data(dataIdx).toString(); } } else { if ( copyColumn < 0 ) copyColumn = index.column(); QModelIndex dataIdx = tree->model()->index(index.row(), copyColumn); row << tree->model()->data(dataIdx).toString(); } clipdata.append(row.join("\t\t").append("\n")); visitedRows << index.row(); } } QClipboard * clipBoard = QApplication::clipboard(); clipBoard->setText(clipdata); } /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */