diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 3b7130c2..229748f3 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -196,6 +196,12 @@ public: unsigned short int GetJoinDelay() const { return m_uJoinDelay; } void SetJoinDelay(unsigned short int uJoinDelay) { m_uJoinDelay = uJoinDelay; } + unsigned long long BytesRead() const { return m_uBytesRead; } + unsigned long long BytesWritten() const { return m_uBytesWritten; } + + void AddBytesRead(unsigned long long u) { m_uBytesRead += u; } + void AddBytesWritten(unsigned long long u) { m_uBytesWritten += u; } + CString ExpandString(const CString& sStr) const; CString& ExpandString(const CString& sStr, CString& sRet) const; private: @@ -245,6 +251,8 @@ protected: CIRCNetworkJoinTimer* m_pJoinTimer; unsigned short int m_uJoinDelay; + unsigned long long m_uBytesRead; + unsigned long long m_uBytesWritten; }; #endif // !ZNC_IRCNETWORK_H diff --git a/include/znc/User.h b/include/znc/User.h index 84018e45..dc7c29e0 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -187,8 +187,8 @@ public: bool AutoClearQueryBuffer() const; bool IsBeingDeleted() const { return m_bBeingDeleted; } CString GetTimezone() const { return m_sTimezone; } - unsigned long long BytesRead() const { return m_uBytesRead; } - unsigned long long BytesWritten() const { return m_uBytesWritten; } + unsigned long long BytesRead() const; + unsigned long long BytesWritten() const; unsigned int JoinTries() const { return m_uMaxJoinTries; } unsigned int MaxJoins() const { return m_uMaxJoins; } CString GetSkinName() const; diff --git a/include/znc/znc.h b/include/znc/znc.h index 5dba289d..2c50d590 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -92,6 +92,7 @@ public: // generated through ZNC. TrafficStatsMap GetTrafficStats(TrafficStatsPair &Users, TrafficStatsPair &ZNC, TrafficStatsPair &Total); + TrafficStatsMap GetNetworkTrafficStats(const CString& sUsername, TrafficStatsPair& Total); // Authenticate a user. // The result is passed back via callbacks to CAuthBase. diff --git a/modules/data/webadmin/tmpl/traffic.tmpl b/modules/data/webadmin/tmpl/traffic.tmpl index 368f09b4..d9641481 100644 --- a/modules/data/webadmin/tmpl/traffic.tmpl +++ b/modules/data/webadmin/tmpl/traffic.tmpl @@ -33,6 +33,23 @@ Total IRC Connections + + + Networks + + + + Attached Networks + + + + Client Connections + + + + IRC Connections + + @@ -40,51 +57,74 @@ - +
-

Traffic

+

Total

- + - - - - - - - - Add the totals separately so that if sort is ever used they stay at the bottom - By keeping them inside the loop we can figure out even/odd classes though. - - - - + + - - + + - - + + - + +
Username In Out Total
User Total
Users
ZNC Total
ZNC
Grand Total
Total
+
+
+
+ + + +
+ +

Users

+ +

Traffic

+ +
+
+ + + + + + + + + + + + + + + + + + +
UsernameNetworkInOutTotal
diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index 6d6ca7ed..8922b24b 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -78,7 +78,7 @@ public: vParams.push_back(make_pair("user", "")); AddSubPage(std::make_shared("settings", "Global Settings", CWebSubPage::F_ADMIN)); AddSubPage(std::make_shared("edituser", "Your Settings", vParams)); - AddSubPage(std::make_shared("traffic", "Traffic Info", CWebSubPage::F_ADMIN)); + AddSubPage(std::make_shared("traffic", "Traffic Info", vParams)); AddSubPage(std::make_shared("listusers", "Manage Users", CWebSubPage::F_ADMIN)); } @@ -573,7 +573,7 @@ public: return true; } else if (sPageName == "listusers" && spSession->IsAdmin()) { return ListUsersPage(WebSock, Tmpl); - } else if (sPageName == "traffic" && spSession->IsAdmin()) { + } else if (sPageName == "traffic") { return TrafficPage(WebSock, Tmpl); } else if (sPageName == "index") { return true; @@ -1431,6 +1431,7 @@ public: } bool TrafficPage(CWebSock& WebSock, CTemplate& Tmpl) { + std::shared_ptr spSession = WebSock.GetSession(); Tmpl["Title"] = "Traffic Info"; Tmpl["Uptime"] = CZNC::Get().GetUptime(); @@ -1441,6 +1442,11 @@ public: for (const auto& it : msUsers) { CUser* pUser = it.second; + + if (!spSession->IsAdmin() && spSession->GetUser() != it.second) { + continue; + } + vector vNetworks = pUser->GetNetworks(); for (const CIRCNetwork* pNetwork : vNetworks) { @@ -1469,12 +1475,27 @@ public: CZNC::TrafficStatsMap traffic = CZNC::Get().GetTrafficStats(Users, ZNC, Total); for (const auto& it : traffic) { + if (!spSession->IsAdmin() && !spSession->GetUser()->GetUserName().Equals(it.first)) { + continue; + } + CTemplate& l = Tmpl.AddRow("TrafficLoop"); l["Username"] = it.first; l["In"] = CString::ToByteStr(it.second.first); l["Out"] = CString::ToByteStr(it.second.second); l["Total"] = CString::ToByteStr(it.second.first + it.second.second); + + CZNC::TrafficStatsPair NetworkTotal; + CZNC::TrafficStatsMap NetworkTraffic = CZNC::Get().GetNetworkTrafficStats(it.first, NetworkTotal); + for (const auto& it2 : NetworkTraffic) { + CTemplate& l2 = Tmpl.AddRow("TrafficLoop"); + + l2["Network"] = it2.first; + l2["In"] = CString::ToByteStr(it2.second.first); + l2["Out"] = CString::ToByteStr(it2.second.second); + l2["Total"] = CString::ToByteStr(it2.second.first + it2.second.second); + } } Tmpl["UserIn"] = CString::ToByteStr(Users.first); diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 6342280e..facc8669 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -143,7 +143,9 @@ CIRCNetwork::CIRCNetwork(CUser *pUser, const CString& sName) m_NoticeBuffer(), m_pPingTimer(nullptr), m_pJoinTimer(nullptr), - m_uJoinDelay(0) + m_uJoinDelay(0), + m_uBytesRead(0), + m_uBytesWritten(0) { SetUser(pUser); @@ -301,6 +303,7 @@ CIRCNetwork::~CIRCNetwork() { } m_vQueries.clear(); + CUser* pUser = GetUser(); SetUser(nullptr); // Make sure we are not in the connection queue @@ -308,6 +311,14 @@ CIRCNetwork::~CIRCNetwork() { CZNC::Get().GetManager().DelCronByAddr(m_pPingTimer); CZNC::Get().GetManager().DelCronByAddr(m_pJoinTimer); + + if (pUser) { + pUser->AddBytesRead(m_uBytesRead); + pUser->AddBytesWritten(m_uBytesWritten); + } else { + CZNC::Get().AddBytesRead(m_uBytesRead); + CZNC::Get().AddBytesWritten(m_uBytesWritten); + } } void CIRCNetwork::DelServers() { diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 8c016b66..450276b8 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -130,8 +130,8 @@ CIRCSock::~CIRCSock() { Quit(); m_msChans.clear(); - m_pNetwork->GetUser()->AddBytesRead(GetBytesRead()); - m_pNetwork->GetUser()->AddBytesWritten(GetBytesWritten()); + m_pNetwork->AddBytesRead(GetBytesRead()); + m_pNetwork->AddBytesWritten(GetBytesWritten()); } void CIRCSock::Quit(const CString& sQuitMsg) { diff --git a/src/Socket.cpp b/src/Socket.cpp index 77cd7859..80e86675 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -407,14 +407,19 @@ CSocket::CSocket(CModule* pModule, const CString& sHostname, unsigned short uPor CSocket::~CSocket() { CUser *pUser = nullptr; + CIRCNetwork* pNetwork = nullptr; // CWebSock could cause us to have a nullptr pointer here if (m_pModule) { pUser = m_pModule->GetUser(); + pNetwork = m_pModule->GetNetwork(); m_pModule->UnlinkSocket(this); } - if (pUser && m_pModule && (m_pModule->GetType() != CModInfo::GlobalModule)) { + if (pNetwork && m_pModule && (m_pModule->GetType() == CModInfo::NetworkModule)) { + pNetwork->AddBytesWritten(GetBytesWritten()); + pNetwork->AddBytesRead(GetBytesRead()); + } else if (pUser && m_pModule && (m_pModule->GetType() == CModInfo::UserModule)) { pUser->AddBytesWritten(GetBytesWritten()); pUser->AddBytesRead(GetBytesRead()); } else { diff --git a/src/User.cpp b/src/User.cpp index a6fe44fc..8b8bedce 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -120,8 +120,8 @@ CUser::~CUser() { CZNC::Get().GetManager().DelCronByAddr(m_pUserTimer); - CZNC::Get().AddBytesRead(BytesRead()); - CZNC::Get().AddBytesWritten(BytesWritten()); + CZNC::Get().AddBytesRead(m_uBytesRead); + CZNC::Get().AddBytesWritten(m_uBytesWritten); } template @@ -1278,3 +1278,19 @@ bool CUser::AutoClearQueryBuffer() const { return m_bAutoClearQueryBuffer; } CString CUser::GetSkinName() const { return m_sSkinName; } const CString& CUser::GetUserPath() const { if (!CFile::Exists(m_sUserPath)) { CDir::MakeDir(m_sUserPath); } return m_sUserPath; } // !Getters + +unsigned long long CUser::BytesRead() const { + unsigned long long uBytes = m_uBytesRead; + for (const CIRCNetwork* pNetwork : m_vIRCNetworks) { + uBytes += pNetwork->BytesRead(); + } + return uBytes; +} + +unsigned long long CUser::BytesWritten() const { + unsigned long long uBytes = m_uBytesWritten; + for (const CIRCNetwork* pNetwork : m_vIRCNetworks) { + uBytes += pNetwork->BytesWritten(); + } + return uBytes; +} diff --git a/src/znc.cpp b/src/znc.cpp index 084145ef..252a758f 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1767,6 +1767,38 @@ CZNC::TrafficStatsMap CZNC::GetTrafficStats(TrafficStatsPair &Users, return ret; } +CZNC::TrafficStatsMap CZNC::GetNetworkTrafficStats(const CString& sUsername, TrafficStatsPair& Total) { + TrafficStatsMap Networks; + + CUser* pUser = FindUser(sUsername); + if (pUser) { + for (const CIRCNetwork* pNetwork : pUser->GetNetworks()) { + Networks[pNetwork->GetName()].first = pNetwork->BytesRead(); + Networks[pNetwork->GetName()].second = pNetwork->BytesWritten(); + Total.first += pNetwork->BytesRead(); + Total.second += pNetwork->BytesWritten(); + } + + for (Csock* pSock : m_Manager) { + CIRCNetwork *pNetwork = nullptr; + if (pSock->GetSockName().StartsWith("IRC::")) { + pNetwork = ((CIRCSock *) pSock)->GetNetwork(); + } else if (pSock->GetSockName().StartsWith("USR::")) { + pNetwork = ((CClient *) pSock)->GetNetwork(); + } + + if (pNetwork && pNetwork->GetUser() == pUser) { + Networks[pNetwork->GetName()].first = pSock->GetBytesRead(); + Networks[pNetwork->GetName()].second = pSock->GetBytesWritten(); + Total.first += pSock->GetBytesRead(); + Total.second += pSock->GetBytesWritten(); + } + } + } + + return Networks; +} + void CZNC::AuthUser(std::shared_ptr AuthClass) { // TODO unless the auth module calls it, CUser::IsHostAllowed() is not honoured bool bReturn = false;