TTW_Bot/tauth.cpp

393 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "tauth.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QUrl>
#include <QUrlQuery>
#include <QDesktopServices>
#include <QHostAddress>
#include <QByteArray>
#include <QDebug>
#include <QStringList>
#include <QTimer>
TAuth::TAuth(QObject *parent)
: QObject(parent)
, m_server(nullptr)
, m_clientSocket(nullptr)
, m_serverPort(0)
, m_autoOpenBrowser(true)
{
}
TAuth::~TAuth()
{
stopServer();
}
void TAuth::startServer(const QString &authUrl, bool openBrowser)
{
stopServer();
m_authUrl = authUrl;
m_autoOpenBrowser = openBrowser;
m_server = new QTcpServer(this);
// Пытаемся слушать на портах, начиная с 8080
QVector<int> portsToTry = {8089};
for (int port : portsToTry) {
if (m_server->listen(QHostAddress::LocalHost, port)) {
m_serverPort = port;
emit serverStarted(port);
break;
}
}
if (!m_server->isListening()) {
emit errorOccurred("Не удалось запустить сервер на доступных портах");
delete m_server;
m_server = nullptr;
return;
}
connect(m_server, &QTcpServer::newConnection,
this, &TAuth::handleNewConnection);
// Открываем браузер только если указано
if (openBrowser && !authUrl.isEmpty()) {
QDesktopServices::openUrl(QUrl(authUrl));
}
}
void TAuth::stopServer()
{
if (m_server) {
m_server->close();
m_server->deleteLater();
m_server = nullptr;
}
if (m_clientSocket) {
m_clientSocket->disconnect();
m_clientSocket->close();
m_clientSocket->deleteLater();
m_clientSocket = nullptr;
}
m_serverPort = 0;
}
void TAuth::handleNewConnection()
{
if (!m_server) return;
m_clientSocket = m_server->nextPendingConnection();
if (m_clientSocket) {
connect(m_clientSocket, &QTcpSocket::readyRead,
this, &TAuth::readClientData);
connect(m_clientSocket, &QTcpSocket::disconnected,
this, [this]() {
// Удаляем сокет после отключения
if (m_clientSocket) {
m_clientSocket->deleteLater();
m_clientSocket = nullptr;
}
});
}
}
void TAuth::readClientData()
{
if (!m_clientSocket || !m_clientSocket->bytesAvailable()) return;
QByteArray requestData = m_clientSocket->readAll();
QString request = QString::fromUtf8(requestData);
QStringList lines = request.split("\r\n");
if (lines.isEmpty()) return;
QString requestLine = lines[0];
QStringList parts = requestLine.split(" ");
if (parts.size() >= 2 && parts[0].toUpper() == "GET") {
QString document = parts[1];
// Проверяем, есть ли в запросе параметры (они могут быть в теле запроса)
// или в реферере
QString referer;
for (const QString &line : lines) {
if (line.startsWith("Referer:", Qt::CaseInsensitive)) {
referer = line.mid(9).trimmed();
break;
}
}
// Обрабатываем обычный redirect
if (document.startsWith("/redirect")) {
handleRedirectRequest(document, m_clientSocket);
}
// Обрабатываем корневой запрос
else if (document == "/" || document == "/?") {
// Проверяем, есть ли хэш в URL
QString fullUrl = document;
if (!referer.isEmpty() && referer.contains("#")) {
// Извлекаем хэш из реферера
QString hash = referer.mid(referer.indexOf("#") + 1);
fullUrl = "/redirect?" + hash;
handleRedirectRequest(fullUrl, m_clientSocket);
} else {
// Или проверяем, есть ли параметры в самом запросе
// (хэш не передается, но могут быть другие параметры)
QString params;
if (document.contains("#")) {
params = document.mid(document.indexOf("#") + 1);
fullUrl = "/redirect?" + params;
handleRedirectRequest(fullUrl, m_clientSocket);
} else {
handleRootRequest(m_clientSocket);
}
}
}
else if (document.startsWith("/da")) {
QString html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Redirecting...</title>\n</head>\n<body>\n"
" <p>получаю код</p>\n<script>\n"
"var paragraph = window.location.href;\n"
"var urrl = paragraph.replace('localhost:8089/da','localhost:8089/redirect');\n"
"urrl = urrl.replace('#','?');\n"
"console.log(urrl);\n"
"window.location.href = urrl;\n"
"</script>\n</body>\n</html>";
sendResponse(m_clientSocket, html);
}
else {
// Показываем страницу с JavaScript для извлечения хэша
QString html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Processing...</title>\n</head>\n<body>\n"
" <p>Обработка авторизации...</p>\n<script>\n"
"try {\n"
" // Проверяем, есть ли хэш в URL\n"
" var hash = window.location.hash;\n"
" if (hash) {\n"
" // Преобразуем # в ? для передачи на сервер\n"
" var params = hash.substring(1); // убираем #\n"
" // Перенаправляем на /redirect с параметрами\n"
" window.location.href = '/redirect?' + params;\n"
" } else {\n"
" document.body.innerHTML = '<h2>Нет данных авторизации</h2>';\n"
" }\n"
"} catch(e) {\n"
" document.body.innerHTML = '<h2>Ошибка: ' + e.message + '</h2>';\n"
"}\n"
"</script>\n</body>\n</html>";
sendResponse(m_clientSocket, html);
}
} else {
sendErrorResponse(m_clientSocket, "Invalid request method");
}
}
void TAuth::handleRootRequest(QTcpSocket *socket)
{
QString html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Processing...</title>\n</head>\n<body>\n"
" <p>Обработка авторизации...</p>\n<script>\n"
"try {\n"
" var hash = window.location.hash;\n"
" if (hash) {\n"
" var params = hash.substring(1);\n"
" window.location.href = '/redirect?' + params;\n"
" } else {\n"
" document.body.innerHTML = '<h2>Нет данных авторизации в URL</h2>';\n"
" }\n"
"} catch(e) {\n"
" document.body.innerHTML = '<h2>Ошибка: ' + e.message + '</h2>';\n"
"}\n"
"</script>\n</body>\n</html>";
sendResponse(socket, html);
}
void TAuth::handleRedirectRequest(const QString &request, QTcpSocket *socket)
{
QString params;
if (request.contains('?')) {
params = request.mid(request.indexOf('?') + 1);
}
QString html;
QString token, code, expiresIn;
// Проверяем наличие access_token
if (params.contains("access_token=")) {
token = extractParam(params, "access_token");
expiresIn = extractExpiresIn(params);
// Формируем полный токен с временем жизни (если есть)
QString fullToken = token;
if (!expiresIn.isEmpty()) {
fullToken += "|" + expiresIn;
}
emit tokenReceived(fullToken);
html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Success</title>\n</head>\n<body>\n"
"<h2>Authorization Successful!</h2>\n"
"<p>Token received. You can close this window.</p>\n"
"</body>\n</html>";
// Отправляем ответ клиенту
sendResponse(socket, html);
// Останавливаем сервер СРАЗУ
QTimer::singleShot(100, this, &TAuth::stopServer);
return;
}
// Проверяем наличие error_description
else if (params.contains("error_description=")) {
QString error = extractErrorDescription(params);
emit errorOccurred(error);
html =
QString("<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Error</title>\n</head>\n<body>\n"
"<h2>Authorization Failed</h2>\n"
"<p>Error: %1</p>\n</body>\n</html>").arg(error);
}
// Проверяем наличие code (для OAuth code flow)
else if (params.contains("code=")) {
code = extractParam(params, "code");
emit codeReceived(code);
html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>Success</title>\n</head>\n<body>\n"
"<h2>Authorization Successful!</h2>\n"
"<p>Code received. You can close this window.</p>\n"
"</body>\n</html>";
// Отправляем ответ клиенту
sendResponse(socket, html);
// Останавливаем сервер СРАЗУ
QTimer::singleShot(100, this, &TAuth::stopServer);
return;
}
else {
html =
"<!DOCTYPE html>\n<html>\n<head>\n"
" <title>No Data</title>\n</head>\n<body>\n"
"<h2>No authorization data received</h2>\n"
"<p>Try again or check your authorization URL.</p>\n"
"</body>\n</html>";
sendResponse(socket, html);
// Останавливаем сервер через 5 секунд если нет данных
QTimer::singleShot(5000, this, &TAuth::stopServer);
return;
}
sendResponse(socket, html);
// Останавливаем сервер через 5 секунд для других случаев
QTimer::singleShot(5000, this, &TAuth::stopServer);
}
QString TAuth::extractParam(const QString &params, const QString &paramName)
{
QStringList paramList = params.split('&');
for (const QString &param : paramList) {
if (param.startsWith(paramName + "=")) {
QString value = param.mid(paramName.length() + 1);
// Декодируем URL-encoded значения
value = QUrl::fromPercentEncoding(value.toUtf8());
return value;
}
}
return QString();
}
QString TAuth::extractExpiresIn(const QString &params)
{
return extractParam(params, "expires_in");
}
void TAuth::extractAndEmitToken(const QString &params)
{
QString token = extractParam(params, "access_token");
QString expiresIn = extractExpiresIn(params);
if (!token.isEmpty()) {
QString fullToken = token;
if (!expiresIn.isEmpty()) {
fullToken += "|" + expiresIn;
}
emit tokenReceived(fullToken);
}
}
void TAuth::extractAndEmitError(const QString &params)
{
QString error = extractErrorDescription(params);
if (!error.isEmpty()) {
emit errorOccurred(error);
}
}
void TAuth::extractAndEmitCode(const QString &params)
{
QString code = extractParam(params, "code");
if (!code.isEmpty()) {
emit codeReceived(code);
}
}
QString TAuth::extractErrorDescription(const QString &params)
{
QString error = extractParam(params, "error_description");
if (error.isEmpty()) {
error = extractParam(params, "error");
}
if (error.isEmpty()) {
error = "Unknown error";
}
return error;
}
void TAuth::sendResponse(QTcpSocket *socket, const QString &content)
{
if (!socket) return;
QString response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Content-Length: " + QString::number(content.toUtf8().size()) + "\r\n"
"Connection: close\r\n"
"\r\n" + content;
socket->write(response.toUtf8());
socket->flush();
}
void TAuth::sendErrorResponse(QTcpSocket *socket, const QString &error)
{
if (!socket) return;
QString content = "<html><body><h1>" + error + "</h1></body></html>";
QString response =
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Content-Length: " + QString::number(content.toUtf8().size()) + "\r\n"
"Connection: close\r\n"
"\r\n" + content;
socket->write(response.toUtf8());
socket->flush();
}