#include "tauth.h" #include #include #include #include #include #include #include #include #include #include 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 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 = "\n\n\n" " Redirecting...\n\n\n" "

получаю код

\n\n\n"; sendResponse(m_clientSocket, html); } else { // Показываем страницу с JavaScript для извлечения хэша QString html = "\n\n\n" " Processing...\n\n\n" "

Обработка авторизации...

\n\n\n"; sendResponse(m_clientSocket, html); } } else { sendErrorResponse(m_clientSocket, "Invalid request method"); } } void TAuth::handleRootRequest(QTcpSocket *socket) { QString html = "\n\n\n" " Processing...\n\n\n" "

Обработка авторизации...

\n\n\n"; 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 = "\n\n\n" " Success\n\n\n" "

Authorization Successful!

\n" "

Token received. You can close this window.

\n" "\n"; // Отправляем ответ клиенту 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("\n\n\n" " Error\n\n\n" "

Authorization Failed

\n" "

Error: %1

\n\n").arg(error); } // Проверяем наличие code (для OAuth code flow) else if (params.contains("code=")) { code = extractParam(params, "code"); emit codeReceived(code); html = "\n\n\n" " Success\n\n\n" "

Authorization Successful!

\n" "

Code received. You can close this window.

\n" "\n"; // Отправляем ответ клиенту sendResponse(socket, html); // Останавливаем сервер СРАЗУ QTimer::singleShot(100, this, &TAuth::stopServer); return; } else { html = "\n\n\n" " No Data\n\n\n" "

No authorization data received

\n" "

Try again or check your authorization URL.

\n" "\n"; 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 ¶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 = "

" + error + "

"; 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(); }