first commit
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
#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,
|
||||
m_clientSocket, &QTcpSocket::deleteLater);
|
||||
}
|
||||
}
|
||||
|
||||
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>";
|
||||
}
|
||||
// Проверяем наличие 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>";
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse(socket, html);
|
||||
|
||||
// Останавливаем сервер после обработки
|
||||
QTimer::singleShot(1000, this, &TAuth::stopServer);
|
||||
}
|
||||
|
||||
QString TAuth::extractParam(const QString ¶ms, const QString ¶mName)
|
||||
{
|
||||
QStringList paramList = params.split('&');
|
||||
for (const QString ¶m : 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 ¶ms)
|
||||
{
|
||||
return extractParam(params, "expires_in");
|
||||
}
|
||||
|
||||
void TAuth::extractAndEmitToken(const QString ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
QString error = extractErrorDescription(params);
|
||||
if (!error.isEmpty()) {
|
||||
emit errorOccurred(error);
|
||||
}
|
||||
}
|
||||
|
||||
void TAuth::extractAndEmitCode(const QString ¶ms)
|
||||
{
|
||||
QString code = extractParam(params, "code");
|
||||
if (!code.isEmpty()) {
|
||||
emit codeReceived(code);
|
||||
}
|
||||
}
|
||||
|
||||
QString TAuth::extractErrorDescription(const QString ¶ms)
|
||||
{
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user