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>
18 #include <QDesktopServices>
22 #include <epan/oids.h>
25 #include <epan/geoip_db.h>
28 #include <epan/wslua/init_wslua.h>
32 #include "epan/register.h"
34 #include "ui/alert_box.h"
35 #include "ui/last_open_dir.h"
36 #include "ui/help_url.h"
37 #include "ui/text_import_scanner.h"
38 #include <wsutil/utf8_entities.h>
41 #include "wsutil/file_util.h"
42 #include "wsutil/tempfile.h"
43 #include "wsutil/plugins.h"
44 #include "wsutil/copyright_info.h"
45 #include "version_info.h"
49 #include <ui/qt/utils/qt_ui_utils.h>
50 #include <ui/qt/utils/variant_pointer.h>
51 #include <ui/qt/models/astringlist_list_model.h>
52 #include <ui/qt/models/url_link_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 == "Acknowledgements" )
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("Email");
114 #if defined(HAVE_PLUGINS) || defined(HAVE_LUA)
115 static void plugins_add_description(const char *name, const char *version,
116 const char *types, const char *filename,
119 QList<QStringList> *plugin_data = (QList<QStringList> *)user_data;
120 QStringList plugin_row = QStringList() << name << version << types << filename;
121 *plugin_data << plugin_row;
125 PluginListModel::PluginListModel(QObject * parent) : AStringListListModel(parent)
127 QList<QStringList> plugin_data;
129 plugins_get_descriptions(plugins_add_description, &plugin_data);
133 wslua_plugins_get_descriptions(plugins_add_description, &plugin_data);
136 GHashTable * tools = extcap_loaded_interfaces();
137 if (tools && g_hash_table_size(tools) > 0) {
138 GList * walker = g_list_first(g_hash_table_get_keys(tools));
139 while (walker && walker->data) {
140 extcap_info * tool = (extcap_info *)g_hash_table_lookup(tools, walker->data);
142 QStringList plugin_row = QStringList() << tool->basename << tool->version << tr("extcap") << tool->full_path;
143 plugin_data << plugin_row;
145 walker = g_list_next(walker);
149 typeNames_ << QString("");
150 foreach(QStringList row, plugin_data)
152 QString type_name = row.at(2);
154 typeNames_ << type_name;
157 if (type_name == wslua_plugin_type_name()) {
158 tooltip = tr("Double-click to edit");
161 appendRow(row, tooltip);
165 typeNames_.removeDuplicates();
168 QStringList PluginListModel::typeNames() const
173 QStringList PluginListModel::headerColumns() const
175 return QStringList() << tr("Name") << tr("Version") << tr("Type") << tr("Path");
178 ShortcutListModel::ShortcutListModel(QObject * parent):
179 AStringListListModel(parent)
181 QMap<QString, QPair<QString, QString> > shortcuts; // name -> (shortcut, description)
182 foreach (const QWidget *child, wsApp->mainWindow()->findChildren<QWidget *>()) {
183 // Recent items look funny here.
184 if (child->objectName().compare("menuOpenRecentCaptureFile") == 0) continue;
185 foreach (const QAction *action, child->actions()) {
187 if (!action->shortcut().isEmpty()) {
188 QString name = action->text();
189 name.replace('&', "");
190 shortcuts[name] = QPair<QString, QString>(action->shortcut().toString(QKeySequence::NativeText), action->toolTip());
195 QStringList names = shortcuts.keys();
197 foreach (const QString &name, names) {
199 row << shortcuts[name].first << name << shortcuts[name].second;
204 QStringList ShortcutListModel::headerColumns() const
206 return QStringList() << tr("Shortcut") << tr("Name") << tr("Description");
209 FolderListModel::FolderListModel(QObject * parent):
210 AStringListListModel(parent)
213 appendRow( QStringList() << tr("\"File\" dialogs") << get_last_open_dir() << tr("capture files"));
216 appendRow( QStringList() << tr("Temp") << g_get_tmp_dir() << tr("untitled capture files"));
219 appendRow( QStringList() << tr("Personal configuration")
220 << gchar_free_to_qstring(get_persconffile_path("", FALSE))
221 << tr("dfilters, preferences, ethers, " UTF8_HORIZONTAL_ELLIPSIS));
224 QString dirPath = get_datafile_dir();
225 if (! dirPath.isEmpty()) {
226 appendRow ( QStringList() << tr("Global configuration") << dirPath
227 << tr("dfilters, preferences, manuf, " UTF8_HORIZONTAL_ELLIPSIS));
231 appendRow( QStringList() << tr("System") << get_systemfile_dir() << tr("ethers, ipxnets"));
234 appendRow( QStringList() << tr("Program") << get_progfile_dir() << tr("program files"));
238 appendRow( QStringList() << tr("Personal Plugins") << get_plugins_pers_dir_with_version() << tr("binary plugins"));
241 appendRow( QStringList() << tr("Global Plugins") << get_plugins_dir_with_version() << tr("binary plugins"));
246 appendRow( QStringList() << tr("Personal Lua Plugins") << get_plugins_pers_dir() << tr("lua scripts"));
249 appendRow( QStringList() << tr("Global Lua Plugins") << get_plugins_dir() << tr("lua scripts"));
253 QStringList extPaths = QString(get_extcap_dir()).split(G_SEARCHPATH_SEPARATOR_S);
255 foreach(QString path, extPaths)
256 appendRow( QStringList() << tr("Extcap path") << path.trimmed() << tr("Extcap Plugins search path"));
260 QStringList geoIpPaths = QString(geoip_db_get_paths()).split(G_SEARCHPATH_SEPARATOR_S);
261 foreach(QString path, geoIpPaths)
262 appendRow( QStringList() << tr("GeoIP path") << path.trimmed() << tr("GeoIP database search path"));
267 QStringList smiPaths = QString(oid_get_default_mib_path()).split(G_SEARCHPATH_SEPARATOR_S);
268 foreach(QString path, smiPaths)
269 appendRow( QStringList() << tr("MIB/PIB path") << path.trimmed() << tr("SMI MIB/PIB search path"));
273 QStringList FolderListModel::headerColumns() const
275 return QStringList() << tr("Name") << tr("Location") << tr("Typical Files");
279 // - Tweak and enhance ui...
281 AboutDialog::AboutDialog(QWidget *parent) :
283 ui(new Ui::AboutDialog)
286 setAttribute(Qt::WA_DeleteOnClose, true);
290 QString vcs_version_info_str = get_ws_vcs_version_info();
291 QString copyright_info_str = get_copyright_info();
292 QString comp_info_str = gstring_free_to_qbytearray(get_compiled_version_info(get_wireshark_qt_compiled_info,
293 get_gui_compiled_info));
294 QString runtime_info_str = gstring_free_to_qbytearray(get_runtime_version_info(get_wireshark_runtime_info));
297 AuthorListModel * authorModel = new AuthorListModel(this);
298 AStringListListSortFilterProxyModel * proxyAuthorModel = new AStringListListSortFilterProxyModel(this);
299 proxyAuthorModel->setSourceModel(authorModel);
300 proxyAuthorModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
301 proxyAuthorModel->setColumnToFilter(0);
302 proxyAuthorModel->setColumnToFilter(1);
303 ui->tblAuthors->setModel(proxyAuthorModel);
304 ui->tblAuthors->setRootIsDecorated(false);
305 ui->pte_Authors->clear();
306 ui->pte_Authors->appendPlainText(authorModel->acknowledgment());
307 ui->pte_Authors->moveCursor(QTextCursor::Start);
309 ui->tblAuthors->setContextMenuPolicy(Qt::CustomContextMenu);
310 connect(ui->tblAuthors, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
311 connect(ui->searchAuthors, SIGNAL(textChanged(QString)), proxyAuthorModel, SLOT(setFilter(QString)));
315 /* Construct the message string */
316 message = "<p>Version " + vcs_version_info_str.toHtmlEscaped() + "</p>\n\n";
317 message += "<p>" + copyright_info_str.toHtmlEscaped() + "</p>\n\n";
318 message += "<p>" + comp_info_str.toHtmlEscaped() + "</p>\n\n";
319 message += "<p>" + runtime_info_str.toHtmlEscaped() + "</p>\n\n";
320 message += "<p>Wireshark is Open Source Software released under the GNU General Public License.</p>\n\n";
321 message += "<p>Check the man page and http://www.wireshark.org for more information.</p>\n\n";
323 ui->label_wireshark->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
324 ui->label_wireshark->setTextFormat(Qt::RichText);
325 ui->label_wireshark->setWordWrap(true);
326 ui->label_wireshark->setTextInteractionFlags(Qt::TextSelectableByMouse);
327 ui->label_wireshark->setText(message);
329 /* Check if it is a dev release... (VERSION_MINOR is odd in dev release) */
330 #if VERSION_MINOR & 1
331 ui->label_logo->setPixmap(QPixmap(":/about/wssplash_dev.png"));
335 FolderListModel * folderModel = new FolderListModel(this);
336 AStringListListSortFilterProxyModel * folderProxyModel = new AStringListListSortFilterProxyModel(this);
337 folderProxyModel->setSourceModel(folderModel);
338 folderProxyModel->setColumnToFilter(1);
339 folderProxyModel->setFilterType(AStringListListSortFilterProxyModel::FilterByStart);
340 AStringListListUrlProxyModel * folderDisplayModel = new AStringListListUrlProxyModel(this);
341 folderDisplayModel->setSourceModel(folderProxyModel);
342 folderDisplayModel->setUrlColumn(1);
343 ui->tblFolders->setModel(folderDisplayModel);
344 ui->tblFolders->setRootIsDecorated(false);
345 ui->tblFolders->setItemDelegateForColumn(1, new UrlLinkDelegate(this));
346 ui->tblFolders->setContextMenuPolicy(Qt::CustomContextMenu);
347 ui->tblFolders->setTextElideMode(Qt::ElideMiddle);
348 connect(ui->tblFolders, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
349 connect(ui->searchFolders, SIGNAL(textChanged(QString)), folderProxyModel, SLOT(setFilter(QString)));
350 connect(ui->tblFolders, SIGNAL(clicked(QModelIndex)), this, SLOT(urlClicked(QModelIndex)));
354 ui->label_no_plugins->hide();
355 PluginListModel * pluginModel = new PluginListModel(this);
356 AStringListListSortFilterProxyModel * pluginFilterModel = new AStringListListSortFilterProxyModel(this);
357 pluginFilterModel->setSourceModel(pluginModel);
358 pluginFilterModel->setColumnToFilter(0);
359 AStringListListSortFilterProxyModel * pluginTypeModel = new AStringListListSortFilterProxyModel(this);
360 pluginTypeModel->setSourceModel(pluginFilterModel);
361 pluginTypeModel->setColumnToFilter(2);
362 ui->tblPlugins->setModel(pluginTypeModel);
363 ui->tblPlugins->setRootIsDecorated(false);
364 ui->cmbType->addItems(pluginModel->typeNames());
365 ui->tblPlugins->setContextMenuPolicy(Qt::CustomContextMenu);
366 ui->tblPlugins->setTextElideMode(Qt::ElideMiddle);
367 connect(ui->tblPlugins, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
368 connect(ui->searchPlugins, SIGNAL(textChanged(QString)), pluginFilterModel, SLOT(setFilter(QString)));
369 connect(ui->cmbType, SIGNAL(currentIndexChanged(QString)), pluginTypeModel, SLOT(setFilter(QString)));
370 if (ui->tblPlugins->model()->rowCount() < 1) {
371 foreach (QWidget *w, ui->tab_plugins->findChildren<QWidget *>()) {
374 ui->label_no_plugins->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
375 ui->label_no_plugins->setEnabled(false);
376 ui->label_no_plugins->show();
380 ShortcutListModel * shortcutModel = new ShortcutListModel(this);
381 AStringListListSortFilterProxyModel * shortcutProxyModel = new AStringListListSortFilterProxyModel(this);
382 shortcutProxyModel->setSourceModel(shortcutModel);
383 shortcutProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
384 shortcutProxyModel->setColumnToFilter(1);
385 shortcutProxyModel->setColumnToFilter(2);
386 ui->tblShortcuts->setModel(shortcutProxyModel);
387 ui->tblShortcuts->setRootIsDecorated(false);
388 ui->tblShortcuts->setContextMenuPolicy(Qt::CustomContextMenu);
389 connect(ui->tblShortcuts, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
390 connect(ui->searchShortcuts, SIGNAL(textChanged(QString)), shortcutProxyModel, SLOT(setFilter(QString)));
394 f_license.setFileName(get_datafile_path("COPYING.txt"));
396 f_license.setFileName(get_datafile_path("COPYING"));
399 f_license.open(QFile::ReadOnly | QFile::Text);
400 QTextStream ReadFile_license(&f_license);
402 ui->pte_License->setFont(wsApp->monospaceFont());
403 ui->pte_License->insertPlainText(ReadFile_license.readAll());
404 ui->pte_License->moveCursor(QTextCursor::Start);
408 AboutDialog::~AboutDialog()
413 void AboutDialog::showEvent(QShowEvent * event)
415 int one_em = fontMetrics().height();
417 // Authors: Names slightly narrower than emails.
418 QAbstractItemModel *model = ui->tblAuthors->model();
419 int column_count = model->columnCount();
420 ui->tblAuthors->setColumnWidth(0, (ui->tblAuthors->parentWidget()->width() / column_count) - one_em);
422 // Folders: First and last to contents.
423 ui->tblFolders->resizeColumnToContents(0);
424 ui->tblFolders->resizeColumnToContents(2);
425 ui->tblFolders->setColumnWidth(1, ui->tblFolders->parentWidget()->width() -
426 (ui->tblFolders->columnWidth(0) + ui->tblFolders->columnWidth(2)));
428 // Plugins: All but the last to contents.
429 model = ui->tblPlugins->model();
430 for (int col = 0; model && col < model->columnCount() - 1; col++) {
431 ui->tblPlugins->resizeColumnToContents(col);
434 // Shortcuts: Set widths manually.
435 model = ui->tblShortcuts->model();
436 // Contents + 2 em-widths
437 ui->tblShortcuts->resizeColumnToContents(0);
438 ui->tblShortcuts->setColumnWidth(0, ui->tblShortcuts->columnWidth(0) + (one_em * 2));
439 ui->tblShortcuts->setColumnWidth(1, one_em * 12);
440 ui->tblShortcuts->resizeColumnToContents(2);
442 QDialog::showEvent(event);
445 void AboutDialog::urlClicked(const QModelIndex &idx)
447 QTreeView * table = qobject_cast<QTreeView *>(sender());
451 QString urlText = table->model()->data(idx).toString();
452 if ( urlText.isEmpty() )
455 QFileInfo fi (urlText);
456 if ( fi.isDir() && fi.exists() )
458 QUrl url = QUrl::fromLocalFile(urlText);
460 QDesktopServices::openUrl(url);
464 void AboutDialog::handleCopyMenu(QPoint pos)
466 QTreeView * tree = qobject_cast<QTreeView *>(sender());
470 QModelIndex index = tree->indexAt(pos);
471 if ( ! index.isValid() )
474 QMenu * menu = new QMenu(this);
476 QAction * copyColumnAction = menu->addAction(tr("Copy"));
477 copyColumnAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
478 connect(copyColumnAction, SIGNAL(triggered()), this, SLOT(copyActionTriggered()));
480 QAction * copyRowAction = menu->addAction(tr("Copy Row(s)"));
481 copyRowAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
482 connect(copyRowAction, SIGNAL(triggered()), this, SLOT(copyRowActionTriggered()));
484 menu->popup(tree->viewport()->mapToGlobal(pos));
487 void AboutDialog::copyRowActionTriggered()
489 copyActionTriggered(true);
492 void AboutDialog::copyActionTriggered(bool copyRow)
494 QAction * sendingAction = qobject_cast<QAction *>(sender());
495 if ( ! sendingAction )
498 QTreeView * tree = VariantPointer<QTreeView>::asPtr(sendingAction->data());
500 QModelIndexList selIndeces = tree->selectionModel()->selectedIndexes();
505 QMenu * menu = qobject_cast<QMenu *>(sendingAction->parentWidget());
508 QPoint menuPosOnTable = tree->mapFromGlobal(menu->pos());
509 QModelIndex clickedIndex = tree->indexAt(menuPosOnTable);
510 if ( clickedIndex.isValid() )
511 copyColumn = clickedIndex.column();
516 if ( selIndeces.count() > 0 )
518 int columnCount = tree->model()->columnCount();
519 QList<int> visitedRows;
521 foreach(QModelIndex index, selIndeces)
523 if ( visitedRows.contains(index.row()) )
529 for ( int cnt = 0; cnt < columnCount; cnt++ )
531 QModelIndex dataIdx = tree->model()->index(index.row(), cnt);
532 row << tree->model()->data(dataIdx).toString();
537 if ( copyColumn < 0 )
538 copyColumn = index.column();
540 QModelIndex dataIdx = tree->model()->index(index.row(), copyColumn);
541 row << tree->model()->data(dataIdx).toString();
544 clipdata.append(row.join("\t\t").append("\n"));
546 visitedRows << index.row();
549 QClipboard * clipBoard = QApplication::clipboard();
550 clipBoard->setText(clipdata);
554 void AboutDialog::on_tblPlugins_doubleClicked(const QModelIndex &index)
556 const int row = index.row();
557 const int type_col = 2;
558 const int path_col = 3;
559 const QAbstractItemModel *model = index.model();
560 if (model->index(row, type_col).data().toString() == wslua_plugin_type_name()) {
561 QDesktopServices::openUrl(QUrl::fromLocalFile(model->index(row, path_col).data().toString()));
572 * indent-tabs-mode: nil
575 * ex: set shiftwidth=4 tabstop=8 expandtab:
576 * :indentSize=4:tabSize=8:noTabs=true: