/* 3proxy-eagle: Accumulate ethical 3proxy statistics with web interface. Source code: https://notabug.org/acetone/3proxy-eagle. Copyright (C) 2022, acetone This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "statistics.h" #include "dbmanager.h" #include "webpage.h" #include "g.h" #include #include #include #include #include QMutex Statistics::m_lastDestinationsMtx; QStringList Statistics::m_lastDestinations; std::atomic Statistics::m_dailyUpload {0}; std::atomic Statistics::m_dailyDownload {0}; std::atomic Statistics::m_totalUpload {0}; std::atomic Statistics::m_totalDownload {0}; QMutex Statistics::m_topDestinationsMtx; DestinationList Statistics::m_dailyTopDestinations; DestinationList Statistics::m_totalTopDestinations; bool Statistics::m_cacheUpdatedAfterLastReport = false; qint64 Statistics::m_actualizeTopListsLastTimestamp = 0; void Statistics::report(const LogEvent& event) { if (g::p::IGNORED_DESTINATIONS.contains(event.dest)) { qDebug() << "Destination" << event.dest << "ignored"; return; } m_lastDestinationsMtx.lock(); int index = m_lastDestinations.indexOf(event.dest); if (index == -1) { if (m_lastDestinations.size() == 5) { m_lastDestinations.pop_back(); } } else { m_lastDestinations.removeAt(index); } m_lastDestinations.push_front(event.dest); m_lastDestinationsMtx.unlock(); if (g::p::IGNORE_IP_ADDRS_IN_STATISTCS and isIpAddress(event.dest)) { qDebug() << event.dest << "rejected from statistics"; return; } DBManager db; m_dailyDownload = db.incrementDailyDownloadTraffic(event.from); m_dailyUpload = db.incrementDailyUploadTraffic(event.to); m_totalDownload = db.incrementTotalDownloadTraffic(event.from); m_totalUpload = db.incrementTotalUploadTraffic(event.to); db.incrementTotalTopCount(event.dest); db.incrementDailyTopCount(event.dest); m_cacheUpdatedAfterLastReport = false; } const QStringList Statistics::lastDestinations() { QMutexLocker lock (&m_lastDestinationsMtx); return m_lastDestinations; } const DestinationList Statistics::dailyTopDestinations() { if (not m_cacheUpdatedAfterLastReport) { actualizeTopLists(); } QMutexLocker lock (&m_topDestinationsMtx); return m_dailyTopDestinations; } const DestinationList Statistics::totalTopDestinations() { QMutexLocker lock (&m_topDestinationsMtx); return m_totalTopDestinations; } void Statistics::initTrafficCounters() { DBManager db; m_dailyDownload = db.dailyDownloadTraffic(); m_dailyUpload = db.dailyUploadTraffic(); m_totalDownload = db.totalDownloadTraffic(); m_totalUpload = db.totalUploadTraffic(); } QString Statistics::statsTxt() { static QMutex mtx; if (not mtx.tryLock()) { return "Service is busy, try again later."; } auto raw = DBManager().allHistory(); quint64 totalRequests = 0; QString result; for (const auto& record: raw) { result += WebPage::counterToHumanReadableString(record.second) + "\t" + record.first + "\n"; totalRequests += record.second; } result.push_front("Total destinations: " + WebPage::counterToHumanReadableString(raw.size()) + "\n" "Total requests: " + WebPage::counterToHumanReadableString(totalRequests) + "\n\n"); mtx.unlock(); return result; } void Statistics::actualizeTopLists() { QMutexLocker lock (&m_topDestinationsMtx); qint64 started = QDateTime::currentMSecsSinceEpoch(); if (started - m_actualizeTopListsLastTimestamp < g::c::CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS) { qDebug() << "Cache actualizing rejected by minimal interval:" << g::c::CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS << "ms"; return; } DBManager db; m_dailyTopDestinations = db.dailyTop(); m_totalTopDestinations = db.totalTop(); m_cacheUpdatedAfterLastReport = true; m_actualizeTopListsLastTimestamp = QDateTime::currentMSecsSinceEpoch(); qDebug() << "Cache actualized in" << m_actualizeTopListsLastTimestamp-started << "ms"; } bool Statistics::isIpAddress(const QString& dest) { static QRegularExpression ipv4("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$"); if (ipv4.match(dest).hasMatch()) { return true; } static QRegularExpression ipv6("^\\[[a-f0-9:]*\\]$"); if (ipv6.match(dest).hasMatch()) { return true; } return false; }