File indexing completed on 2025-01-05 05:07:03
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2018 Alexis Lopes Zubeta <contact@azubieta.net> 0003 // SPDX-FileCopyrightText: 2020 Tomaz Canabrava <tcanabrava@kde.org> 0004 0005 #include "connectionsmodel.h" 0006 0007 #include <chrono> 0008 0009 #include <QDebug> 0010 #include <QMetaEnum> 0011 0012 #include <KLocalizedString> 0013 0014 using namespace std::chrono_literals; 0015 0016 ConnectionsModel::ConnectionsModel(QObject *parent) 0017 : QAbstractTableModel(parent) 0018 { 0019 connect(&m_netstatHelper, &NetstatHelper::queryFinished, this, &ConnectionsModel::refreshConnections); 0020 connect(&timer, &QTimer::timeout, &m_netstatHelper, &NetstatHelper::query); 0021 timer.setInterval(10s); // arbitrary query interval, should be long enough to let the helper finish on its own 0022 } 0023 0024 void ConnectionsModel::start() 0025 { 0026 m_busy = true; 0027 Q_EMIT busyChanged(); 0028 timer.start(); 0029 QTimer::singleShot(0, &m_netstatHelper, &NetstatHelper::query); 0030 } 0031 0032 void ConnectionsModel::stop() 0033 { 0034 timer.stop(); 0035 } 0036 0037 int ConnectionsModel::rowCount(const QModelIndex &parent) const 0038 { 0039 if (parent.isValid()) { 0040 return 0; 0041 } 0042 0043 return m_connectionsData.size(); 0044 } 0045 0046 int ConnectionsModel::columnCount(const QModelIndex &parent) const 0047 { 0048 Q_UNUSED(parent) 0049 return QMetaEnum::fromType<ConnectionsModelColumns>().keyCount(); 0050 } 0051 0052 bool ConnectionsModel::busy() const 0053 { 0054 return m_busy; 0055 } 0056 0057 QVariant ConnectionsModel::data(const QModelIndex &index, int role) const 0058 { 0059 Q_UNUSED(role); 0060 const auto checkIndexFlags = QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::ParentIsInvalid; 0061 0062 if (!checkIndex(index, checkIndexFlags)) { 0063 return {}; 0064 } 0065 0066 ConnectionsData data = m_connectionsData.at(index.row()); 0067 0068 switch (static_cast<ConnectionsModelColumns>(index.column())) { 0069 case ProtocolColumn: 0070 return data.protocol; 0071 case LocalAddressColumn: 0072 return data.localAddress; 0073 case ForeignAddressColumn: 0074 return data.foreignAddress; 0075 case StatusColumn: 0076 return data.status; 0077 case PidColumn: 0078 return data.pid; 0079 case ProgramColumn: 0080 // HACK. Firefox reports as MainThread 0081 if (data.program == QLatin1String("MainThread")) { 0082 return "Firefox"; 0083 } else { 0084 return data.program; 0085 } 0086 break; 0087 } 0088 return {}; 0089 } 0090 0091 QVariant ConnectionsModel::headerData(int section, Qt::Orientation orientation, int role) const 0092 { 0093 Q_UNUSED(orientation) 0094 Q_UNUSED(role) 0095 switch (static_cast<ConnectionsModelColumns>(section)) { 0096 case ProtocolColumn: 0097 return i18nc("@title:column", "Protocol"); 0098 case LocalAddressColumn: 0099 return i18nc("@title:column", "Local address"); 0100 case ForeignAddressColumn: 0101 return i18nc("@title:column", "Foreign address"); 0102 case StatusColumn: 0103 return i18nc("@title:column", "Status"); 0104 case PidColumn: 0105 return i18nc("@title:column", "PID"); 0106 case ProgramColumn: 0107 return i18nc("@title:column", "Program"); 0108 } 0109 return {}; 0110 } 0111 0112 void ConnectionsModel::refreshConnections(const QList<QStringList> &result) 0113 { 0114 if (m_netstatHelper.hasError()) { 0115 Q_EMIT showErrorMessage(i18n("Failed to get connections: %1", m_netstatHelper.errorString())); 0116 return; 0117 } 0118 0119 const auto oldConnectionsData = m_connectionsData; 0120 QList<ConnectionsData> newConnectionsData; 0121 0122 beginResetModel(); 0123 m_connectionsData.clear(); 0124 for (const auto &connection : result) { 0125 ConnectionsData conn{.protocol = connection.at(0), 0126 .localAddress = connection.at(1), 0127 .foreignAddress = connection.at(2), 0128 .status = connection.at(3), 0129 .pid = connection.at(4), 0130 .program = connection.at(5)}; 0131 0132 if (conn.status == QLatin1String("UNCONN")) { 0133 conn.status = i18n("Not Connected"); 0134 } else if (conn.status == QLatin1String("ESTAB")) { 0135 conn.status = i18n("Established"); 0136 } else if (conn.status == QLatin1String("LISTEN")) { 0137 conn.status = i18n("Listening"); 0138 } 0139 0140 newConnectionsData.append(conn); 0141 } 0142 0143 if (newConnectionsData != oldConnectionsData) { 0144 beginResetModel(); 0145 m_connectionsData = newConnectionsData; 0146 endResetModel(); 0147 } 0148 0149 if (newConnectionsData.count() != oldConnectionsData.count()) { 0150 Q_EMIT countChanged(); 0151 } 0152 0153 if (m_busy) { 0154 m_busy = false; 0155 Q_EMIT busyChanged(); 0156 } 0157 }