|
43 | 43 | #include <QQmlContext> |
44 | 44 | #include <QWindow> |
45 | 45 | #include <QGuiApplication> |
| 46 | +#include <QClipboard> |
46 | 47 | #endif |
| 48 | +#include <QUrl> |
| 49 | +#include <QUrlQuery> |
| 50 | +#include <QString> |
| 51 | +#include <QStringList> |
47 | 52 | #include <QHostAddress> |
48 | 53 | #include <QNetworkAccessManager> |
49 | 54 | #include <QNetworkReply> |
@@ -957,6 +962,7 @@ namespace { |
957 | 962 | QNetworkRequest request(subUrl); |
958 | 963 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, |
959 | 964 | QNetworkRequest::NoLessSafeRedirectPolicy); |
| 965 | + request.setMaximumRedirectsAllowed(3); |
960 | 966 | manager.get(request); |
961 | 967 | } |
962 | 968 | } |
@@ -1066,6 +1072,7 @@ void ImageWriter::handleNetworkRequestFinished(QNetworkReply *data) { |
1066 | 1072 |
|
1067 | 1073 | auto request = QNetworkRequest(redirUrl); |
1068 | 1074 | request.setAttribute(QNetworkRequest::RedirectionTargetAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); |
| 1075 | + request.setMaximumRedirectsAllowed(3); |
1069 | 1076 | data->manager()->get(request); |
1070 | 1077 |
|
1071 | 1078 | // maintain manager |
@@ -1202,6 +1209,7 @@ void ImageWriter::beginOSListFetch() { |
1202 | 1209 | QNetworkRequest request = QNetworkRequest(topUrl); |
1203 | 1210 | request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, |
1204 | 1211 | QNetworkRequest::NoLessSafeRedirectPolicy); |
| 1212 | + request.setMaximumRedirectsAllowed(3); |
1205 | 1213 | // This will set up a chain of requests that culiminate in the eventual fetch and assembly of |
1206 | 1214 | // a complete cached OS list. |
1207 | 1215 | _networkManager.get(request); |
@@ -2547,18 +2555,87 @@ void ImageWriter::openUrl(const QUrl &url) |
2547 | 2555 | } |
2548 | 2556 | } |
2549 | 2557 |
|
| 2558 | +bool ImageWriter::verifyAuthKey(const QString &s, bool strict) const |
| 2559 | +{ |
| 2560 | + // Base58 (no 0 O I l) |
| 2561 | + static const QRegularExpression base58OnlyRe(QStringLiteral("^[1-9A-HJ-NP-Za-km-z]+$")); |
| 2562 | + |
| 2563 | + // Required prefix |
| 2564 | + bool hasPrefix = s.startsWith(QStringLiteral("rpuak_")) || s.startsWith(QStringLiteral("rpoak_")); |
| 2565 | + if (!hasPrefix) |
| 2566 | + return false; |
| 2567 | + |
| 2568 | + const QString payload = s.mid(6); |
| 2569 | + bool base58Match = base58OnlyRe.match(payload).hasMatch(); |
| 2570 | + |
| 2571 | + if (payload.isEmpty() || !base58Match) |
| 2572 | + return false; |
| 2573 | + |
| 2574 | + if (strict) { |
| 2575 | + // Exactly 24 Base58 chars today → total length 30 |
| 2576 | + return payload.size() == 24; |
| 2577 | + } else { |
| 2578 | + // Future-proof: accept >=24 Base58 chars |
| 2579 | + return payload.size() >= 24; |
| 2580 | + } |
| 2581 | +} |
| 2582 | + |
| 2583 | +QString ImageWriter::parseTokenFromUrl(const QUrl &url, bool strictAuthKey) const { |
| 2584 | + // Handle QUrl or string, accept auth_key |
| 2585 | + if (!url.isValid()) |
| 2586 | + return {}; |
| 2587 | + |
| 2588 | + QUrlQuery q(url); |
| 2589 | + const QString val = q.queryItemValue(QStringLiteral("auth_key"), QUrl::FullyDecoded); |
| 2590 | + if (!val.isEmpty()) { |
| 2591 | + if (verifyAuthKey(val, strictAuthKey)) { |
| 2592 | + return val; |
| 2593 | + } |
| 2594 | + |
| 2595 | + qWarning() << "Ignoring auth_key with invalid format/length:" << val; |
| 2596 | + } |
| 2597 | + |
| 2598 | + return {}; |
| 2599 | +} |
| 2600 | + |
2550 | 2601 | void ImageWriter::handleIncomingUrl(const QUrl &url) |
2551 | 2602 | { |
2552 | 2603 | qDebug() << "Incoming URL:" << url; |
2553 | | - emit connectCallbackReceived(QVariant::fromValue(url)); |
| 2604 | + |
| 2605 | + auto token = parseTokenFromUrl(url); |
| 2606 | + if (!token.isEmpty()) { |
| 2607 | + if (!_piConnectToken.isEmpty()) { |
| 2608 | + if (_piConnectToken != token) { |
| 2609 | + // Let QML decide whether to overwrite |
| 2610 | + emit connectTokenConflictDetected(token); |
| 2611 | + } |
| 2612 | + |
| 2613 | + return; |
| 2614 | + } |
| 2615 | + |
| 2616 | + overwriteConnectToken(token); |
| 2617 | + } |
2554 | 2618 | } |
2555 | 2619 |
|
2556 | | -void ImageWriter::setRuntimeConnectToken(const QString &token) |
| 2620 | +void ImageWriter::overwriteConnectToken(const QString &token) |
2557 | 2621 | { |
| 2622 | + // Ephemeral session-only Connect token (never persisted) |
2558 | 2623 | _piConnectToken = token; |
| 2624 | + emit connectTokenReceived(token); |
2559 | 2625 | } |
2560 | 2626 |
|
2561 | 2627 | QString ImageWriter::getRuntimeConnectToken() const |
2562 | 2628 | { |
2563 | 2629 | return _piConnectToken; |
2564 | 2630 | } |
| 2631 | + |
| 2632 | +QString ImageWriter::getClipboardText() const |
| 2633 | +{ |
| 2634 | +#ifndef CLI_ONLY_BUILD |
| 2635 | + QClipboard *clipboard = QGuiApplication::clipboard(); |
| 2636 | + if (clipboard) { |
| 2637 | + return clipboard->text(); |
| 2638 | + } |
| 2639 | +#endif |
| 2640 | + return QString(); |
| 2641 | +} |
0 commit comments