/** @file * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TabData::TabData() : _name(QString()), _protoId(-1) {} TabData::TabData(QString name, int protoId) : _name(name), _protoId(protoId) {} QString TabData::name() const { return _name; } int TabData::protoId() const { return _protoId; } TrafficTab::TrafficTab(QWidget * parent) : DetachableTabWidget(parent) { _createModel = nullptr; _createDelegate = nullptr; _disableTaps = false; _nameResolution = false; setTabBasename(QString()); } TrafficTab::~TrafficTab() {} void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel) { setTabBasename(tableName); _allProtocols = trafficList->protocols(); if (createModel) _createModel = createModel; _recentColumnList = recentColumnList; setOpenTabs(trafficList->protocols(true)); } void TrafficTab::setDelegate(ATapCreateDelegate createDelegate) { if (! createDelegate) return; _createDelegate = createDelegate; for (int idx = 0; idx < count(); idx++) { if (qobject_cast(widget(idx))) { QTreeView * tree = qobject_cast(widget(idx)); tree->setItemDelegate(createDelegate(tree)); } } } QTreeView * TrafficTab::createTree(int protoId) { TrafficTree * tree = new TrafficTree(tabBasename(), _recentColumnList, this); if (_createModel) { ATapDataModel * model = _createModel(protoId, ""); model->setParent(tree); connect(model, &ATapDataModel::tapListenerChanged, tree, &TrafficTree::tapListenerEnabled); model->enableTap(); if (_createDelegate) { tree->setItemDelegate(_createDelegate(tree)); } TrafficDataFilterProxy * proxyModel = new TrafficDataFilterProxy(tree); proxyModel->setSourceModel(model); tree->setModel(proxyModel); QItemSelectionModel * ism = new QItemSelectionModel(proxyModel, tree); tree->setSelectionModel(ism); connect(ism, &QItemSelectionModel::currentChanged, this, &TrafficTab::doCurrentIndexChange); tree->applyRecentColumns(); tree->sortByColumn(0, Qt::AscendingOrder); connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, [tree]() { if (tree->model()->rowCount() > 0) { for (int col = 0; col < tree->model()->columnCount(); col++) tree->resizeColumnToContents(col); } }); connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, &TrafficTab::modelReset); /* If the columns for the tree have changed, contact the tab. By also having the tab * columns changed signal connecting back to the tree, it will propagate to all trees * registered with this tab. Attention, this heavily relies on the fact, that all * tree data models are identical */ connect(tree, &TrafficTree::columnsHaveChanged, this, &TrafficTab::columnsHaveChanged); connect(this, &TrafficTab::columnsHaveChanged, tree, &TrafficTree::columnsChanged); } return tree; } void TrafficTab::useAbsoluteTime(bool absolute) { for(int idx = 0; idx < count(); idx++) { ATapDataModel * atdm = modelForTabIndex(idx); if (atdm) atdm->useAbsoluteTime(absolute); } } void TrafficTab::useNanosecondTimestamps(bool nanoseconds) { for(int idx = 0; idx < count(); idx++) { ATapDataModel * atdm = modelForTabIndex(idx); if (atdm) atdm->useNanosecondTimestamps(nanoseconds); } } void TrafficTab::disableTap() { for(int idx = 0; idx < count(); idx++) { ATapDataModel * atdm = modelForTabIndex(idx); if (atdm) atdm->disableTap(); } _disableTaps = true; emit disablingTaps(); } void TrafficTab::setOpenTabs(QList protocols) { QList tabs = _tabs.keys(); QList remove; blockSignals(true); foreach(int protocol, protocols) { if (! tabs.contains(protocol)) { insertProtoTab(protocol, false); } tabs.removeAll(protocol); } foreach(int protocol, tabs) removeProtoTab(protocol, false); blockSignals(false); emit tabsChanged(_tabs.keys()); emit retapRequired(); } void TrafficTab::insertProtoTab(int protoId, bool emitSignals) { QList lUsed = _tabs.keys(); if (lUsed.contains(protoId) && lUsed.count() != count()) { _tabs.clear(); for (int idx = 0; idx < count(); idx++) { TabData tabData = qvariant_cast(tabBar()->tabData(idx)); _tabs.insert(tabData.protoId(), idx); } lUsed = _tabs.keys(); } if (protoId <= 0 || lUsed.contains(protoId)) return; QList lFull = _allProtocols; int idx = (int) lFull.indexOf(protoId); if (idx < 0) return; QList part = lFull.mid(0, idx); int insertAt = 0; if (part.count() > 0) { for (int cnt = idx - 1; cnt >= 0; cnt--) { if (lUsed.contains(part[cnt]) && part[cnt] != protoId) { insertAt = (int) lUsed.indexOf(part[cnt]) + 1; break; } } } QTreeView * tree = createTree(protoId); QString tableName = proto_get_protocol_short_name(find_protocol_by_id(protoId)); TabData tabData(tableName, protoId); QVariant storage; storage.setValue(tabData); if (tree->model()->rowCount() > 0) tableName += QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(tree->model()->rowCount()); int tabId = -1; if (insertAt > -1) tabId = insertTab(insertAt, tree, tableName); else tabId = addTab(tree, tableName); if (tabId >= 0) tabBar()->setTabData(tabId, storage); /* We reset the correct tab idxs. That operations is costly, but it is only * called during this operation and ensures, that other operations do not * need to iterate, but rather can lookup the indeces. */ _tabs.clear(); for (int idx = 0; idx < count(); idx++) { TabData tabData = qvariant_cast(tabBar()->tabData(idx)); _tabs.insert(tabData.protoId(), idx); } if (emitSignals) { emit tabsChanged(_tabs.keys()); emit retapRequired(); } } void TrafficTab::removeProtoTab(int protoId, bool emitSignals) { if (_tabs.keys().contains(protoId)) { for(int idx = 0; idx < count(); idx++) { TabData tabData = qvariant_cast(tabBar()->tabData(idx)); if (protoId == tabData.protoId()) { removeTab(idx); break; } } } /* We reset the correct tab idxs. That operations is costly, but it is only * called during this operation and ensures, that other operations do not * need to iterate, but rather can lookup the indeces. */ _tabs.clear(); for (int idx = 0; idx < count(); idx++) { TabData tabData = qvariant_cast(tabBar()->tabData(idx)); _tabs.insert(tabData.protoId(), idx); } if (emitSignals) { emit tabsChanged(_tabs.keys()); emit retapRequired(); } } void TrafficTab::doCurrentIndexChange(const QModelIndex & cur, const QModelIndex &) { if (! cur.isValid()) return; const TrafficDataFilterProxy * proxy = qobject_cast(cur.model()); if (! proxy) return; ATapDataModel * model = qobject_cast(proxy->sourceModel()); if (! model) return; int tabId = _tabs[model->protoId()]; emit tabDataChanged(tabId); } QVariant TrafficTab::currentItemData(int role) { QTreeView * tree = qobject_cast(currentWidget()); if (tree) { QModelIndex idx = tree->selectionModel()->currentIndex(); /* In case no selection has been made yet, we select the topmostleft index, * to ensure proper handling. Especially ConversationDialog depends on this * method always returning data */ if (!idx.isValid()) { ATapDataModel * model = modelForTabIndex(currentIndex()); idx = model->index(0, 0); } return idx.data(role); } return QVariant(); } void TrafficTab::modelReset() { if (! qobject_cast(sender())) return; TrafficDataFilterProxy * qsfpm = qobject_cast(sender()); if (!qsfpm || ! qobject_cast(qsfpm->sourceModel())) return; ATapDataModel * atdm = qobject_cast(qsfpm->sourceModel()); int protoId = atdm->protoId(); if (!_tabs.keys().contains(protoId)) return; int tabIdx = _tabs[protoId]; TabData tabData = qvariant_cast(tabBar()->tabData(tabIdx)); if (tabData.protoId() == protoId) { if (qsfpm->rowCount() == 0) setTabText(tabIdx, tabData.name()); else setTabText(tabIdx, tabData.name() + QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(qsfpm->rowCount())); } emit tabDataChanged(tabIdx); } ATapDataModel * TrafficTab::modelForTabIndex(int tabIdx) { if (tabIdx == -1) tabIdx = currentIndex(); return modelForWidget(widget(tabIdx)); } ATapDataModel * TrafficTab::modelForWidget(QWidget * searchWidget) { if (qobject_cast(searchWidget)) { QTreeView * tree = qobject_cast(searchWidget); if (qobject_cast(tree->model())) { TrafficDataFilterProxy * qsfpm = qobject_cast(tree->model()); if (qsfpm && qobject_cast(qsfpm->sourceModel())) { return qobject_cast(qsfpm->sourceModel()); } } } return nullptr; } void TrafficTab::setFilter(QString filter) { for (int idx = 0; idx < count(); idx++ ) { ATapDataModel * atdm = modelForTabIndex(idx); if (! atdm) continue; atdm->setFilter(filter); } } void TrafficTab::setNameResolution(bool checked) { if (checked == _nameResolution) return; for (int idx = 0; idx < count(); idx++ ) { ATapDataModel * atdm = modelForTabIndex(idx); if (! atdm) continue; atdm->setResolveNames(checked); } _nameResolution = checked; /* Send the signal, that all tabs have potentially changed */ emit tabDataChanged(-1); } bool TrafficTab::hasNameResolution(int tabIdx) { int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; ATapDataModel * dataModel = modelForTabIndex(tab); if (! dataModel) return false; return dataModel->allowsNameResolution(); } QMenu * TrafficTab::createCopyMenu(QWidget *parent) { TrafficTree * tree = qobject_cast(currentWidget()); if ( ! tree) return nullptr; return tree->createCopyMenu(parent); } #ifdef HAVE_MAXMINDDB bool TrafficTab::hasGeoIPData(int tabIdx) { int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; ATapDataModel * dataModel = modelForTabIndex(tab); return dataModel->hasGeoIPData(); } bool TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel) { QTextStream out(fp); if (!json_only) { QFile ipmap(get_datafile_path("ipmap.html")); if (!ipmap.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr("Map file error"), tr("Could not open base file %1 for reading: %2") .arg(get_datafile_path("ipmap.html")) .arg(g_strerror(errno)) ); return false; } /* Copy ipmap.html to map file. */ QTextStream in(&ipmap); QString line; while (in.readLineInto(&line)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) out << line << Qt::endl; #else out << line << endl; #endif } out << QString("\n"); out.flush(); return true; } QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx) { int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx; ATapDataModel * dataModel = modelForTabIndex(tab); if (! (dataModel && dataModel->hasGeoIPData())) { QMessageBox::warning(this, tr("Map file error"), tr("No endpoints available to map")); return QUrl(); } QString tempname = QString("%1/ipmapXXXXXX.html").arg(QDir::tempPath()); QTemporaryFile tf(tempname); if (!tf.open()) { QMessageBox::warning(this, tr("Map file error"), tr("Unable to create temporary file")); return QUrl(); } if (!writeGeoIPMapFile(&tf, json_only, dataModel)) { tf.close(); return QUrl(); } tf.setAutoRemove(false); return QUrl::fromLocalFile(tf.fileName()); } #endif void TrafficTab::detachTab(int tabIdx, QPoint pos) { ATapDataModel * model = modelForTabIndex(tabIdx); if (!model) return; TrafficTree * tree = qobject_cast(widget(tabIdx)); if (!tree) return; connect(this, &TrafficTab::disablingTaps ,tree , &TrafficTree::disableTap); DetachableTabWidget::detachTab(tabIdx, pos); removeProtoTab(model->protoId()); } void TrafficTab::attachTab(QWidget * content, QString name) { ATapDataModel * model = modelForWidget(content); if (!model) { attachTab(content, name); return; } insertProtoTab(model->protoId()); }