Skip to content

Commit 908b29a

Browse files
committed
enable QtKeychain with Wasm
1 parent ab4bfdb commit 908b29a

11 files changed

Lines changed: 498 additions & 155 deletions

File tree

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ RUN apt-get update && \
1717
libglib2.0-0 \
1818
libxkbcommon-dev \
1919
ninja-build \
20+
pkg-config \
2021
python3 \
2122
python3-pip \
2223
unzip \
@@ -42,7 +43,7 @@ RUN ./emsdk activate 4.0.7 && \
4243
qt-cmake -S /app -B /build \
4344
-DQT_HOST_PATH=$QTSDK_DIR/6.10.3/gcc_64 \
4445
-DCMAKE_BUILD_TYPE=Release \
45-
-DWITH_OPENSSL=OFF -DWITH_TESTS=OFF -DWITH_WEBSOCKET=ON -DWITH_UPDATER=OFF -DPACKAGE_TYPE=Wasm && \
46+
-DWITH_OPENSSL=OFF -DWITH_TESTS=OFF -DWITH_WEBSOCKET=ON -DWITH_UPDATER=OFF -DWITH_QTKEYCHAIN=ON -DPACKAGE_TYPE=Wasm && \
4647
ACTUAL_JOBS=${JOBS:-$(nproc)} && \
4748
cmake --build /build --parallel ${ACTUAL_JOBS} && \
4849
cmake --install /build --prefix /dist && \

external/qtkeychain/CMakeLists.txt

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
if(QTKEYCHAIN_FOUND)
22
message(STATUS "QtKeychain found: ${QTKEYCHAIN_LIBRARIES} ${QTKEYCHAIN_INCLUDE_DIRS}")
33
else()
4-
if(WIN32 OR APPLE)
4+
if(WIN32 OR APPLE OR EMSCRIPTEN)
55
message(STATUS "Could not find system QtKeychain; building from local source tree")
66
else()
77
message(FATAL_ERROR "QtKeychain NOT found: use `-DWITH_QTKEYCHAIN=OFF` to build without account autologin")
88
endif()
9+
910
set(QTKEYCHAIN_LIBRARY_NAME qt6keychain)
1011
if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
1112
set(QTKEYCHAIN_LIBRARY_NAME "${QTKEYCHAIN_LIBRARY_NAME}d")
1213
endif()
1314

1415
set(QTKEYCHAIN_SHARED_LIBRARY
1516
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/bin/${CMAKE_SHARED_LIBRARY_PREFIX}${QTKEYCHAIN_LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
16-
if (MSVC)
17+
18+
if (MSVC OR EMSCRIPTEN)
1719
set(QTKEYCHAIN_LIBRARY
18-
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/lib/${QTKEYCHAIN_LIBRARY_NAME}.lib")
20+
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${QTKEYCHAIN_LIBRARY_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}")
1921
else()
2022
set(QTKEYCHAIN_LIBRARY "${QTKEYCHAIN_SHARED_LIBRARY}")
2123
endif()
24+
2225
set(QTKEYCHAIN_LIBRARIES ${QTKEYCHAIN_LIBRARY})
2326
set(QTKEYCHAIN_INCLUDE_DIR
2427
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/qtkeychain/" PARENT_SCOPE)
@@ -32,9 +35,19 @@ else()
3235
add_dependencies(qtkeychain qtkeychain_repo)
3336
else()
3437
include(ExternalProject)
38+
39+
set(QTKEYCHAIN_EXTRA_ARGS -DBUILD_SHARED_LIBS:BOOL=ON)
40+
if(EMSCRIPTEN)
41+
set(QTKEYCHAIN_EXTRA_ARGS
42+
-DBUILD_SHARED_LIBS:BOOL=OFF
43+
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
44+
-DQT_HOST_PATH=${QT_HOST_PATH}
45+
)
46+
endif()
47+
3548
ExternalProject_Add(qtkeychain_repo
36-
GIT_REPOSITORY "https://github.com/frankosterfeld/qtkeychain.git"
37-
GIT_TAG "7668a63a3669400223c5a563928ae042f499dbf4"
49+
GIT_REPOSITORY "https://github.com/nschimme/qtkeychain.git"
50+
GIT_TAG "wasm"
3851

3952
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-src"
4053
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build"
@@ -43,17 +56,18 @@ else()
4356
CMAKE_ARGS -DBUILD_WITH_QT6:BOOL=ON
4457
-DBUILD_TRANSLATIONS:BOOL=OFF
4558
-DBUILD_TEST_APPLICATION:BOOL=OFF
46-
-DBUILD_SHARED_LIBS:BOOL=ON
4759
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
4860
-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET}
61+
${QTKEYCHAIN_EXTRA_ARGS}
4962
CMAKE_CACHE_ARGS -DCMAKE_PREFIX_PATH:PATH=${Qt6_DIR}
5063

5164
UPDATE_COMMAND ""
5265
BUILD_ALWAYS 0
5366

5467
BUILD_BYPRODUCTS "${QTKEYCHAIN_SHARED_LIBRARY}"
55-
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/lib/${QTKEYCHAIN_LIBRARY_NAME}.lib"
68+
"${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${QTKEYCHAIN_LIBRARY_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}"
5669
)
70+
5771
add_custom_target(qtkeychain)
5872
if(WIN32)
5973
if(MSVC)

src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,8 @@ set(mmapper_SRCS
525525
preferences/grouppage.h
526526
preferences/mumeprotocolpage.cpp
527527
preferences/mumeprotocolpage.h
528+
preferences/ManagePasswordDialog.cpp
529+
preferences/ManagePasswordDialog.h
528530
preferences/parserpage.cpp
529531
preferences/parserpage.h
530532
preferences/pathmachinepage.cpp
@@ -665,6 +667,7 @@ set(mmapper_UIS
665667
preferences/graphicspage.ui
666668
preferences/grouppage.ui
667669
preferences/mumeprotocolpage.ui
670+
preferences/ManagePasswordDialog.ui
668671
preferences/parserpage.ui
669672
preferences/pathmachinepage.ui
670673
)

src/configuration/PasswordConfig.cpp

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
#include "PasswordConfig.h"
55

6-
#include "../global/macros.h"
6+
#include "../global/ConfigConsts-Computed.h"
7+
#include "../global/ConfigEnums.h"
8+
#include "configuration.h"
79

810
#ifndef MMAPPER_NO_QTKEYCHAIN
911
static const QLatin1String PASSWORD_KEY("password");
@@ -15,21 +17,43 @@ PasswordConfig::PasswordConfig(QObject *const parent)
1517
#ifndef MMAPPER_NO_QTKEYCHAIN
1618
, m_readJob(APP_NAME)
1719
, m_writeJob(APP_NAME)
20+
, m_deleteJob(APP_NAME)
1821
{
1922
m_readJob.setAutoDelete(false);
2023
m_writeJob.setAutoDelete(false);
24+
m_deleteJob.setAutoDelete(false);
2125

22-
connect(&m_readJob, &QKeychain::ReadPasswordJob::finished, [this]() {
26+
auto handleError = [this](const QKeychain::Job &job) {
27+
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
28+
if (job.error() == QKeychain::AccessDeniedByUser) {
29+
return true;
30+
}
31+
}
32+
emit sig_error(job.errorString());
33+
return false;
34+
};
35+
36+
connect(&m_readJob, &QKeychain::ReadPasswordJob::finished, [this, handleError]() {
2337
if (m_readJob.error()) {
24-
emit sig_error(m_readJob.errorString());
38+
handleError(m_readJob);
2539
} else {
2640
emit sig_incomingPassword(m_readJob.textData());
2741
}
2842
});
2943

30-
connect(&m_writeJob, &QKeychain::WritePasswordJob::finished, [this]() {
44+
connect(&m_writeJob, &QKeychain::WritePasswordJob::finished, [this, handleError]() {
3145
if (m_writeJob.error()) {
32-
emit sig_error(m_writeJob.errorString());
46+
handleError(m_writeJob);
47+
} else {
48+
emit sig_passwordSaved();
49+
}
50+
});
51+
52+
connect(&m_deleteJob, &QKeychain::DeletePasswordJob::finished, [this, handleError]() {
53+
if (m_deleteJob.error()) {
54+
handleError(m_deleteJob);
55+
} else {
56+
emit sig_passwordDeleted();
3357
}
3458
});
3559
}
@@ -41,7 +65,11 @@ PasswordConfig::PasswordConfig(QObject *const parent)
4165
void PasswordConfig::setPassword(const QString &password)
4266
{
4367
#ifndef MMAPPER_NO_QTKEYCHAIN
44-
m_writeJob.setKey(PASSWORD_KEY);
68+
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
69+
m_writeJob.setKey(getConfig().account.accountName);
70+
} else {
71+
m_writeJob.setKey(PASSWORD_KEY);
72+
}
4573
m_writeJob.setTextData(password);
4674
m_writeJob.start();
4775
#else
@@ -53,9 +81,27 @@ void PasswordConfig::setPassword(const QString &password)
5381
void PasswordConfig::getPassword()
5482
{
5583
#ifndef MMAPPER_NO_QTKEYCHAIN
56-
m_readJob.setKey(PASSWORD_KEY);
84+
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
85+
m_readJob.setKey(getConfig().account.accountName);
86+
} else {
87+
m_readJob.setKey(PASSWORD_KEY);
88+
}
5789
m_readJob.start();
5890
#else
5991
emit sig_error("Password retrieval is not available.");
6092
#endif
6193
}
94+
95+
void PasswordConfig::deletePassword()
96+
{
97+
#ifndef MMAPPER_NO_QTKEYCHAIN
98+
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
99+
m_deleteJob.setKey(getConfig().account.accountName);
100+
} else {
101+
m_deleteJob.setKey(PASSWORD_KEY);
102+
}
103+
m_deleteJob.start();
104+
#else
105+
emit sig_error("Password deletion is not available.");
106+
#endif
107+
}

src/configuration/PasswordConfig.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Copyright (C) 2019 The MMapper Authors
44

55
#include "../global/RuleOf5.h"
6+
#include "../global/macros.h"
67

78
#include <QObject>
89
#include <QString>
@@ -11,14 +12,15 @@
1112
#include <keychain.h>
1213
#endif
1314

14-
class PasswordConfig final : public QObject
15+
class NODISCARD_QOBJECT PasswordConfig final : public QObject
1516
{
1617
Q_OBJECT
1718

1819
private:
1920
#ifndef MMAPPER_NO_QTKEYCHAIN
2021
QKeychain::ReadPasswordJob m_readJob;
2122
QKeychain::WritePasswordJob m_writeJob;
23+
QKeychain::DeletePasswordJob m_deleteJob;
2224
#endif
2325

2426
public:
@@ -28,8 +30,11 @@ class PasswordConfig final : public QObject
2830

2931
void setPassword(const QString &password);
3032
void getPassword();
33+
void deletePassword();
3134

3235
signals:
3336
void sig_error(const QString &msg);
3437
void sig_incomingPassword(const QString &password);
38+
void sig_passwordSaved();
39+
void sig_passwordDeleted();
3540
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (C) 2026 The MMapper Authors
3+
4+
#include "ManagePasswordDialog.h"
5+
6+
#include "../global/ConfigConsts-Computed.h"
7+
#include "../global/ConfigEnums.h"
8+
#include "ui_ManagePasswordDialog.h"
9+
10+
#include <QLineEdit>
11+
#include <QPushButton>
12+
13+
ManagePasswordDialog::ManagePasswordDialog(QWidget *parent)
14+
: QDialog(parent)
15+
, ui(new Ui::ManagePasswordDialog)
16+
{
17+
ui->setupUi(this);
18+
19+
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
20+
setWindowTitle("Manage Account");
21+
ui->label_2->hide();
22+
ui->accountPassword->hide();
23+
ui->showPassword->hide();
24+
ui->deleteButton->hide();
25+
} else {
26+
connect(ui->showPassword, &QToolButton::toggled, this, [this](bool checked) {
27+
ui->accountPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
28+
});
29+
30+
connect(ui->deleteButton, &QPushButton::clicked, this, [this]() {
31+
emit sig_deleteRequested();
32+
ui->accountPassword->clear();
33+
ui->deleteButton->setEnabled(false);
34+
});
35+
}
36+
}
37+
38+
ManagePasswordDialog::~ManagePasswordDialog()
39+
{
40+
delete ui;
41+
}
42+
43+
void ManagePasswordDialog::setAccountName(const QString &name)
44+
{
45+
ui->accountName->setText(name);
46+
}
47+
48+
QString ManagePasswordDialog::accountName() const
49+
{
50+
return ui->accountName->text();
51+
}
52+
53+
void ManagePasswordDialog::setPassword(const QString &password)
54+
{
55+
ui->accountPassword->setText(password);
56+
if constexpr (CURRENT_PLATFORM != PlatformEnum::Wasm) {
57+
ui->deleteButton->setEnabled(!password.isEmpty());
58+
}
59+
}
60+
61+
QString ManagePasswordDialog::password() const
62+
{
63+
return ui->accountPassword->text();
64+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
// Copyright (C) 2026 The MMapper Authors
4+
5+
#include "../global/macros.h"
6+
7+
#include <QDialog>
8+
9+
namespace Ui {
10+
class ManagePasswordDialog;
11+
}
12+
13+
class NODISCARD_QOBJECT ManagePasswordDialog final : public QDialog
14+
{
15+
Q_OBJECT
16+
17+
private:
18+
Ui::ManagePasswordDialog *const ui;
19+
20+
public:
21+
explicit ManagePasswordDialog(QWidget *parent = nullptr);
22+
~ManagePasswordDialog() final;
23+
24+
void setAccountName(const QString &name);
25+
NODISCARD QString accountName() const;
26+
27+
void setPassword(const QString &password);
28+
NODISCARD QString password() const;
29+
30+
signals:
31+
void sig_deleteRequested();
32+
};

0 commit comments

Comments
 (0)