local ideas to git

This commit is contained in:
acetone
2022-11-18 20:38:36 +03:00
parent 85431eb94c
commit cc19774a57
25 changed files with 2032 additions and 0 deletions
+33
View File
@@ -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
+86
View File
@@ -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);
}
+40
View File
@@ -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};
};
+212
View File
@@ -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
+80
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>html/index.html</file>
<file>html/main.css</file>
</qresource>
</RCC>
+10
View File
@@ -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

+121
View File
@@ -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>
+1
View File
@@ -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}
+177
View File
@@ -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;
}
+87
View File
@@ -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;
};
+43
View File
@@ -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);
}
+33
View File
@@ -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
View File
@@ -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();
}
+237
View File
@@ -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();
}
+59
View File
@@ -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;
};
+118
View File
@@ -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;
}
+49
View File
@@ -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();
};
+82
View File
@@ -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";
}
}
+45
View File
@@ -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;
};
+92
View File
@@ -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");
}
+64
View File
@@ -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;
};
+55
View File
@@ -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;
};
+57
View File
@@ -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;
}
+36
View File
@@ -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;
};