Skip to content

Commit d2e92b5

Browse files
authored
Merge branch 'main' into burn-qml-import-version-numbers
2 parents 5ae803f + ff9e58e commit d2e92b5

18 files changed

+858
-113
lines changed

qt/features_exclude.embedded.list

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ assistant
44
batch_test_support
55
bluetooth
66
calendarwidget
7-
clipboard
87
completer
98
concatenatetablesproxymodel
109
designer

qt/features_exclude.list

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ assistant
44
batch_test_support
55
bluetooth
66
calendarwidget
7-
clipboard
87
completer
98
concatenatetablesproxymodel
109
designer

qt/features_exclude.macos.list

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ assistant
88
batch_test_support
99
bluetooth
1010
calendarwidget
11-
clipboard
1211
completer
1312
concatenatetablesproxymodel
1413
designer

src/CMakeLists.txt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
3939
set(CMAKE_CXX_STANDARD 20)
4040
set(CMAKE_CXX_STANDARD_REQUIRED ON)
4141

42+
# One place to define the Windows callback IPC port
43+
set(IMAGER_CALLBACK_PORT "49629" CACHE STRING "TCP port for rpi-imager callback relay on Windows")
44+
4245
# Apply optimization flags globally to all targets (including bundled dependencies)
4346
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
4447
# Use -Os instead of -O3 for better code size vs performance balance
@@ -232,11 +235,11 @@ if(BUILD_CLI_ONLY)
232235
find_package(Qt6 6.9 COMPONENTS Core Network OPTIONAL_COMPONENTS LinguistTools)
233236
endif()
234237
else()
235-
# Regular GUI build: need Core, Quick, Svg, Network - DBus only needed on Linux
238+
# Regular GUI build: need Core, Quick, Svg, Network, Gui - DBus only needed on Linux
236239
if(UNIX AND NOT APPLE)
237-
find_package(Qt6 6.9 COMPONENTS Core Quick Svg Network OPTIONAL_COMPONENTS LinguistTools DBus)
240+
find_package(Qt6 6.9 COMPONENTS Core Gui Quick Svg Network OPTIONAL_COMPONENTS LinguistTools DBus)
238241
else()
239-
find_package(Qt6 6.9 COMPONENTS Core Quick Svg Network OPTIONAL_COMPONENTS LinguistTools)
242+
find_package(Qt6 6.9 COMPONENTS Core Gui Quick Svg Network OPTIONAL_COMPONENTS LinguistTools)
240243
endif()
241244
endif()
242245
if (Qt6_FOUND)
@@ -371,6 +374,13 @@ endif()
371374
if (WIN32)
372375
# Adding WIN32 prevents a console window being opened on Windows
373376
add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${DEPENDENCIES})
377+
378+
# Inject the callback port macro for Windows builds
379+
target_compile_definitions(${PROJECT_NAME}
380+
PRIVATE RPI_IMAGER_CALLBACK_PORT=${IMAGER_CALLBACK_PORT})
381+
382+
# Ensure the relay builds with the app
383+
add_dependencies(${PROJECT_NAME} rpi-imager-callback-relay)
374384
else()
375385
add_executable(${PROJECT_NAME} ${SOURCES} ${DEPENDENCIES})
376386
endif()
@@ -481,6 +491,7 @@ if(NOT BUILD_CLI_ONLY)
481491
hwlistmodel.cpp
482492
oslistmodel.cpp
483493
urlfmt.cpp
494+
clipboardhelper.cpp
484495
)
485496
endif()
486497

@@ -517,7 +528,7 @@ if(BUILD_CLI_ONLY)
517528
target_link_libraries(${PROJECT_NAME} PRIVATE ${QT}::Core ${QT}::Network ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ZSTD_LIBRARIES} ${LIBLZMA_LIBRARIES} ${ZLIB_LIBRARIES} ${YESCRYPT_LIBRARIES} ${LIBDRM_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
518529
else()
519530
# Regular GUI build: link all GUI components
520-
target_link_libraries(${PROJECT_NAME} PRIVATE ${QT}::Core ${QT}::Quick ${QT}::Svg ${QT}::Network ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ZSTD_LIBRARIES} ${LIBLZMA_LIBRARIES} ${ZLIB_LIBRARIES} ${YESCRYPT_LIBRARIES} ${LIBDRM_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
531+
target_link_libraries(${PROJECT_NAME} PRIVATE ${QT}::Core ${QT}::Gui ${QT}::Quick ${QT}::Svg ${QT}::Network ${CURL_LIBRARIES} ${LibArchive_LIBRARIES} ${ZSTD_LIBRARIES} ${LIBLZMA_LIBRARIES} ${ZLIB_LIBRARIES} ${YESCRYPT_LIBRARIES} ${LIBDRM_LIBRARIES} ${ATOMIC_LIBRARY} ${EXTRALIBS})
521532
endif()
522533

523534
# Testing

src/clipboardhelper.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright (C) 2025 Raspberry Pi Ltd
4+
*/
5+
6+
#include "clipboardhelper.h"
7+
#include <QGuiApplication>
8+
#include <QClipboard>
9+
#include <QDebug>
10+
11+
ClipboardHelper::ClipboardHelper(QObject *parent)
12+
: QObject(parent)
13+
{
14+
// Connect to clipboard changes to emit our signal
15+
QClipboard *clipboard = QGuiApplication::clipboard();
16+
if (clipboard) {
17+
connect(clipboard, &QClipboard::dataChanged, this, &ClipboardHelper::clipboardChanged);
18+
}
19+
}
20+
21+
void ClipboardHelper::setText(const QString &text)
22+
{
23+
QClipboard *clipboard = QGuiApplication::clipboard();
24+
if (clipboard) {
25+
clipboard->setText(text, QClipboard::Clipboard);
26+
qDebug() << "ClipboardHelper: Set clipboard text:" << text;
27+
} else {
28+
qWarning() << "ClipboardHelper: Failed to access clipboard";
29+
}
30+
}
31+
32+
QString ClipboardHelper::getText() const
33+
{
34+
// Use the static method as shown in Qt documentation
35+
QClipboard *clipboard = QGuiApplication::clipboard();
36+
if (clipboard) {
37+
QString text = clipboard->text(QClipboard::Clipboard);
38+
qDebug() << "ClipboardHelper: Got clipboard text:" << text;
39+
return text;
40+
} else {
41+
qWarning() << "ClipboardHelper: Failed to access clipboard";
42+
return QString();
43+
}
44+
}
45+
46+
bool ClipboardHelper::hasText() const
47+
{
48+
// Use the static method as shown in Qt documentation
49+
QClipboard *clipboard = QGuiApplication::clipboard();
50+
if (clipboard) {
51+
return !clipboard->text(QClipboard::Clipboard).isEmpty();
52+
}
53+
return false;
54+
}
55+

src/clipboardhelper.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef CLIPBOARDHELPER_H
2+
#define CLIPBOARDHELPER_H
3+
4+
/*
5+
* SPDX-License-Identifier: Apache-2.0
6+
* Copyright (C) 2025 Raspberry Pi Ltd
7+
*/
8+
9+
#include <QObject>
10+
#include <QString>
11+
#include <QQmlEngine>
12+
13+
class ClipboardHelper : public QObject
14+
{
15+
Q_OBJECT
16+
QML_ELEMENT
17+
QML_SINGLETON
18+
Q_PROPERTY(bool hasText READ hasText NOTIFY clipboardChanged)
19+
20+
public:
21+
explicit ClipboardHelper(QObject *parent = nullptr);
22+
23+
// Clipboard operations
24+
Q_INVOKABLE void setText(const QString &text);
25+
Q_INVOKABLE QString getText() const;
26+
bool hasText() const;
27+
28+
signals:
29+
void clipboardChanged();
30+
};
31+
32+
#endif // CLIPBOARDHELPER_H
33+

src/imagewriter.cpp

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@
4343
#include <QQmlContext>
4444
#include <QWindow>
4545
#include <QGuiApplication>
46+
#include <QClipboard>
4647
#endif
48+
#include <QUrl>
49+
#include <QUrlQuery>
50+
#include <QString>
51+
#include <QStringList>
4752
#include <QHostAddress>
4853
#include <QNetworkAccessManager>
4954
#include <QNetworkReply>
@@ -957,6 +962,7 @@ namespace {
957962
QNetworkRequest request(subUrl);
958963
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
959964
QNetworkRequest::NoLessSafeRedirectPolicy);
965+
request.setMaximumRedirectsAllowed(3);
960966
manager.get(request);
961967
}
962968
}
@@ -1066,6 +1072,7 @@ void ImageWriter::handleNetworkRequestFinished(QNetworkReply *data) {
10661072

10671073
auto request = QNetworkRequest(redirUrl);
10681074
request.setAttribute(QNetworkRequest::RedirectionTargetAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
1075+
request.setMaximumRedirectsAllowed(3);
10691076
data->manager()->get(request);
10701077

10711078
// maintain manager
@@ -1202,6 +1209,7 @@ void ImageWriter::beginOSListFetch() {
12021209
QNetworkRequest request = QNetworkRequest(topUrl);
12031210
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
12041211
QNetworkRequest::NoLessSafeRedirectPolicy);
1212+
request.setMaximumRedirectsAllowed(3);
12051213
// This will set up a chain of requests that culiminate in the eventual fetch and assembly of
12061214
// a complete cached OS list.
12071215
_networkManager.get(request);
@@ -2547,18 +2555,87 @@ void ImageWriter::openUrl(const QUrl &url)
25472555
}
25482556
}
25492557

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+
25502601
void ImageWriter::handleIncomingUrl(const QUrl &url)
25512602
{
25522603
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+
}
25542618
}
25552619

2556-
void ImageWriter::setRuntimeConnectToken(const QString &token)
2620+
void ImageWriter::overwriteConnectToken(const QString &token)
25572621
{
2622+
// Ephemeral session-only Connect token (never persisted)
25582623
_piConnectToken = token;
2624+
emit connectTokenReceived(token);
25592625
}
25602626

25612627
QString ImageWriter::getRuntimeConnectToken() const
25622628
{
25632629
return _piConnectToken;
25642630
}
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+
}

src/imagewriter.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,10 @@ class ImageWriter : public QObject
267267
Q_INVOKABLE void reboot();
268268
Q_INVOKABLE void openUrl(const QUrl &url);
269269
Q_INVOKABLE void handleIncomingUrl(const QUrl &url);
270-
// Ephemeral session-only Connect token (never persisted)
271-
Q_INVOKABLE void setRuntimeConnectToken(const QString &token);
270+
Q_INVOKABLE void overwriteConnectToken(const QString &token);
272271
Q_INVOKABLE QString getRuntimeConnectToken() const;
272+
Q_INVOKABLE QString getClipboardText() const;
273+
Q_INVOKABLE bool verifyAuthKey(const QString &token, bool strict = false) const;
273274

274275
/* Override OS list refresh schedule (in minutes); pass negative to clear override */
275276
Q_INVOKABLE void setOsListRefreshOverride(int intervalMinutes, int jitterMinutes);
@@ -299,7 +300,8 @@ class ImageWriter : public QObject
299300
void keychainPermissionRequested();
300301
void keychainPermissionResponseReceived();
301302
void writeStateChanged();
302-
void connectCallbackReceived(QVariant url);
303+
void connectTokenReceived(const QString &token);
304+
void connectTokenConflictDetected(const QString &token);
303305
void cacheStatusChanged();
304306

305307
protected slots:
@@ -339,6 +341,8 @@ protected slots:
339341
bool _deviceFilterIsInclusive;
340342
std::shared_ptr<DeviceInfo> _device_info;
341343

344+
QString parseTokenFromUrl(const QUrl &url, bool strictAuthKey = false) const;
345+
342346
protected:
343347
QUrl _src, _repo;
344348
QString _dst, _parentCategory, _osName, _osReleaseDate, _currentLang, _currentLangcode, _currentKeyboard;

0 commit comments

Comments
 (0)