| 247 | } |
| 248 | |
| 249 | void OAuthService::onNewConnection() |
| 250 | { |
| 251 | if (!m_server) return; |
| 252 | |
| 253 | QTcpSocket *socket = m_server->nextPendingConnection(); |
| 254 | if (!socket) return; |
| 255 | |
| 256 | // Use QPointer to safely check if objects are still alive |
| 257 | QPointer<OAuthService> self(this); |
| 258 | QPointer<QTcpSocket> socketGuard(socket); |
| 259 | |
| 260 | // Timeout for incomplete requests |
| 261 | QTimer::singleShot(30000, this, [self, socketGuard]() { |
| 262 | if (!self) return; // OAuthService was destroyed |
| 263 | if (socketGuard && socketGuard->state() == QAbstractSocket::ConnectedState) { |
| 264 | socketGuard->disconnectFromHost(); |
| 265 | } |
| 266 | }); |
| 267 | |
| 268 | auto recvBuf = std::make_shared<QByteArray>(); |
| 269 | connect(socket, &QTcpSocket::readyRead, this, [this, socket, recvBuf, self]() { |
| 270 | if (!self) return; // OAuthService was destroyed |
| 271 | |
| 272 | *recvBuf += socket->readAll(); |
| 273 | if (!recvBuf->contains("\r\n\r\n")) return; // wait for complete request |
| 274 | |
| 275 | // Disconnect signal to prevent re-entry |
| 276 | disconnect(socket, &QTcpSocket::readyRead, this, nullptr); |
| 277 | |
| 278 | QString request = QString::fromUtf8(*recvBuf); |
| 279 | |
| 280 | // Expected: "GET /callback?code=XXX&state=YYY HTTP/1.1" (Google) |
| 281 | // Or: "GET /?code=XXX HTTP/1.1" (OneDrive via rclone) |
| 282 | int getEnd = request.indexOf(" HTTP/"); |
| 283 | if (getEnd < 0) { |
| 284 | socket->close(); |
| 285 | socket->deleteLater(); |
| 286 | return; |
| 287 | } |
| 288 | QString path = request.mid(4, getEnd - 4); |
| 289 | qDebug() << "[OAuth] Received request:" << path; |
| 290 | |
| 291 | // Ignore non-callback requests (favicon, preflight, etc.) |
| 292 | if (!path.startsWith("/callback") && !path.startsWith("/?")) { |
| 293 | qDebug() << "[OAuth] Ignoring non-callback request"; |
| 294 | socket->write("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n"); |
| 295 | socket->flush(); |
| 296 | socket->close(); |
| 297 | socket->deleteLater(); |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | QUrl url("http://localhost" + path); |
| 302 | QUrlQuery query(url.query()); |
| 303 | QString code = query.queryItemValue("code"); |
| 304 | QString state = query.queryItemValue("state"); |
| 305 | QString error = query.queryItemValue("error"); |
| 306 |
nothing calls this directly
no outgoing calls
no test coverage detected