diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 8f0b2854a..a3852c800 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -65,6 +65,9 @@ install(TARGETS keepassxc-cli
RUNTIME DESTINATION ${CLI_INSTALL_DIR} COMPONENT Runtime)
if(WIN32)
+ target_sources(keepassxc-cli
+ PRIVATE keepassxc-cli.exe.manifest)
+
# install(CODE "include(BundleUtilities)
# fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/keepassxc-cli.exe\" \"\" \"\")"
# COMPONENT Runtime)
diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp
index 6c0b73e93..d8134de26 100644
--- a/src/cli/Utils.cpp
+++ b/src/cli/Utils.cpp
@@ -63,6 +63,13 @@ namespace Utils
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
#endif
DEVNULL.setDevice(fd);
+
+#ifdef Q_OS_WIN
+ // On Windows, we ask via keepassxc-cli.exe.manifest to use UTF-8,
+ // but the console code-page isn't automatically changed to match.
+ SetConsoleCP(GetACP());
+ SetConsoleOutputCP(GetACP());
+#endif
}
void setStdinEcho(bool enable = true)
diff --git a/src/cli/keepassxc-cli.exe.manifest b/src/cli/keepassxc-cli.exe.manifest
new file mode 100644
index 000000000..9ef750e6f
--- /dev/null
+++ b/src/cli/keepassxc-cli.exe.manifest
@@ -0,0 +1,8 @@
+
+
+
+
+ UTF-8
+
+
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 325ff880e..b84869c19 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -233,6 +233,7 @@ endif()
add_unit_test(NAME testcli SOURCES TestCli.cpp
LIBS testsupport cli ${TEST_LIBRARIES})
+target_compile_definitions(testcli PRIVATE KEEPASSX_CLI_PATH="$")
if(WITH_GUI_TESTS)
add_subdirectory(gui)
diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp
index c560f4c25..5b136ca83 100644
--- a/tests/TestCli.cpp
+++ b/tests/TestCli.cpp
@@ -102,6 +102,9 @@ void TestCli::init()
m_yubiKeyProtectedDbFile.reset(new TemporaryFile());
m_yubiKeyProtectedDbFile->copyFromFile(file.arg("YubiKeyProtectedPasswords.kdbx"));
+ m_nonAsciiDbFile.reset(new TemporaryFile());
+ m_nonAsciiDbFile->copyFromFile(file.arg("NonAscii.kdbx"));
+
m_stdout.reset(new QBuffer());
m_stdout->open(QIODevice::ReadWrite);
Utils::STDOUT.setDevice(m_stdout.data());
@@ -2316,6 +2319,29 @@ void TestCli::testYubiKeyOption()
QCOMPARE(m_stdout->readAll(), QByteArray());
}
+void TestCli::testNonAscii()
+{
+ QProcess process;
+ process.setProcessChannelMode(QProcess::MergedChannels);
+ process.start(
+ KEEPASSX_CLI_PATH,
+ QStringList(
+ {"show", "-a", "password", m_nonAsciiDbFile->fileName(), QString::fromUtf8("\xe7\xa7\x98\xe5\xaf\x86")}));
+ process.waitForStarted();
+ QCOMPARE(process.state(), QProcess::ProcessState::Running);
+
+ // Write password.
+ process.write("\xce\x94\xc3\xb6\xd8\xb6\n");
+ process.closeWriteChannel();
+
+ process.waitForFinished();
+
+ process.readLine(); // skip password prompt
+ QByteArray password = process.readLine();
+ QCOMPARE(QString::fromUtf8(password).trimmed(),
+ QString::fromUtf8("\xf0\x9f\x9a\x97\xf0\x9f\x90\x8e\xf0\x9f\x94\x8b\xf0\x9f\x93\x8e"));
+}
+
void TestCli::testCommandParsing_data()
{
QTest::addColumn("input");
diff --git a/tests/TestCli.h b/tests/TestCli.h
index 3beb80724..d33dde26c 100644
--- a/tests/TestCli.h
+++ b/tests/TestCli.h
@@ -80,6 +80,7 @@ private slots:
void testShow();
void testInvalidDbFiles();
void testYubiKeyOption();
+ void testNonAscii();
private:
QScopedPointer m_devNull;
@@ -90,6 +91,7 @@ private:
QScopedPointer m_keyFileProtectedDbFile;
QScopedPointer m_keyFileProtectedNoPasswordDbFile;
QScopedPointer m_yubiKeyProtectedDbFile;
+ QScopedPointer m_nonAsciiDbFile;
QScopedPointer m_stdout;
QScopedPointer m_stderr;
diff --git a/tests/data/NonAscii.kdbx b/tests/data/NonAscii.kdbx
index 06aa5bf2c..8ebaac249 100644
Binary files a/tests/data/NonAscii.kdbx and b/tests/data/NonAscii.kdbx differ