Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ RUN ./emsdk activate 4.0.7 && \
qt-cmake -S /app -B /build \
-DQT_HOST_PATH=$QTSDK_DIR/6.10.3/gcc_64 \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_OPENSSL=OFF -DWITH_TESTS=OFF -DWITH_WEBSOCKET=ON -DWITH_UPDATER=OFF -DPACKAGE_TYPE=Wasm && \
-DWITH_OPENSSL=OFF -DWITH_TESTS=OFF -DWITH_WEBSOCKET=ON -DWITH_UPDATER=OFF -DWITH_QTKEYCHAIN=ON -DPACKAGE_TYPE=Wasm && \
ACTUAL_JOBS=${JOBS:-$(nproc)} && \
cmake --build /build --parallel ${ACTUAL_JOBS} && \
cmake --install /build --prefix /dist && \
Expand Down
6 changes: 3 additions & 3 deletions external/qtkeychain/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
if(QTKEYCHAIN_FOUND)
message(STATUS "QtKeychain found: ${QTKEYCHAIN_LIBRARIES} ${QTKEYCHAIN_INCLUDE_DIRS}")
else()
if(WIN32 OR APPLE)
if(WIN32 OR APPLE OR EMSCRIPTEN)
message(STATUS "Could not find system QtKeychain; building from local source tree")
else()
message(FATAL_ERROR "QtKeychain NOT found: use `-DWITH_QTKEYCHAIN=OFF` to build without account autologin")
Expand Down Expand Up @@ -33,8 +33,8 @@ else()
else()
include(ExternalProject)
ExternalProject_Add(qtkeychain_repo
GIT_REPOSITORY "https://github.com/frankosterfeld/qtkeychain.git"
GIT_TAG "7668a63a3669400223c5a563928ae042f499dbf4"
GIT_REPOSITORY "https://github.com/nschimme/qtkeychain.git"
GIT_TAG "wasm"

SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/qtkeychain-build"
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ set(mmapper_SRCS
preferences/grouppage.h
preferences/mumeprotocolpage.cpp
preferences/mumeprotocolpage.h
preferences/ManagePasswordDialog.cpp
preferences/ManagePasswordDialog.h
preferences/parserpage.cpp
preferences/parserpage.h
preferences/pathmachinepage.cpp
Expand Down Expand Up @@ -665,6 +667,7 @@ set(mmapper_UIS
preferences/graphicspage.ui
preferences/grouppage.ui
preferences/mumeprotocolpage.ui
preferences/ManagePasswordDialog.ui
preferences/parserpage.ui
preferences/pathmachinepage.ui
)
Expand Down
67 changes: 58 additions & 9 deletions src/configuration/PasswordConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,57 @@

#include "PasswordConfig.h"

#include "../global/ConfigConsts-Computed.h"
#include "../global/ConfigEnums.h"
#include "../global/macros.h"

#ifndef MMAPPER_NO_QTKEYCHAIN
static const QLatin1String PASSWORD_KEY("password");
static const QLatin1String APP_NAME("org.mume.mmapper");
static const QLatin1String PASSWORD_KEY("password");
#endif

PasswordConfig::PasswordConfig(QObject *const parent)
: QObject(parent)
#ifndef MMAPPER_NO_QTKEYCHAIN
, m_readJob(APP_NAME)
, m_writeJob(APP_NAME)
, m_deleteJob(APP_NAME)
{
m_readJob.setAutoDelete(false);
m_writeJob.setAutoDelete(false);
m_deleteJob.setAutoDelete(false);

auto handleError = [this](const QKeychain::Job &job) {
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
if (job.error() == QKeychain::AccessDeniedByUser) {
return true;
}
}
emit sig_error(job.errorString());
return false;
Comment on lines +26 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: handleError lambda returns a bool that is never consumed

Since all callers ignore the bool this lambda returns, the value is effectively dead and makes the intent less clear. Either change it to return void, or add the branching logic that uses the bool if you plan to act on it later.

};

connect(&m_readJob, &QKeychain::ReadPasswordJob::finished, [this]() {
connect(&m_readJob, &QKeychain::ReadPasswordJob::finished, [this, handleError]() {
if (m_readJob.error()) {
emit sig_error(m_readJob.errorString());
handleError(m_readJob);
} else {
emit sig_incomingPassword(m_readJob.textData());
}
});

connect(&m_writeJob, &QKeychain::WritePasswordJob::finished, [this]() {
connect(&m_writeJob, &QKeychain::WritePasswordJob::finished, [this, handleError]() {
if (m_writeJob.error()) {
emit sig_error(m_writeJob.errorString());
handleError(m_writeJob);
} else {
emit sig_passwordSaved();
}
});

connect(&m_deleteJob, &QKeychain::DeletePasswordJob::finished, [this, handleError]() {
if (m_deleteJob.error()) {
handleError(m_deleteJob);
} else {
emit sig_passwordDeleted();
}
});
}
Expand All @@ -38,24 +62,49 @@ PasswordConfig::PasswordConfig(QObject *const parent)
}
#endif

void PasswordConfig::setPassword(const QString &password)
void PasswordConfig::setPassword(const QString &accountName, const QString &password)
{
#ifndef MMAPPER_NO_QTKEYCHAIN
m_writeJob.setKey(PASSWORD_KEY);
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
m_writeJob.setKey(accountName);
} else {
m_writeJob.setKey(PASSWORD_KEY);
}
m_writeJob.setTextData(password);
m_writeJob.start();
#else
std::ignore = accountName;
std::ignore = password;
emit sig_error("Password setting is not available.");
#endif
}

void PasswordConfig::getPassword()
void PasswordConfig::getPassword(const QString &accountName)
{
#ifndef MMAPPER_NO_QTKEYCHAIN
m_readJob.setKey(PASSWORD_KEY);
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
m_readJob.setKey(accountName);
} else {
m_readJob.setKey(PASSWORD_KEY);
}
m_readJob.start();
#else
std::ignore = accountName;
emit sig_error("Password retrieval is not available.");
#endif
}

void PasswordConfig::deletePassword(const QString &accountName)
{
#ifndef MMAPPER_NO_QTKEYCHAIN
if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
m_deleteJob.setKey(accountName);
} else {
m_deleteJob.setKey(PASSWORD_KEY);
}
m_deleteJob.start();
#else
std::ignore = accountName;
emit sig_error("Password deletion is not available.");
#endif
}
11 changes: 8 additions & 3 deletions src/configuration/PasswordConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright (C) 2019 The MMapper Authors

#include "../global/RuleOf5.h"
#include "../global/macros.h"

#include <QObject>
#include <QString>
Expand All @@ -11,25 +12,29 @@
#include <keychain.h>
#endif

class PasswordConfig final : public QObject
class NODISCARD_QOBJECT PasswordConfig final : public QObject
{
Q_OBJECT

private:
#ifndef MMAPPER_NO_QTKEYCHAIN
QKeychain::ReadPasswordJob m_readJob;
QKeychain::WritePasswordJob m_writeJob;
QKeychain::DeletePasswordJob m_deleteJob;
#endif

public:
explicit PasswordConfig(QObject *parent = nullptr);
~PasswordConfig() final = default;
DELETE_CTORS_AND_ASSIGN_OPS(PasswordConfig);

void setPassword(const QString &password);
void getPassword();
void setPassword(const QString &accountName, const QString &password);
void getPassword(const QString &accountName);
void deletePassword(const QString &accountName);

signals:
void sig_error(const QString &msg);
void sig_incomingPassword(const QString &password);
void sig_passwordSaved();
void sig_passwordDeleted();
};
64 changes: 64 additions & 0 deletions src/preferences/ManagePasswordDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) 2019 The MMapper Authors

#include "ManagePasswordDialog.h"

#include "../global/ConfigConsts-Computed.h"
#include "../global/ConfigEnums.h"
#include "ui_ManagePasswordDialog.h"

#include <QLineEdit>
#include <QPushButton>

ManagePasswordDialog::ManagePasswordDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::ManagePasswordDialog)
{
ui->setupUi(this);

if constexpr (CURRENT_PLATFORM == PlatformEnum::Wasm) {
setWindowTitle("Manage Account");
ui->label_2->hide();
ui->accountPassword->hide();
ui->showPassword->hide();
ui->deleteButton->hide();
} else {
connect(ui->showPassword, &QToolButton::toggled, this, [this](bool checked) {
ui->accountPassword->setEchoMode(checked ? QLineEdit::Normal : QLineEdit::Password);
});

connect(ui->deleteButton, &QPushButton::clicked, this, [this]() {
emit sig_deleteRequested();
ui->accountPassword->clear();
ui->deleteButton->setEnabled(false);
Comment on lines +30 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Delete button stays disabled for the lifetime of the dialog after first use

After a delete, the button remains disabled for that dialog instance. If the user edits the password field again in the same session, they can’t delete without closing and reopening the dialog. If multi-step edits should be allowed, consider re‑enabling the button when accountPassword becomes non‑empty (e.g., via a textChanged handler).

});
}
}

ManagePasswordDialog::~ManagePasswordDialog()
{
delete ui;
}

void ManagePasswordDialog::setAccountName(const QString &name)
{
ui->accountName->setText(name);
}

QString ManagePasswordDialog::accountName() const
{
return ui->accountName->text();
}

void ManagePasswordDialog::setPassword(const QString &password)
{
ui->accountPassword->setText(password);
if constexpr (CURRENT_PLATFORM != PlatformEnum::Wasm) {
ui->deleteButton->setEnabled(!password.isEmpty());
}
}

QString ManagePasswordDialog::password() const
{
return ui->accountPassword->text();
}
32 changes: 32 additions & 0 deletions src/preferences/ManagePasswordDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) 2019 The MMapper Authors

#include "../global/macros.h"

#include <QDialog>

namespace Ui {
class ManagePasswordDialog;
}

class NODISCARD_QOBJECT ManagePasswordDialog final : public QDialog
{
Q_OBJECT

private:
Ui::ManagePasswordDialog *const ui;

public:
explicit ManagePasswordDialog(QWidget *parent = nullptr);
~ManagePasswordDialog() final;

void setAccountName(const QString &name);
NODISCARD QString accountName() const;

void setPassword(const QString &password);
NODISCARD QString password() const;

signals:
void sig_deleteRequested();
};
93 changes: 93 additions & 0 deletions src/preferences/ManagePasswordDialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ManagePasswordDialog</class>
<widget class="QDialog" name="ManagePasswordDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>180</height>
</rect>
</property>
<property name="windowTitle">
<string>Manage Password</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Account:</string>
</property>
<property name="buddy">
<cstring>accountName</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Password:</string>
</property>
<property name="buddy">
<cstring>accountPassword</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="accountName"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="accountPassword">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="showPassword">
<property name="text">
<string>View</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ManagePasswordDialog</receiver>
<slot>accept()</slot>
</connection>
</connections>
</ui>
Loading