local ideas to git
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
QT -= gui
|
||||
QT += network
|
||||
|
||||
CONFIG += c++17 console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
SOURCES += \
|
||||
dailycounter.cpp \
|
||||
g.cpp \
|
||||
httpdocument.cpp \
|
||||
httpserver.cpp \
|
||||
main.cpp \
|
||||
proxyinstanse.cpp \
|
||||
socketrunnable.cpp \
|
||||
socketrunnablebase.cpp \
|
||||
statistics.cpp \
|
||||
webpage.cpp \
|
||||
webserverbase.cpp
|
||||
|
||||
HEADERS += \
|
||||
dailycounter.h \
|
||||
g.h \
|
||||
httpdocument.h \
|
||||
httpserver.h \
|
||||
proxyinstanse.h \
|
||||
socketrunnable.h \
|
||||
socketrunnablebase.h \
|
||||
statistics.h \
|
||||
webpage.h \
|
||||
webserverbase.h
|
||||
|
||||
RESOURCES += \
|
||||
html-static.qrc
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dailycounter.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
DailyCounter::DailyCounter()
|
||||
{
|
||||
}
|
||||
|
||||
quint64 DailyCounter::value()
|
||||
{
|
||||
const auto factDay = getCurrentDay();
|
||||
if (day() != factDay)
|
||||
{
|
||||
m_day = factDay;
|
||||
m_counter = 0;
|
||||
}
|
||||
return m_counter;
|
||||
}
|
||||
|
||||
QString DailyCounter::day() const
|
||||
{
|
||||
return m_day;
|
||||
}
|
||||
|
||||
QJsonObject DailyCounter::serialize()
|
||||
{
|
||||
QJsonObject me;
|
||||
me["day"] = day();
|
||||
me["count"] = QString::number( value() );
|
||||
return me;
|
||||
}
|
||||
|
||||
void DailyCounter::deserialize(const QJsonObject &json)
|
||||
{
|
||||
const QString serializedDay = json.value("day").toString();
|
||||
const QString factDay = getCurrentDay();
|
||||
|
||||
m_day = factDay;
|
||||
auto serializedCounter = json.value("count").toString().toULongLong();
|
||||
if (serializedDay == factDay)
|
||||
{
|
||||
m_counter = serializedCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DailyCounter::increment(quint64 value)
|
||||
{
|
||||
const auto factDay = getCurrentDay();
|
||||
if (day() == factDay)
|
||||
{
|
||||
m_counter += value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_day = factDay;
|
||||
m_counter = value;
|
||||
}
|
||||
}
|
||||
|
||||
QString DailyCounter::getCurrentDay()
|
||||
{
|
||||
return QDateTime::currentDateTimeUtc().toString(Qt::DateFormat::RFC2822Date);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
class DailyCounter
|
||||
{
|
||||
public:
|
||||
DailyCounter();
|
||||
quint64 value();
|
||||
QString day() const;
|
||||
QJsonObject serialize();
|
||||
void deserialize(const QJsonObject& json);
|
||||
void increment(quint64);
|
||||
|
||||
static QString getCurrentDay();
|
||||
|
||||
private:
|
||||
QString m_day;
|
||||
std::atomic<quint64> m_counter {0};
|
||||
};
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "g.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QCryptographicHash>
|
||||
#include <QMutex>
|
||||
#include <QFile>
|
||||
#include <iostream>
|
||||
|
||||
namespace g {
|
||||
|
||||
namespace c {
|
||||
const QString SOFTWARE_NAME = "3proxy-eagle";
|
||||
const QString SOFTWARE_VERSION = "0.0.1a";
|
||||
const QString COPYRIGHT = "GPLv3 (c) acetone, 2022";
|
||||
const QString HA_PAGE_TITLE = "{{PAGE_TITLE}}";
|
||||
const QString HA_CUSTOM_CSS = "{{CUSTOM_CSS}}";
|
||||
const QString HA_DAILY_UPLOAD = "{{DAILY_UPLOAD}}";
|
||||
const QString HA_DAILY_UPLOAD_MEASURE = "{{DAILY_UPLOAD_MEASURE}}";
|
||||
const QString HA_DAILY_DOWNLOAD = "{{DAILY_DOWNLOAD}}";
|
||||
const QString HA_DAILY_DOWNLOAD_MEASURE = "{{DAILY_DOWNLOAD_MEASURE}}";
|
||||
const QString HA_TOTAL_UPLOAD = "{{TOTAL_UPLOAD}}";
|
||||
const QString HA_TOTAL_DOWNLOAD = "{{TOTAL_DOWNLOAD}}";
|
||||
const QString HA_LAST_DESTINATIONS_LIST = "{{LAST_DESTINATIONS_LIST}}";
|
||||
const QString HA_DAILY_TOP_DESTINATIONS_LIST = "{{DAILY_TOP_DESTINATIONS_LIST}}";
|
||||
const QString HA_TOTAL_TOP_DESTINATIONS_LIST = "{{TOTAL_TOP_DESTINATIONS_LIST}}";
|
||||
const QString HA_BLOCKED_DESTINTIONS = "{{BLOCKED_DESTINATIONS}}";
|
||||
const QString HA_INFORMATION = "{{INFORMATION}}";
|
||||
const QString HA_COPYRIGHT = "{{COPYRIGHT}}";
|
||||
} // namespace c
|
||||
|
||||
namespace p {
|
||||
uint LAST_AND_TOP_LIST_SIZE = 5;
|
||||
QStringList IGNORED_DESTINATIONS = {"[0.0.0.0]", "127.0.0.1"};
|
||||
QString WORKING_DIR = "data";
|
||||
QString SERVICE_TITLE = "3proxy-eagle";
|
||||
QString BIND_TO_ADDRESS = "127.0.0.1";
|
||||
quint16 BIND_TO_PORT = 8161;
|
||||
} // namespace p
|
||||
|
||||
std::list< std::pair<QThread*, ProxyInstanse*> > instanses;
|
||||
|
||||
LogLevel logLevel = LogLevel::Info;
|
||||
|
||||
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
if (logLevel == LogLevel::Off) return;
|
||||
|
||||
static QMutex mtx;
|
||||
QMutexLocker lock (&mtx);
|
||||
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
const char *file = context.file ? context.file : "";
|
||||
const char *function = context.function ? context.function : "";
|
||||
|
||||
if (type == QtDebugMsg)
|
||||
{
|
||||
if (logLevel == LogLevel::Debug)
|
||||
{
|
||||
std::cout << "[Dbug]: " << localMsg.constData() << " " << function << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == QtInfoMsg)
|
||||
{
|
||||
if (logLevel >= LogLevel::Info)
|
||||
{
|
||||
std::cout << "[Info]: " << localMsg.constData() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == QtWarningMsg)
|
||||
{
|
||||
if (logLevel >= LogLevel::Warning)
|
||||
{
|
||||
std::cerr << "[Warn]: " << localMsg.constData() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == QtCriticalMsg)
|
||||
{
|
||||
if (logLevel >= LogLevel::Error)
|
||||
{
|
||||
std::cerr << "[Crit]: " << localMsg.constData() << " (" << file << "," << context.line << "," << function << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == QtFatalMsg)
|
||||
{
|
||||
if (logLevel >= LogLevel::Error)
|
||||
{
|
||||
std::cerr << "[Fatl]: " << localMsg.constData() << " (" << file << "," << context.line << "," << function << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString getValue(const QString &string, const QString &key, GetValueType type)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (type == GetValueType::HttpHeader)
|
||||
{
|
||||
if (string.indexOf(QRegularExpression(key + ":")) == -1)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
else if (string.indexOf(QRegularExpression(key + "\\s*=")) == -1)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString result {string};
|
||||
if (type == GetValueType::HttpHeader)
|
||||
{
|
||||
result.remove(QRegularExpression("^.*"+key+":\\s", QRegularExpression::DotMatchesEverythingOption));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.remove(QRegularExpression("^.*"+key+"\\s*=", QRegularExpression::DotMatchesEverythingOption));
|
||||
}
|
||||
|
||||
QString separator {' '};
|
||||
if (type == GetValueType::Url)
|
||||
{
|
||||
separator = '&';
|
||||
}
|
||||
else if (type == GetValueType::HttpHeader)
|
||||
{
|
||||
separator = "\r\n";
|
||||
}
|
||||
|
||||
int valueEnd = result.indexOf(separator);
|
||||
|
||||
if (valueEnd == -1 and type == GetValueType::Url)
|
||||
{
|
||||
separator = ' ';
|
||||
valueEnd = result.indexOf(separator);
|
||||
if (valueEnd != -1)
|
||||
{
|
||||
result.remove(valueEnd, result.size()-valueEnd);
|
||||
}
|
||||
}
|
||||
else if (valueEnd != -1)
|
||||
{
|
||||
result.remove(valueEnd, result.size()-valueEnd);
|
||||
}
|
||||
|
||||
if (type == GetValueType::Url)
|
||||
{
|
||||
result = QByteArray::fromPercentEncoding(result.toUtf8());
|
||||
result.replace('+', ' ');
|
||||
}
|
||||
else if (type == GetValueType::HttpHeader)
|
||||
{
|
||||
result.remove('\"');
|
||||
}
|
||||
else
|
||||
{
|
||||
static QRegularExpression beginSpaces("^\\s*");
|
||||
static QRegularExpression endSpaces("\\s*$");
|
||||
result.remove(beginSpaces);
|
||||
result.remove(endSpaces);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QString hash(const QByteArray &data)
|
||||
{
|
||||
QString hash = QCryptographicHash::hash(data, QCryptographicHash::Md5).toBase64();
|
||||
static QRegularExpression rgx_removeTrash ("[^0-9a-zA-Z_]");
|
||||
hash.remove(rgx_removeTrash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
QString hash(QFile file)
|
||||
{
|
||||
if (not file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Hash failed: file openning failed:" << file.fileName();
|
||||
return QString();
|
||||
}
|
||||
QString result = hash(file.readAll());
|
||||
file.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace g
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proxyinstanse.h"
|
||||
#include <QString>
|
||||
|
||||
class QFile;
|
||||
|
||||
namespace g /* for Global */ {
|
||||
|
||||
namespace c /* for Constants */ {
|
||||
extern const QString SOFTWARE_NAME;
|
||||
extern const QString SOFTWARE_VERSION;
|
||||
extern const QString COPYRIGHT;
|
||||
extern const QString HA_PAGE_TITLE;
|
||||
extern const QString HA_CUSTOM_CSS;
|
||||
extern const QString HA_DAILY_UPLOAD;
|
||||
extern const QString HA_DAILY_UPLOAD_MEASURE;
|
||||
extern const QString HA_DAILY_DOWNLOAD;
|
||||
extern const QString HA_DAILY_DOWNLOAD_MEASURE;
|
||||
extern const QString HA_TOTAL_UPLOAD;
|
||||
extern const QString HA_TOTAL_DOWNLOAD;
|
||||
extern const QString HA_LAST_DESTINATIONS_LIST;
|
||||
extern const QString HA_DAILY_TOP_DESTINATIONS_LIST;
|
||||
extern const QString HA_TOTAL_TOP_DESTINATIONS_LIST;
|
||||
extern const QString HA_BLOCKED_DESTINTIONS;
|
||||
extern const QString HA_INFORMATION;
|
||||
extern const QString HA_COPYRIGHT;
|
||||
} // namespace c
|
||||
|
||||
namespace p /* for Parameters */ {
|
||||
extern QString WORKING_DIR;
|
||||
extern QStringList IGNORED_DESTINATIONS;
|
||||
extern QString SERVICE_TITLE;
|
||||
extern uint LAST_AND_TOP_LIST_SIZE;
|
||||
extern QString BIND_TO_ADDRESS;
|
||||
extern quint16 BIND_TO_PORT;
|
||||
} // namespace p
|
||||
|
||||
extern std::list< std::pair<QThread*, ProxyInstanse*> > instanses;
|
||||
|
||||
enum LogLevel : uint8_t
|
||||
{
|
||||
Off = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Info = 3,
|
||||
Debug = 4
|
||||
};
|
||||
extern LogLevel logLevel;
|
||||
|
||||
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
enum class GetValueType { // QString getValue()
|
||||
Default = 1,
|
||||
Url = 2,
|
||||
HttpHeader = 3
|
||||
};
|
||||
QString getValue(const QString &string, const QString &key, GetValueType type = GetValueType::Default);
|
||||
QString hash(const QByteArray &data);
|
||||
QString hash(QFile file);
|
||||
|
||||
} // namespace g
|
||||
@@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>html/index.html</file>
|
||||
<file>html/main.css</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 219.3 493.4" style="enable-background:new 0 0 219.3 493.4;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M218.4,122.5c-1.5,3.6-4.3,5.4-8.3,5.4h-64v356.3c0,2.7-0.9,4.9-2.6,6.6c-1.7,1.7-3.9,2.6-6.6,2.6H82.2
|
||||
c-2.7,0-4.9-0.9-6.6-2.6c-1.7-1.7-2.6-3.9-2.6-6.6V127.9h-64c-3.8,0-6.6-1.8-8.3-5.4c-1.5-3.6-1-6.9,1.4-10L103.7,2.9
|
||||
c1.9-1.9,4.2-2.9,6.9-2.9c2.5,0,4.7,0.9,6.6,2.9L217,112.5C219.5,115.5,220,118.9,218.4,122.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 585 B |
@@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{PAGE_TITLE}}</title>
|
||||
<link rel="stylesheet" href="/main.css">{{CUSTOM_CSS}}
|
||||
</head>
|
||||
<body>
|
||||
<main class="main container">
|
||||
<div class="proxyService">
|
||||
<h1 class="proxyService__title">
|
||||
{{PAGE_TITLE}}
|
||||
</h1>
|
||||
<section class="proxyService__lastDestinations lastDestinations section">
|
||||
<div class="lastDestinations__title section__title">
|
||||
Last destinations
|
||||
</div>
|
||||
<ul class="lastDestinations__list">
|
||||
{{LAST_DESTINATIONS_LIST}}
|
||||
</ul>
|
||||
</section>
|
||||
<section class="proxyService__handledTraffic handledTraffic section">
|
||||
<div class="handledTraffic__mainTitle section__title">
|
||||
Traffic
|
||||
</div>
|
||||
<div class="handledTraffic__blocks">
|
||||
<div class="handledTraffic__block handledTraffic__block_upload">
|
||||
<div class="handledTraffic__title">
|
||||
Daily upload
|
||||
</div>
|
||||
<div class="handledTraffic__amount">
|
||||
<div class="handledTraffic__icon">
|
||||
<svg class="handledTraffic__svg" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 219.3 493.4" style="enable-background:new 0 0 219.3 493.4;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M218.4,122.5c-1.5,3.6-4.3,5.4-8.3,5.4h-64v356.3c0,2.7-0.9,4.9-2.6,6.6c-1.7,1.7-3.9,2.6-6.6,2.6H82.2
|
||||
c-2.7,0-4.9-0.9-6.6-2.6c-1.7-1.7-2.6-3.9-2.6-6.6V127.9h-64c-3.8,0-6.6-1.8-8.3-5.4c-1.5-3.6-1-6.9,1.4-10L103.7,2.9
|
||||
c1.9-1.9,4.2-2.9,6.9-2.9c2.5,0,4.7,0.9,6.6,2.9L217,112.5C219.5,115.5,220,118.9,218.4,122.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="handledTraffic__number">
|
||||
{{DAILY_UPLOAD}}
|
||||
</div>
|
||||
<div class="handledTraffic__measure">
|
||||
{{DAILY_UPLOAD_MEASURE}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="handledTraffic__total">
|
||||
<div class="handledTraffic__totalTitle">
|
||||
Total
|
||||
</div>
|
||||
<div class="handledTraffic__totalAmount">
|
||||
{{TOTAL_UPLOAD}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="handledTraffic__block handledTraffic__block_download">
|
||||
<div class="handledTraffic__title">
|
||||
Daily download
|
||||
</div>
|
||||
<div class="handledTraffic__amount">
|
||||
<div class="handledTraffic__icon">
|
||||
<svg class="handledTraffic__svg" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 219.3 493.4" style="enable-background:new 0 0 219.3 493.4;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M218.4,370.9c-1.5-3.6-4.3-5.4-8.3-5.4h-64V9.1c0-2.7-0.9-4.9-2.6-6.6c-1.7-1.7-3.9-2.6-6.6-2.6H82.2
|
||||
c-2.7,0-4.9,0.9-6.6,2.6c-1.7,1.7-2.6,3.9-2.6,6.6v356.3h-64c-3.8,0-6.6,1.8-8.3,5.4c-1.5,3.6-1,6.9,1.4,10l101.4,109.6
|
||||
c1.9,1.9,4.2,2.9,6.9,2.9c2.5,0,4.7-0.9,6.6-2.9L217,380.9C219.5,377.8,220,374.5,218.4,370.9z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="handledTraffic__number">
|
||||
{{DAILY_DOWNLOAD}}
|
||||
</div>
|
||||
<div class="handledTraffic__measure">
|
||||
{{DAILY_DOWNLOAD_MEASURE}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="handledTraffic__total">
|
||||
<div class="handledTraffic__totalTitle">
|
||||
Total
|
||||
</div>
|
||||
<div class="handledTraffic__totalAmount">
|
||||
{{TOTAL_DOWNLOAD}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="proxyService__topDestinations topDestinations section">
|
||||
<div class="section__title">
|
||||
Top destinations
|
||||
</div>
|
||||
<div class="topDestinations__block">
|
||||
<div class="topDestinations__title">
|
||||
Daily
|
||||
</div>
|
||||
<ul class="topDestinations__list topDestinations__list_daily">
|
||||
{{DAILY_TOP_DESTINATIONS_LIST}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="topDestinations__block">
|
||||
<div class="topDestinations__title">
|
||||
Total
|
||||
</div>
|
||||
<ul class="topDestinations__list topDestinations__list_total">
|
||||
{{TOTAL_TOP_DESTINATIONS_LIST}}
|
||||
</ul>
|
||||
</div>
|
||||
</section>{{BLOCKED_DESTINATIONS}}{{INFORMATION}}
|
||||
</div>
|
||||
</main>
|
||||
<div class="proxyService__copyright copyright">
|
||||
{{COPYRIGHT}} | FE by Trotsky | <a href="https://notabug.org/acetone/3proxy-eagle" target="_blank">source code</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
*{box-sizing:border-box;outline:none;z-index:1}*:hover{outline:none}*:focus{outline:none}html{width:100%;font-size:16px;scroll-behavior:smooth}body{overflow-y:scroll;height:100%;line-height:1.42;margin:0;background-color:#fff;color:#000;height:100vh;font-family:monospace;font-size:16px}main{display:flex;flex-flow:column nowrap;align-items:stretch;justify-content:flex-start}ul,ol{padding:0;margin:0}li{list-style:none}a{text-decoration:none;color:inherit}a:hover{text-decoration:none}button{cursor:pointer;background-color:rgba(0,0,0,0);border:0px solid rgba(0,0,0,0);padding:0}button:focus,input:focus,a:focus,textarea:focus{text-decoration:none}button,input,textarea{font-family:inherit;font-size:inherit}textarea{resize:none}p{margin:0}h1,h2,h3,h4,h5{margin-top:0;margin-bottom:0;font-weight:normal;font-size:100%}img{display:block;max-width:100%;height:auto}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}table{border-collapse:collapse}input[type=checkbox]{margin:0}.wrapper{height:100%;padding:16px 0;flex:1}.container{max-width:648px;margin:0 auto;padding:0 5px}.section{margin-bottom:35px}.section__title{margin-bottom:10px;font-size:20px;font-weight:bold}.proxyService__title{text-align:center;font-size:50px;padding:12px 0;font-weight:bold}.handledTraffic{width:100%}.handledTraffic__blocks{width:100%;display:flex;flex-flow:row nowrap;align-items:start;justify-content:center;gap:10px}.handledTraffic__block{width:calc(50% - 5px)}.handledTraffic__block_download .handledTraffic__svg{fill:#91fd91}.handledTraffic__block_upload .handledTraffic__svg{fill:#ff6f6f}.handledTraffic__title{text-transform:uppercase;font-weight:bold;background-color:#ddd;padding:3px 5px;font-size:20px}.handledTraffic__amount{display:flex;flex-flow:row nowrap;align-items:stretch;justify-content:center;background-color:#aaa;padding:10px;border-top:1px solid #000;border-bottom:1px solid #000}.handledTraffic__icon{text-align:center;display:flex;flex-flow:column nowrap;align-items:center;justify-content:center;padding:3px 8px;margin-right:5px}.handledTraffic__icon .handledTraffic__svg{height:35px}.handledTraffic__number{line-height:1;font-size:56px;font-weight:bold;text-transform:uppercase;margin-right:8px}.handledTraffic__measure{font-size:18px;font-weight:bold;align-self:end}.handledTraffic__total{background-color:#ddd;padding:8px;font-size:16px;text-align:right}.handledTraffic__totalTitle{text-transform:uppercase;font-weight:bold}.accordeo__radio{display:none}.accordeo__radio:checked~.accordeo__dropdown{display:block}.accordeo__dropdown{display:none}.blockedDestinations__item{background-color:#ddd;overflow:hidden;white-space:nowrap;margin-bottom:5px;text-overflow:ellipsis}.blockedDestinations__url{display:block;width:100%;padding:3px 6px;cursor:pointer}.blockedDestinations__ipList{background-color:#aaa;padding-left:10px}.blockedDestinations__ipItem{padding:4px}.lastDestinations{width:100%;padding:5px 0}.lastDestinations__list{width:100%}.lastDestinations__item{width:100%;margin-bottom:5px;padding:3px 6px;overflow:hidden;text-overflow:ellipsis}.lastDestinations__item:last-child{margin-bottom:0}.lastDestinations__item_blocked{background-color:#888}.lastDestinations__item_allowed{background-color:#ddd}.topDestinations__block{margin-bottom:14px}.topDestinations__block:last-child{margin-bottom:0}.topDestinations__title{font-weight:bold;margin-bottom:10px}.topDestinations__item{padding:4px;overflow:hidden;white-space:nowrap;margin-bottom:5px;text-overflow:ellipsis;background-color:#ddd}.copyright{padding:10px 0;text-align:center;color:#888}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "httpdocument.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QMapIterator>
|
||||
#include <QJsonDocument>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
const QString HttpDocument::Code::_200 = "200 OK";
|
||||
const QString HttpDocument::Code::_303 = "303 See Other";
|
||||
const QString HttpDocument::Code::_304 = "304 Not Modified";
|
||||
const QString HttpDocument::Code::_400 = "400 Bad request";
|
||||
const QString HttpDocument::Code::_403 = "403 Access Denied";
|
||||
const QString HttpDocument::Code::_404 = "404 Not Found";
|
||||
const QString HttpDocument::Code::_413 = "413 Payload Too Large";
|
||||
const QString HttpDocument::Code::_500 = "500 Internal Error";
|
||||
|
||||
const QString HttpDocument::ContentType::HTML = "text/html; charset=utf-8";
|
||||
const QString HttpDocument::ContentType::TEXT = "text/plain; charset=utf-8";
|
||||
const QString HttpDocument::ContentType::CSS = "text/css";
|
||||
const QString HttpDocument::ContentType::SVG = "image/svg+xml";
|
||||
const QString HttpDocument::ContentType::ICO = "image/ico";
|
||||
const QString HttpDocument::ContentType::PNG = "image/png";
|
||||
const QString HttpDocument::ContentType::GIF = "image/gif";
|
||||
const QString HttpDocument::ContentType::JS = "text/javascript; charset=utf-8";
|
||||
const QString HttpDocument::ContentType::WOFF = "font/woff";
|
||||
const QString HttpDocument::ContentType::WOFF2 = "font/woff2";
|
||||
const QString HttpDocument::ContentType::JSON = "application/json; charset=utf-8";
|
||||
const QString HttpDocument::ContentType::MP3 = "audio/mpeg";
|
||||
const QString HttpDocument::ContentType::BIN = "application/octet-stream";
|
||||
|
||||
const QString HttpDocument::CommonHeader::ContentType = "Content-Type";
|
||||
const QString HttpDocument::CommonHeader::ContentLength = "Content-Length";
|
||||
const QString HttpDocument::CommonHeader::ETag = "ETag";
|
||||
const QString HttpDocument::CommonHeader::ProcessingDuration = "Processing-duration";
|
||||
|
||||
HttpDocument::HttpDocument() :
|
||||
m_processingDurationStartMarker(QDateTime::currentMSecsSinceEpoch())
|
||||
{
|
||||
setCode(Code::_200);
|
||||
}
|
||||
|
||||
void HttpDocument::setCode(const QString &code)
|
||||
{
|
||||
m_code = code;
|
||||
}
|
||||
|
||||
void HttpDocument::setHeader(const QString &name, const QString &value)
|
||||
{
|
||||
if (name.isEmpty() or value.isEmpty()) return;
|
||||
|
||||
m_headers.insert(name, value);
|
||||
}
|
||||
|
||||
void HttpDocument::setBody(const QByteArray &body)
|
||||
{
|
||||
m_body = body;
|
||||
}
|
||||
|
||||
void HttpDocument::setBody(const QJsonObject &body)
|
||||
{
|
||||
m_body = QJsonDocument(body).toJson(QJsonDocument::JsonFormat::Compact);
|
||||
setHeader(CommonHeader::ContentType, ContentType::JSON);
|
||||
}
|
||||
|
||||
void HttpDocument::setBody(QFile payload, const QString& oldETag)
|
||||
{
|
||||
if (not payload.exists())
|
||||
{
|
||||
qDebug() << "File" << payload.fileName() << "not found";
|
||||
|
||||
setCode(Code::_404);
|
||||
setBodySingleHTMLLabel("Not found", "Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (not payload.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "File" << payload.fileName() << "reading failed";
|
||||
|
||||
setCode(Code::_500);
|
||||
setBodySingleHTMLLabel("Not available", "Can't read");
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray fileBytes = payload.readAll();
|
||||
payload.close();
|
||||
|
||||
QString eTag = g::hash(fileBytes);
|
||||
|
||||
if (oldETag == eTag)
|
||||
{
|
||||
qDebug() << "File cached by user:" << payload.fileName() << "/ Hash:" << oldETag;
|
||||
|
||||
setCode(Code::_304);
|
||||
return;
|
||||
}
|
||||
|
||||
setBody(fileBytes);
|
||||
setHeader(CommonHeader::ETag, eTag);
|
||||
|
||||
setHeader(CommonHeader::ContentType,
|
||||
payload.fileName().endsWith(".html") ? ContentType::HTML :
|
||||
payload.fileName().endsWith(".css") ? ContentType::CSS :
|
||||
payload.fileName().endsWith(".js") ? ContentType::JS :
|
||||
payload.fileName().endsWith(".ico") ? ContentType::ICO :
|
||||
payload.fileName().endsWith(".svg") ? ContentType::SVG :
|
||||
payload.fileName().endsWith(".png") ? ContentType::PNG :
|
||||
payload.fileName().endsWith(".woff") ? ContentType::WOFF :
|
||||
payload.fileName().endsWith(".woff2") ? ContentType::WOFF2 :
|
||||
payload.fileName().endsWith(".json") ? ContentType::JSON :
|
||||
payload.fileName().endsWith(".mp3") ? ContentType::MP3 :
|
||||
payload.fileName().endsWith(".gif") ? ContentType::GIF :
|
||||
payload.fileName().endsWith(".txt") ? ContentType::TEXT :
|
||||
ContentType::BIN);
|
||||
}
|
||||
|
||||
void HttpDocument::setBodySingleHTMLLabel(const QString &text, const QString& title)
|
||||
{
|
||||
m_body = "<!DOCTYPE html>\r\n"
|
||||
"<head>\r\n"
|
||||
" <title>"+title.toUtf8()+"</title>\r\n"
|
||||
"</head>\r\n"
|
||||
"<style>\r\n"
|
||||
" * { font-family: monospace }\r\n"
|
||||
"</style>\r\n"
|
||||
"<center>\r\n"
|
||||
" <h1><p>"+text.toUtf8()+"</p></h1>\r\n"
|
||||
" <h2><p>[<a href=\"/\" style=\"text-decoration: none\">back to main</a>]</p></h2>\r\n"
|
||||
" <br><br>\r\n"
|
||||
"</p>\r\n"
|
||||
" <p style=\"color: gray\">3PROXY-EAGLE</p>\r\n"
|
||||
"</center>";
|
||||
}
|
||||
|
||||
QByteArray HttpDocument::document() const
|
||||
{
|
||||
static constexpr const char HTML_NEWLINE[] {"\r\n"};
|
||||
|
||||
QByteArray result = "HTTP/1.0 " + code().toUtf8() + HTML_NEWLINE;
|
||||
|
||||
auto h = headers();
|
||||
QMapIterator iter(h);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
iter.next();
|
||||
result += (iter.key()+": "+iter.value()+HTML_NEWLINE).toUtf8();
|
||||
}
|
||||
|
||||
result += QString( CommonHeader::ContentLength + ": " +
|
||||
QString::number( body().size()).toUtf8() ).toUtf8() + HTML_NEWLINE;
|
||||
result += QString( CommonHeader::ProcessingDuration + ": " +
|
||||
QString::number( QDateTime::currentMSecsSinceEpoch()-m_processingDurationStartMarker) ).toUtf8() + HTML_NEWLINE;
|
||||
result += HTML_NEWLINE;
|
||||
|
||||
result += body();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QJsonObject>
|
||||
#include <QFile>
|
||||
|
||||
class HttpDocument
|
||||
{
|
||||
public:
|
||||
struct CommonHeader
|
||||
{
|
||||
static const QString ContentType;
|
||||
static const QString ContentLength;
|
||||
static const QString ETag;
|
||||
static const QString ProcessingDuration;
|
||||
};
|
||||
|
||||
struct ContentType
|
||||
{
|
||||
static const QString HTML;
|
||||
static const QString TEXT;
|
||||
static const QString CSS;
|
||||
static const QString SVG;
|
||||
static const QString ICO;
|
||||
static const QString PNG;
|
||||
static const QString GIF;
|
||||
static const QString JS;
|
||||
static const QString WOFF;
|
||||
static const QString WOFF2;
|
||||
static const QString JSON;
|
||||
static const QString MP3;
|
||||
static const QString BIN;
|
||||
};
|
||||
|
||||
struct Code {
|
||||
static const QString _200;
|
||||
static const QString _303;
|
||||
static const QString _304;
|
||||
static const QString _400;
|
||||
static const QString _403;
|
||||
static const QString _404;
|
||||
static const QString _413;
|
||||
static const QString _500;
|
||||
};
|
||||
|
||||
HttpDocument();
|
||||
void setCode(const QString& code = Code::_200);
|
||||
QString code() const { return m_code; }
|
||||
|
||||
void setHeader(const QString& name, const QString& value);
|
||||
QMap<QString, QString> headers() const { return m_headers; }
|
||||
|
||||
void setBody(const QByteArray& body);
|
||||
void setBody(const QJsonObject& body);
|
||||
void setBody(QFile payload, const QString& eTag = QString());
|
||||
void setBodySingleHTMLLabel(const QString& text, const QString& title = "Notice");
|
||||
QByteArray body() const { return m_body; }
|
||||
|
||||
QByteArray document() const;
|
||||
|
||||
private:
|
||||
QString m_code = Code::_200;
|
||||
QMap<QString, QString> m_headers;
|
||||
QByteArray m_body;
|
||||
|
||||
const qint64 m_processingDurationStartMarker;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "httpserver.h"
|
||||
#include "socketrunnable.h"
|
||||
|
||||
HttpServer::HttpServer(QObject *parent) :
|
||||
WebServerBase(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HttpServer::killTheCapitalism()
|
||||
{
|
||||
qWarning() << "Try to kill capitalism...";
|
||||
QThread::sleep(1);
|
||||
qWarning() << "Pick up black flag...";
|
||||
QThread::sleep(1);
|
||||
qWarning() << "Hmmm. Timed out. Try later with friends!";
|
||||
}
|
||||
|
||||
void HttpServer::incomingConnection(qintptr handle)
|
||||
{
|
||||
SocketRunnable* socket = new SocketRunnable(handle);
|
||||
socket->setAutoDelete(true);
|
||||
pool()->start(socket);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "webserverbase.h"
|
||||
|
||||
class HttpServer : public WebServerBase
|
||||
{
|
||||
public:
|
||||
HttpServer(QObject *parent = nullptr);
|
||||
void killTheCapitalism();
|
||||
|
||||
// QTcpServer interface
|
||||
protected:
|
||||
void incomingConnection(qintptr handle);
|
||||
};
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "proxyinstanse.h"
|
||||
#include "httpserver.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QProcess>
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
|
||||
void terminate(int)
|
||||
{
|
||||
std::cout << "Terminating..." << std::endl;
|
||||
for (const auto& instanse: g::instanses)
|
||||
{
|
||||
instanse.first->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
std::cout << g::c::SOFTWARE_NAME.toStdString() << " " << g::c::SOFTWARE_VERSION.toStdString() << std::endl
|
||||
<< "Accumulate ethical 3proxy statistics with web interface\n\n"
|
||||
|
||||
"U S A G E:\n"
|
||||
" -i --instanse <3proxy>,<3proxy.cfg>\n"
|
||||
" -w --working-directory <data>\n"
|
||||
" -t --service-title <3proxy-eagle>\n"
|
||||
" -s --last-and-top-list-size <5>\n"
|
||||
" -I --ignored-destinations <[0.0.0.0],127.0.0.1>\n"
|
||||
" -a --bind-to-address <127.0.0.1>\n"
|
||||
" -p --bind-to-port <8161>\n"
|
||||
" -l --log-level <info> (off, error, warn, info, debug)\n\n"
|
||||
|
||||
"N O T E S:\n"
|
||||
"* Multi instanses supported. Just pass new one --instanse value!\n"
|
||||
"* Main 3proxy cfg must contain log to stdout with strict format:\n"
|
||||
" log\n"
|
||||
" logformat \" type=%N destination=%n to=%I from=%O\"\n"
|
||||
"* 3proxy cfg can contain blocked domains in format:\n"
|
||||
" {{vk.com,mail.ru,google.com}}\n"
|
||||
" This domains will be resolved automatically and replaced by:\n"
|
||||
" deny * * original.domain\n"
|
||||
" deny * * 8.8.8.8,1.1.1.1 # resolved addresses"
|
||||
"* If the working directory contains a information.html, the Information\n"
|
||||
" box will be added to the web page. The block is full html-formatted.\n"
|
||||
"* The html folder can contain any files, they will be available\n"
|
||||
" for downloading through the web browser.\n"
|
||||
"* html folder can contain styles.css file for overloading default styles.\n"
|
||||
" See page source via web browser to customize CSS classes.\n"
|
||||
|
||||
"\n" << g::c::COPYRIGHT.toStdString() << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
qInstallMessageHandler(g::customMessageOutput);
|
||||
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
QList<QPair<QString,QString>> instanses;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
QString key(argv[i]);
|
||||
QString value;
|
||||
if (i+1 < argc)
|
||||
{
|
||||
value = argv[i+1];
|
||||
}
|
||||
|
||||
if (key == "--log-level" and not value.isEmpty())
|
||||
{
|
||||
if (value.contains("off", Qt::CaseInsensitive))
|
||||
{
|
||||
g::logLevel = g::LogLevel::Off;
|
||||
}
|
||||
else if (value.contains("error", Qt::CaseInsensitive))
|
||||
{
|
||||
g::logLevel = g::LogLevel::Error;
|
||||
}
|
||||
else if (value.contains("warn", Qt::CaseInsensitive))
|
||||
{
|
||||
g::logLevel = g::LogLevel::Warning;
|
||||
}
|
||||
else if (value.contains("info", Qt::CaseInsensitive))
|
||||
{
|
||||
g::logLevel = g::LogLevel::Info;
|
||||
}
|
||||
else if (value.contains("debug", Qt::CaseInsensitive))
|
||||
{
|
||||
g::logLevel = g::LogLevel::Debug;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Invalid log level flag:" << value << "(maybe you should read the --help)";
|
||||
}
|
||||
}
|
||||
|
||||
else if ((key == "-i" or key == "--instanse") and not value.isEmpty())
|
||||
{
|
||||
QStringList splitted = value.split(',');
|
||||
if (splitted.size() != 2)
|
||||
{
|
||||
qWarning() << "--instanse parsing failed:" << value;
|
||||
continue;
|
||||
}
|
||||
instanses.push_back( {splitted.front(), splitted.back()} );
|
||||
}
|
||||
|
||||
else if ((key == "-w" or key == "--working-directory") and not value.isEmpty())
|
||||
{
|
||||
g::p::WORKING_DIR = value;
|
||||
}
|
||||
|
||||
else if ((key == "-s" or key == "--last-and-top-list-size") and not value.isEmpty())
|
||||
{
|
||||
bool ok = false;
|
||||
value.toUInt(&ok);
|
||||
if (not ok)
|
||||
{
|
||||
qWarning() << "--last-and-top-list-size parsing failed, not a positive number:" << value;
|
||||
continue;
|
||||
}
|
||||
g::p::LAST_AND_TOP_LIST_SIZE = value.toUInt();
|
||||
}
|
||||
|
||||
else if ((key == "-I" or key == "--ignored-destinations") and not value.isEmpty())
|
||||
{
|
||||
g::p::IGNORED_DESTINATIONS = value.split(',');
|
||||
}
|
||||
|
||||
else if ((key == "-b" or key == "--bind-to-address") and not value.isEmpty())
|
||||
{
|
||||
g::p::BIND_TO_ADDRESS = value;
|
||||
}
|
||||
|
||||
else if ((key == "-p" or key == "--bind-to-port") and not value.isEmpty())
|
||||
{
|
||||
bool ok = false;
|
||||
value.toUShort(&ok);
|
||||
if (not ok)
|
||||
{
|
||||
qWarning() << "--bind-to-port parsing failed, incorrect port value:" << value;
|
||||
continue;
|
||||
}
|
||||
g::p::BIND_TO_PORT = value.toUShort();
|
||||
}
|
||||
|
||||
else if ((key == "-t" or key == "--service-title") and not value.isEmpty())
|
||||
{
|
||||
g::p::SERVICE_TITLE = value;
|
||||
}
|
||||
|
||||
else if (key == "-h" or key == "--help")
|
||||
{
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
qInfo().noquote() << "Instanses count:" << instanses.size();
|
||||
qInfo().noquote() << "Working directory:" << g::p::WORKING_DIR;
|
||||
qInfo().noquote() << "Last and top dest list size:" << g::p::LAST_AND_TOP_LIST_SIZE;
|
||||
qInfo().noquote() << "Ignored destinations:" << g::p::IGNORED_DESTINATIONS;
|
||||
qInfo().noquote() << "Bind to address:" << g::p::BIND_TO_ADDRESS;
|
||||
qInfo().noquote() << "Bind to port:" << g::p::BIND_TO_PORT;
|
||||
qInfo() << "";
|
||||
|
||||
for (const auto& pair: instanses)
|
||||
{
|
||||
QThread* thread = new QThread;
|
||||
ProxyInstanse* pi = new ProxyInstanse;
|
||||
pi->set3proxyBinaryFile(pair.first);
|
||||
pi->set3proxyConfigFile(pair.second);
|
||||
pi->moveToThread(thread);
|
||||
QObject::connect(thread, &QThread::started, pi, &ProxyInstanse::start);
|
||||
QObject::connect(thread, &QThread::finished, pi, &ProxyInstanse::deleteLater);
|
||||
thread->start();
|
||||
g::instanses.push_back( {thread, pi} );
|
||||
}
|
||||
|
||||
(new HttpServer)/*->killTheCapitalism()*/;
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "proxyinstanse.h"
|
||||
#include "statistics.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QHostInfo>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
ProxyInstanse::ProxyInstanse(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ProxyInstanse::~ProxyInstanse()
|
||||
{
|
||||
qInfo() << "Proxy instanse" << g::hash(confPath().toUtf8()) << "terminated";
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
m_process->terminate();
|
||||
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
m_process, &QProcess::deleteLater);
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyInstanse::set3proxyBinaryFile(const QString &pathTo3proxyBinFile)
|
||||
{
|
||||
if (m_process)
|
||||
{
|
||||
qWarning() << "ProxyInstanse::set3proxyBinaryFile() process already started";
|
||||
}
|
||||
if (not QFile::exists(pathTo3proxyBinFile))
|
||||
{
|
||||
qCritical() << "ProxyInstanse::set3proxyBinaryFile()" << pathTo3proxyBinFile << "not exists";
|
||||
}
|
||||
m_binPath = pathTo3proxyBinFile;
|
||||
}
|
||||
|
||||
void ProxyInstanse::set3proxyConfigFile(const QString &pathTo3proxyConfFile)
|
||||
{
|
||||
if (m_process)
|
||||
{
|
||||
qWarning() << "ProxyInstanse::set3proxyConfigFile() process already started";
|
||||
}
|
||||
|
||||
if (not QFile::exists(pathTo3proxyConfFile))
|
||||
{
|
||||
qCritical() << "ProxyInstanse::set3proxyConfigFile()" << pathTo3proxyConfFile << "not exists";
|
||||
}
|
||||
|
||||
m_confPath = pathTo3proxyConfFile;
|
||||
|
||||
createConfigurationFile();
|
||||
}
|
||||
|
||||
void ProxyInstanse::generateBlackList(const QStringList &domainsOrAddresses)
|
||||
{
|
||||
qDebug() << "ProxyInstanse::generateBlackList() raw:" << domainsOrAddresses;
|
||||
|
||||
QStringListIterator iterRaw(domainsOrAddresses);
|
||||
while (iterRaw.hasNext())
|
||||
{
|
||||
QString name = iterRaw.next();
|
||||
m_blackList.insert(name, QStringList());
|
||||
static QRegularExpression rgx_hiddenNetworks("^.*\\.(i2p|onion|loki)$");
|
||||
if (name.contains(rgx_hiddenNetworks))
|
||||
{
|
||||
qDebug() << name << "is hidden network name, no attempt to resolve";
|
||||
continue;
|
||||
}
|
||||
|
||||
QHostInfo host = QHostInfo::fromName(name);
|
||||
if (host.error() != QHostInfo::NoError)
|
||||
{
|
||||
qDebug() << "Can not resolve" << name << ":" << host.errorString();
|
||||
continue;
|
||||
}
|
||||
else if (host.addresses().first().toString() == name)
|
||||
{
|
||||
qDebug() << name << "not a domain name (it's normal, just debug info)";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto resolved = host.addresses();
|
||||
for (auto it = resolved.begin(); it != resolved.end(); ++it)
|
||||
{
|
||||
QString address = it->toString();
|
||||
qDebug() << "Domain" << name << "resolved to" << address;
|
||||
m_blackList[name].push_back(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ProxyInstanse::workingConfigPath() const
|
||||
{
|
||||
return g::p::WORKING_DIR + "/" + g::hash(confPath()) + ".cfg";
|
||||
}
|
||||
|
||||
void ProxyInstanse::start()
|
||||
{
|
||||
m_process = new QProcess;
|
||||
m_process->setProgram(binPath());
|
||||
m_process->setArguments(QStringList() << workingConfigPath());
|
||||
|
||||
connect (m_process, &QProcess::readyReadStandardOutput, this, &ProxyInstanse::reader);
|
||||
connect (m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &ProxyInstanse::restart);
|
||||
|
||||
m_process->start();
|
||||
}
|
||||
|
||||
void ProxyInstanse::reader()
|
||||
{
|
||||
QMutexLocker lock (&m_readerMtx);
|
||||
|
||||
m_readBuffer = m_process->readAll();
|
||||
|
||||
bool lastStringIsUncomplete = not m_readBuffer.endsWith('\n');
|
||||
QStringList completeLines = m_readBuffer.split('\n');
|
||||
|
||||
if (lastStringIsUncomplete)
|
||||
{
|
||||
m_readBuffer = completeLines.last();
|
||||
completeLines.pop_back();
|
||||
}
|
||||
|
||||
for (const auto& str: completeLines)
|
||||
{
|
||||
if (str.isEmpty()) continue;
|
||||
QString type = g::getValue(str, "type");
|
||||
QString dest = g::getValue(str, "destination");
|
||||
quint64 to = g::getValue(str, "to").toULongLong();
|
||||
quint64 from = g::getValue(str, "from").toULongLong();
|
||||
|
||||
if (type.isEmpty() or dest.isEmpty())
|
||||
{
|
||||
qWarning() << "Receive incorrect log format. Please read --help for usage information";
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "Type:" << type
|
||||
<< "dest:" << dest
|
||||
<< "to:" << to
|
||||
<< "from:" << from;
|
||||
|
||||
Statistics::report( {type, dest, to, from} );
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyInstanse::restart()
|
||||
{
|
||||
qCritical() << "Called because process unexpected finished";
|
||||
m_process->deleteLater();
|
||||
start();
|
||||
}
|
||||
|
||||
void ProxyInstanse::createConfigurationFile()
|
||||
{
|
||||
QFile original (confPath());
|
||||
if (not original.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qFatal("Can not read original cfg");
|
||||
return;
|
||||
}
|
||||
QString originalFile = original.readAll();
|
||||
original.close();
|
||||
|
||||
static QRegularExpression rgx_domainsPattern ("^.*\\{\\{.*\\}\\}.*", QRegularExpression::DotMatchesEverythingOption);
|
||||
if (originalFile.contains(rgx_domainsPattern))
|
||||
{
|
||||
QString rawDomainsList = originalFile;
|
||||
static QRegularExpression rgx_listBegin ("^.*\\{\\{", QRegularExpression::DotMatchesEverythingOption);
|
||||
rawDomainsList.remove(rgx_listBegin);
|
||||
static QRegularExpression rgx_listEnd ("\\}\\}.*$", QRegularExpression::DotMatchesEverythingOption);
|
||||
rawDomainsList.remove(rgx_listEnd);
|
||||
if (rawDomainsList.isEmpty())
|
||||
{
|
||||
qWarning() << "Blocked domains pattern detected, but list is empty in" << confPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
generateBlackList(rawDomainsList.split(','));
|
||||
|
||||
QString blockedDomainsConfigPart;
|
||||
QMapIterator mapIterator (m_blackList);
|
||||
while (mapIterator.hasNext())
|
||||
{
|
||||
auto currentNode = mapIterator.next();
|
||||
blockedDomainsConfigPart += "deny * * " + currentNode.key() + '\n';
|
||||
for (const auto& addr: currentNode.value())
|
||||
{
|
||||
blockedDomainsConfigPart += "deny * * " + addr + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
static QRegularExpression rgx_domains ("\\{\\{.*\\}\\}", QRegularExpression::DotMatchesEverythingOption);
|
||||
originalFile.replace(rgx_domains, blockedDomainsConfigPart);
|
||||
}
|
||||
}
|
||||
|
||||
QFile tmpConfig (workingConfigPath());
|
||||
if (not tmpConfig.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qFatal("Can not write working config");
|
||||
return;
|
||||
}
|
||||
|
||||
auto cfgByteArray = originalFile.toUtf8();
|
||||
int cfgSize = cfgByteArray.size();
|
||||
int writed = tmpConfig.write(cfgByteArray);
|
||||
if (cfgSize != writed)
|
||||
{
|
||||
qCritical() << "Write error:" << writed << "/" << cfgSize;
|
||||
}
|
||||
tmpConfig.close();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QProcess>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
class ProxyInstanse : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProxyInstanse(QObject* parent = nullptr);
|
||||
~ProxyInstanse();
|
||||
void set3proxyBinaryFile(const QString& pathTo3proxyBinFile);
|
||||
void set3proxyConfigFile(const QString& pathTo3proxyConfFile);
|
||||
|
||||
QString binPath() const { return m_binPath; }
|
||||
QString confPath() const { return m_confPath; }
|
||||
QMap<QString, QStringList> blackList() const { return m_blackList; }
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
||||
private slots:
|
||||
void reader();
|
||||
void restart();
|
||||
|
||||
private:
|
||||
void createConfigurationFile();
|
||||
void generateBlackList(const QStringList& domainsOrAddresses);
|
||||
QString workingConfigPath() const;
|
||||
|
||||
QString m_binPath;
|
||||
QString m_confPath;
|
||||
QProcess* m_process = nullptr;
|
||||
QMap<QString, QStringList> m_blackList; // domain, addresses
|
||||
|
||||
QMutex m_readerMtx;
|
||||
QString m_readBuffer;
|
||||
};
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "socketrunnable.h"
|
||||
#include "g.h"
|
||||
#include "webpage.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
constexpr const int getValueBeforeSpaceLIMIT = 200;
|
||||
constexpr const int getValueBeforeBodyLIMIT = 1000;
|
||||
|
||||
SocketRunnable::SocketRunnable(qintptr socketDescriptor, QObject *parent) :
|
||||
SocketRunnableBase(socketDescriptor, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void SocketRunnable::get()
|
||||
{
|
||||
if (urlPath() == "/index.html")
|
||||
{
|
||||
httpDocument().setBody(WebPage::document());
|
||||
return;
|
||||
}
|
||||
|
||||
QString eTag = g::getValue(m_headers, "If-None-Match", g::GetValueType::HttpHeader);
|
||||
|
||||
if (urlPath() == "/main.css")
|
||||
{
|
||||
httpDocument().setBody(QFile(":/html/main.css"), eTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpDocument().setBody(QFile(g::p::WORKING_DIR + "/html" + urlPath()), eTag);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketRunnable::reader()
|
||||
{
|
||||
QString reqType = getValueBeforeSpace();
|
||||
m_urlPath = QByteArray::fromPercentEncoding( getValueBeforeSpace().toUtf8() );
|
||||
if (m_urlPath.contains(".."))
|
||||
{
|
||||
// Possible attempt to explore server directories
|
||||
httpDocument().setCode(HttpDocument::Code::_404);
|
||||
httpDocument().setBodySingleHTMLLabel("What you where looking for is not here", "Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_urlPath == "/")
|
||||
{
|
||||
m_urlPath = "/index.html";
|
||||
}
|
||||
m_headers = getValueBeforeBody();
|
||||
|
||||
if (reqType == "GET")
|
||||
{
|
||||
get();
|
||||
}
|
||||
else if (reqType != "HEAD")
|
||||
{
|
||||
httpDocument().setCode(HttpDocument::Code::_400); // default code is 200
|
||||
}
|
||||
|
||||
setDataToWrite(httpDocument().document());
|
||||
}
|
||||
|
||||
QString SocketRunnable::getValueBeforeSpace()
|
||||
{
|
||||
QString result;
|
||||
char symbol {0};
|
||||
qint64 tail = socket()->read(&symbol, 1);
|
||||
while (symbol != ' ' and tail > 0)
|
||||
{
|
||||
if (result.size() > getValueBeforeSpaceLIMIT)
|
||||
{
|
||||
qDebug() << "Parsing error: TYPE or URL length >" << getValueBeforeSpaceLIMIT;
|
||||
break;
|
||||
}
|
||||
result += symbol;
|
||||
tail = socket()->read(&symbol, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString SocketRunnable::getValueBeforeBody()
|
||||
{
|
||||
QString result;
|
||||
char symbol {0};
|
||||
qint64 tail = socket()->read(&symbol, 1);
|
||||
while (not result.endsWith("\r\n\r") and tail > 0)
|
||||
{
|
||||
if (result.size() > getValueBeforeBodyLIMIT)
|
||||
{
|
||||
qDebug() << "Parsing error: headers length >" << getValueBeforeBodyLIMIT;
|
||||
break;
|
||||
}
|
||||
result += symbol;
|
||||
tail = socket()->read(&symbol, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "socketrunnablebase.h"
|
||||
#include "httpdocument.h"
|
||||
|
||||
class SocketRunnable : public SocketRunnableBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SocketRunnable(qintptr, QObject* parent = nullptr);
|
||||
|
||||
// SocketRunnableBase interface
|
||||
public:
|
||||
void reader() override;
|
||||
|
||||
private:
|
||||
HttpDocument& httpDocument() { return m_httpDocument; }
|
||||
HttpDocument m_httpDocument;
|
||||
|
||||
QString m_urlPath;
|
||||
const QString& urlPath() const { return m_urlPath; }
|
||||
|
||||
QString m_headers;
|
||||
const QString& headers() const { return m_headers; }
|
||||
|
||||
void get();
|
||||
|
||||
QString getValueBeforeSpace();
|
||||
QString getValueBeforeBody();
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "socketrunnablebase.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
SocketRunnableBase::SocketRunnableBase(qintptr socketDescriptor, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_socketDescriptor(socketDescriptor)
|
||||
{
|
||||
}
|
||||
|
||||
SocketRunnableBase::~SocketRunnableBase()
|
||||
{
|
||||
if (m_socket) delete m_socket;
|
||||
}
|
||||
|
||||
void SocketRunnableBase::setDataToWrite(const QByteArray &data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
QTcpSocket *SocketRunnableBase::socket()
|
||||
{
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
void SocketRunnableBase::run()
|
||||
{
|
||||
if (not m_socketDescriptor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_socket = new QTcpSocket;
|
||||
m_socket->setSocketDescriptor(m_socketDescriptor);
|
||||
|
||||
if (m_socket->waitForReadyRead())
|
||||
{
|
||||
reader();
|
||||
|
||||
if (not m_data.isEmpty())
|
||||
{
|
||||
m_socket->write(m_data);
|
||||
while (m_socket->bytesToWrite() > 0)
|
||||
{
|
||||
if (m_socket->state() == QTcpSocket::UnconnectedState)
|
||||
{
|
||||
qWarning() << "Socket turned to unconnected state at writing moment";
|
||||
break;
|
||||
}
|
||||
m_socket->waitForBytesWritten(100);
|
||||
}
|
||||
m_socket->close();
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Data to write is empty";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Socket reading timed out";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "httpdocument.h"
|
||||
|
||||
#include <QRunnable>
|
||||
#include <QTcpSocket>
|
||||
#include <QObject>
|
||||
|
||||
/* 1) override reader(); 2) call setDataToWrite() at and of reader() */
|
||||
|
||||
class SocketRunnableBase : public QObject, public QRunnable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SocketRunnableBase(qintptr, QObject* parent = nullptr);
|
||||
~SocketRunnableBase();
|
||||
void run() override;
|
||||
virtual void reader() = 0;
|
||||
void setDataToWrite(const QByteArray& data);
|
||||
QTcpSocket *socket();
|
||||
|
||||
private:
|
||||
qintptr m_socketDescriptor;
|
||||
QTcpSocket* m_socket = nullptr;
|
||||
QByteArray m_data;
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "statistics.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
QStringList Statistics::m_lastDestinations;
|
||||
qint64 Statistics::m_lastSerializeTimestamp = 0;
|
||||
bool Statistics::m_deserialized = false;
|
||||
DailyCounter Statistics::m_dailyUpload;
|
||||
DailyCounter Statistics::m_dailyDownload;
|
||||
quint64 Statistics::m_totalUpload = 0;
|
||||
quint64 Statistics::m_totalDownload = 0;
|
||||
|
||||
void Statistics::report(const LogEvent& event)
|
||||
{
|
||||
// mutex enabled in ProxyInstanse::reader()
|
||||
|
||||
deserialize();
|
||||
|
||||
if (g::p::IGNORED_DESTINATIONS.contains(event.dest))
|
||||
{
|
||||
qDebug() << "Destination" << event.dest << "ignored";
|
||||
return;
|
||||
}
|
||||
|
||||
addToDestinationsList(event.dest);
|
||||
incrementUpload(event.to);
|
||||
incrementDownload(event.from);
|
||||
// type not handled
|
||||
|
||||
serialize();
|
||||
}
|
||||
|
||||
void Statistics::incrementUpload(quint64 value)
|
||||
{
|
||||
m_dailyUpload.increment(value);
|
||||
m_totalUpload += value;
|
||||
}
|
||||
|
||||
void Statistics::incrementDownload(quint64 value)
|
||||
{
|
||||
m_dailyDownload.increment(value);
|
||||
m_totalDownload += value;
|
||||
}
|
||||
|
||||
void Statistics::addToDestinationsList(const QString &dest)
|
||||
{
|
||||
if (static_cast<uint>(lastDestinations().size()) >= g::p::LAST_AND_TOP_LIST_SIZE)
|
||||
{
|
||||
m_lastDestinations.pop_back();
|
||||
}
|
||||
m_lastDestinations.push_front(dest);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Statistics::serialize()
|
||||
{
|
||||
QFile file(g::p::WORKING_DIR + "/statistics.json");
|
||||
}
|
||||
|
||||
void Statistics::deserialize()
|
||||
{
|
||||
if (m_deserialized) return;
|
||||
|
||||
QFile file(g::p::WORKING_DIR + "/statistics.json");
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dailycounter.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
struct LogEvent
|
||||
{
|
||||
QString type;
|
||||
QString dest;
|
||||
quint64 to = 0;
|
||||
quint64 from = 0;
|
||||
};
|
||||
|
||||
class Statistics
|
||||
{
|
||||
public:
|
||||
Statistics() = delete;
|
||||
static void report (const LogEvent& event);
|
||||
|
||||
static const QStringList lastDestinations() { return QStringList{"google.com"}; /*m_lastDestinations;*/ }
|
||||
static const QStringList dailyTopDestinations() { return QStringList{"cloudflare.com", "yahoo.com"}; }
|
||||
static const QStringList totalTopDestinations() { return QStringList{"domain.com", "top.org"}; }
|
||||
static quint64 dailyUpload() { return 124; }
|
||||
static quint64 totalUpload() { return 241298412; }
|
||||
static quint64 dailyDownload() { return 21312312312412; }
|
||||
static quint64 totalDownload() { return 23112; }
|
||||
|
||||
private:
|
||||
static void incrementUpload(quint64 value);
|
||||
static void incrementDownload(quint64 value);
|
||||
static void addToDestinationsList(const QString& dest);
|
||||
static void serialize();
|
||||
static void deserialize();
|
||||
|
||||
static QStringList m_lastDestinations;
|
||||
|
||||
static qint64 m_lastSerializeTimestamp;
|
||||
static bool m_deserialized;
|
||||
static DailyCounter m_dailyUpload;
|
||||
static DailyCounter m_dailyDownload;
|
||||
static quint64 m_totalUpload;
|
||||
static quint64 m_totalDownload;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QStringList>
|
||||
|
||||
class WebPage
|
||||
{
|
||||
public:
|
||||
WebPage() = delete;
|
||||
static QByteArray document();
|
||||
|
||||
private:
|
||||
static void setTitle(QString& document);
|
||||
static void customStyles(QString& document);
|
||||
static void lastDestinations(QString& document);
|
||||
static void trafficBlock(QString& document);
|
||||
static void topDestinations(QString& document);
|
||||
static void blockedDestinations(QString& document);
|
||||
static void informationBlock(QString& document);
|
||||
static void copyright(QString& document);
|
||||
|
||||
static QString lastDestinationItem(const QString& destination);
|
||||
static QString dailyTopDestinationItem(const QString& destination);
|
||||
static QString totalTopDestinationItem(const QString& destination);
|
||||
static QString blockedDestinationItem(const QString& destination, const QStringList& addresses);
|
||||
static QPair<QString, QString> bytesToHumanReadableString(quint64 bytes); // count, measure
|
||||
|
||||
static const QString m_lastDestinationItem;
|
||||
static const QString m_dailyTopDestinationItem;
|
||||
static const QString m_totalTopDestinationItem;
|
||||
static const QString m_blockedDestinationsBlock;
|
||||
static const QString m_blockedDestinationItemLvl1;
|
||||
static const QString m_blockedDestinationItemLvl2;
|
||||
static const QString m_informationBlock;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "webserverbase.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
WebServerBase::WebServerBase(QObject *parent) :
|
||||
QTcpServer(parent)
|
||||
{
|
||||
m_pool = new QThreadPool(this);
|
||||
auto threadCount = std::thread::hardware_concurrency() * 2;
|
||||
m_pool->setMaxThreadCount(threadCount);
|
||||
|
||||
if (not listen(QHostAddress(g::p::BIND_TO_ADDRESS), g::p::BIND_TO_PORT))
|
||||
{
|
||||
throw std::runtime_error("Web server not binded at " +
|
||||
g::p::BIND_TO_ADDRESS.toStdString() + " / " +
|
||||
QString::number(g::p::BIND_TO_PORT).toStdString());
|
||||
}
|
||||
qInfo() << "Web server started at" << g::p::BIND_TO_ADDRESS << "/" << g::p::BIND_TO_PORT;
|
||||
}
|
||||
|
||||
WebServerBase::~WebServerBase()
|
||||
{
|
||||
if (m_pool)
|
||||
{
|
||||
m_pool->clear();
|
||||
if (not m_pool->waitForDone(100))
|
||||
{
|
||||
qWarning() << "Thread pool of web server not finished at 100ms";
|
||||
}
|
||||
m_pool->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
QThreadPool *WebServerBase::pool()
|
||||
{
|
||||
return m_pool;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpServer>
|
||||
#include <QThreadPool>
|
||||
|
||||
class WebServerBase : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebServerBase (QObject *parent = nullptr);
|
||||
virtual ~WebServerBase();
|
||||
QThreadPool *pool();
|
||||
|
||||
private:
|
||||
QThreadPool *m_pool;
|
||||
};
|
||||
Reference in New Issue
Block a user