aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSaturn&Eric <[email protected]>2022-05-13 19:31:40 +0000
committerGitHub <[email protected]>2022-05-13 19:31:40 +0000
commit49090d1d511a4a0fbcfac253656a40a42716c82e (patch)
tree2553bd9a1d14c099a0a990576fb7c874f72b3859
parentMerge pull request #60 from saturneric/develop-2.0.7 (diff)
parentfix(core): solve memory access issues (diff)
downloadGpgFrontend-49090d1d511a4a0fbcfac253656a40a42716c82e.tar.gz
GpgFrontend-49090d1d511a4a0fbcfac253656a40a42716c82e.zip
Merge pull request #62 from saturneric/develop-2.0.8v2.0.8
Develop 2.0.8
-rw-r--r--.github/workflows/debug.yml4
-rw-r--r--.github/workflows/release.yml3
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt5
-rw-r--r--manual/_sidebar.md2
-rw-r--r--src/core/CMakeLists.txt36
-rw-r--r--src/core/GpgConstants.cpp33
-rw-r--r--src/core/function/CharsetOperator.cpp136
-rw-r--r--src/core/function/CharsetOperator.h49
-rw-r--r--src/core/function/gpg/GpgKeyGetter.cpp57
-rw-r--r--src/core/function/gpg/GpgKeyGetter.h16
-rw-r--r--src/core/thread/CtxCheckTask.cpp (renamed from src/core/thread/CtxCheckThread.cpp)8
-rw-r--r--src/core/thread/CtxCheckTask.h (renamed from src/core/thread/CtxCheckThread.h)11
-rw-r--r--src/core/thread/FileReadTask.cpp88
-rw-r--r--src/core/thread/FileReadTask.h (renamed from src/ui/thread/FileReadThread.h)46
-rw-r--r--src/core/thread/Task.cpp74
-rw-r--r--src/core/thread/Task.h113
-rw-r--r--src/core/thread/TaskRunner.cpp66
-rw-r--r--src/core/thread/TaskRunner.h75
-rw-r--r--src/core/thread/TaskRunnerGetter.cpp46
-rw-r--r--src/core/thread/TaskRunnerGetter.h56
-rw-r--r--src/main.cpp1
-rw-r--r--src/ui/GpgFrontendUIInit.cpp37
-rw-r--r--src/ui/UserInterfaceUtils.cpp67
-rw-r--r--src/ui/keypair_details/KeyPairOperaTab.cpp15
-rw-r--r--src/ui/settings/SettingsKeyServer.cpp2
-rw-r--r--src/ui/thread/FileReadThread.cpp87
-rw-r--r--src/ui/widgets/FilePage.cpp35
-rw-r--r--src/ui/widgets/KeyList.cpp29
-rw-r--r--src/ui/widgets/PlainTextEditorPage.cpp189
-rw-r--r--src/ui/widgets/PlainTextEditorPage.h37
-rw-r--r--src/ui/widgets/TextEdit.cpp30
32 files changed, 1096 insertions, 359 deletions
diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml
index b8468ba7..9ee690be 100644
--- a/.github/workflows/debug.yml
+++ b/.github/workflows/debug.yml
@@ -124,7 +124,7 @@ jobs:
pacman --noconfirm -S --needed make texinfo mingw-w64-x86_64-libconfig mingw-w64-x86_64-boost automake
pacman --noconfirm -S --needed mingw-w64-x86_64-qt5 libintl msys2-runtime-devel gettext-devel
pacman --noconfirm -S --needed mingw-w64-x86_64-ninja mingw-w64-x86_64-gnupg mingw-w64-x86_64-gpgme
- pacman --noconfirm -S --needed mingw-w64-x86_64-libarchive
+ pacman --noconfirm -S --needed mingw-w64-x86_64-libarchive mingw-w64-x86_64-icu mingw-w64-x86_64-icu-debug-libs
if: matrix.os == 'windows-latest'
- name: Build GpgFrontend (Linux)
@@ -137,7 +137,7 @@ jobs:
- name: Build GpgFrontend (macOS)
# Build your GpgFrontend with the given configuration
run: |
- cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DGPGFRONTEND_BUILD_TYPE_TEST_UI=ON -DOPENSSL_ROOT_DIR=/usr/local/opt/[email protected]
+ cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DGPGFRONTEND_BUILD_TYPE_TEST_UI=ON
cmake --build ${{github.workspace}}/build --config {{$env.BUILD_TYPE}} -- -v
if: matrix.os == 'macos-10.15'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 32577559..5696c692 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -131,6 +131,7 @@ jobs:
pacman --noconfirm -S --needed make texinfo mingw-w64-x86_64-libconfig mingw-w64-x86_64-boost automake
pacman --noconfirm -S --needed mingw-w64-x86_64-qt5 libintl msys2-runtime-devel gettext-devel mingw-w64-x86_64-gpgme
pacman --noconfirm -S --needed mingw-w64-x86_64-ninja mingw-w64-x86_64-gnupg mingw-w64-x86_64-libarchive
+ pacman --noconfirm -S --needed mingw-w64-x86_64-icu mingw-w64-x86_64-icu-debug-libs
if: matrix.os == 'windows-2019'
- name: Build GpgFrontend (Linux)
@@ -143,7 +144,7 @@ jobs:
- name: Build GpgFrontend (macOS)
# Build your GpgFrontend with the given configuration
run: |
- cmake -B ${{github.workspace}}/build -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DOPENSSL_ROOT_DIR=/usr/local/opt/[email protected]
+ cmake -B ${{github.workspace}}/build -G Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
cmake --build ${{github.workspace}}/build --config {{$env.BUILD_TYPE}} -- -v
if: matrix.os == 'macos-10.15'
diff --git a/.gitignore b/.gitignore
index 7b2c01dc..3a8c7092 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,7 @@ src/GpgFrontend.h
src/GpgFrontendBuildInfo.h
src/GpgFrontendBuildInstallInfo.h
src/core/GpgFrontendCoreExport.h
-src/core/GpgFrontendUIExport.h
+src/ui/GpgFrontendUIExport.h
docs/
# gettext
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 167f23d5..e93de668 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@
cmake_minimum_required(VERSION 3.16)
# define project
-project(GpgFrontend VERSION 2.0.7 LANGUAGES CXX)
+project(GpgFrontend VERSION 2.0.8 LANGUAGES CXX)
# show cmake version
message(STATUS "GpgFrontend Build Configuration Started CMAKE Version ${CMAKE_VERSION}")
@@ -422,6 +422,9 @@ SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
find_package(Boost COMPONENTS date_time system REQUIRED)
# Introduce OpenSSL
+if(APPLE)
+ set(OPENSSL_ROOT_DIR /usr/local/opt/[email protected])
+endif()
find_package(OpenSSL REQUIRED)
# Introduce Qt
diff --git a/manual/_sidebar.md b/manual/_sidebar.md
index 399909b7..26a39c9f 100644
--- a/manual/_sidebar.md
+++ b/manual/_sidebar.md
@@ -13,8 +13,6 @@
- [View Key Pair Details](manual/view-keypair-info.md)
- [Import & Export Key Pair](manual/import-export-key-pair.md)
- [Key Server Operations](manual/key-server-operations.md)
- - [Email Operations](manual/email-operations.md)
- - [Advanced Key Operation](manual/advance-key-opera.md)
- Features Guides
- [Introduce](features/introduce.md)
- [Short Cipher-text](features/short-ciphertext.md)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f11accd4..ee0e39a1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -44,9 +44,9 @@ set(UTILS_DIR ${CMAKE_SOURCE_DIR}/utils)
set(GPGME_LIB_DIR ${UTILS_DIR}/gpgme/lib)
# link third-party libraries
-target_link_libraries(gpgfrontend_core config++)
+target_link_libraries(gpgfrontend_core PUBLIC config++)
if (NOT LINUX)
- target_link_libraries(gpgfrontend_core config++ intl)
+ target_link_libraries(gpgfrontend_core PUBLIC config++ intl)
endif ()
# easyloggingpp
@@ -55,28 +55,40 @@ target_include_directories(gpgfrontend_core PUBLIC
target_sources(gpgfrontend_core PUBLIC
${CMAKE_SOURCE_DIR}/third_party/easyloggingpp/src/easylogging++.cc)
# qt-aes
-target_sources(gpgfrontend_core PUBLIC
+target_sources(gpgfrontend_core PRIVATE
${CMAKE_SOURCE_DIR}/third_party/qt-aes/qaesencryption.cpp)
# encoding detect library
aux_source_directory(${CMAKE_SOURCE_DIR}/third_party/encoding-detect ENCODING_DETECT_SOURCE_CODE)
target_sources(gpgfrontend_core PUBLIC ${ENCODING_DETECT_SOURCE_CODE})
+# icu
+if(APPLE)
+ target_include_directories(gpgfrontend_core PRIVATE /usr/local/opt/icu4c/include)
+ target_link_directories(gpgfrontend_core PRIVATE /usr/local/opt/icu4c/lib)
+ target_link_libraries(gpgfrontend_core PRIVATE icui18n icuuc icudata)
+else ()
+ find_package(ICU 60.0 REQUIRED COMPONENTS i18n uc data)
+ message("ICU version: ${ICU_VERSION}")
+ message("ICU libraries: ${ICU_LIBRARIES}")
+ target_link_libraries(gpgfrontend_core PRIVATE ${ICU_LIBRARIES})
+endif ()
+
# link gnupg libraries
-target_link_libraries(gpgfrontend_core gpgme assuan gpg-error)
+target_link_libraries(gpgfrontend_core PUBLIC gpgme assuan gpg-error)
# link openssl
-target_link_libraries(gpgfrontend_core OpenSSL::SSL OpenSSL::Crypto)
+target_link_libraries(gpgfrontend_core PUBLIC OpenSSL::SSL OpenSSL::Crypto)
# link boost libraries
-target_link_libraries(gpgfrontend_core ${Boost_LIBRARIES})
+target_link_libraries(gpgfrontend_core PUBLIC ${Boost_LIBRARIES})
# link libarchive
-target_link_libraries(gpgfrontend_core archive)
+target_link_libraries(gpgfrontend_core PRIVATE archive)
# link json
target_link_libraries(gpgfrontend_core
- nlohmann_json::nlohmann_json)
+ PUBLIC nlohmann_json::nlohmann_json)
# link Qt core
-target_link_libraries(gpgfrontend_core Qt5::Core)
+target_link_libraries(gpgfrontend_core PUBLIC Qt5::Core)
# set up pch
target_precompile_headers(gpgfrontend_core
@@ -89,10 +101,10 @@ target_compile_features(gpgfrontend_core PUBLIC cxx_std_17)
# link for different platforms
if (MINGW)
message(STATUS "Link GPG Static Library For MINGW")
- target_link_libraries(gpgfrontend_core wsock32)
+ target_link_libraries(gpgfrontend_core PUBLIC wsock32)
elseif (APPLE)
message(STATUS "Link GPG Static Library For macOS")
- target_link_libraries(gpgfrontend_core dl)
+ target_link_libraries(gpgfrontend_core PUBLIC dl)
if (XCODE_BUILD)
set_target_properties(gpgfrontend_core
PROPERTIES
@@ -105,5 +117,5 @@ elseif (APPLE)
else ()
# linux
message(STATUS "Link GPG Static Library For Unix")
- target_link_libraries(gpgfrontend_core pthread dl)
+ target_link_libraries(gpgfrontend_core PUBLIC pthread dl)
endif ()
diff --git a/src/core/GpgConstants.cpp b/src/core/GpgConstants.cpp
index 284022a8..88068f37 100644
--- a/src/core/GpgConstants.cpp
+++ b/src/core/GpgConstants.cpp
@@ -33,6 +33,8 @@
#include <boost/algorithm/string/predicate.hpp>
#include <string>
+#include "function/FileOperator.h"
+
const char* GpgFrontend::GpgConstants::PGP_CRYPT_BEGIN =
"-----BEGIN PGP MESSAGE-----"; ///<
const char* GpgFrontend::GpgConstants::PGP_CRYPT_END =
@@ -115,37 +117,14 @@ static inline std::string trim(std::string& s) {
}
std::string GpgFrontend::read_all_data_in_file(const std::string& utf8_path) {
- using namespace std::filesystem;
- class path file_info(utf8_path.c_str());
- if (!exists(file_info) || !is_regular_file(file_info)) return {};
- std::ifstream in_file;
-#ifndef WINDOWS
- in_file.open(file_info.u8string(), std::ios::in);
-#else
- in_file.open(file_info.wstring().c_str(), std::ios::in);
-#endif
- if (!in_file.good()) return {};
- std::istreambuf_iterator<char> begin(in_file);
- std::istreambuf_iterator<char> end;
- std::string in_buffer(begin, end);
- in_file.close();
- return in_buffer;
+ std::string data;
+ FileOperator::ReadFileStd(utf8_path, data);
+ return data;
}
bool GpgFrontend::write_buffer_to_file(const std::string& utf8_path,
const std::string& out_buffer) {
- using namespace std::filesystem;
- class path file_info(utf8_path.c_str());
-#ifndef WINDOWS
- std::ofstream out_file(file_info.u8string(), std::ios::out | std::ios::trunc);
-#else
- std::ofstream out_file(file_info.wstring().c_str(),
- std::ios::out | std::ios::trunc);
-#endif
- if (!out_file.good()) return false;
- out_file.write(out_buffer.c_str(), out_buffer.size());
- out_file.close();
- return true;
+ return FileOperator::WriteFileStd(utf8_path, out_buffer);
}
std::string GpgFrontend::get_file_extension(const std::string& path) {
diff --git a/src/core/function/CharsetOperator.cpp b/src/core/function/CharsetOperator.cpp
new file mode 100644
index 00000000..81c23388
--- /dev/null
+++ b/src/core/function/CharsetOperator.cpp
@@ -0,0 +1,136 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * All the source code of GpgFrontend was modified and released by
+ * Saturneric<[email protected]> starting on May 12, 2021.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#include "core/function/CharsetOperator.h"
+
+#include <unicode/ucnv.h>
+#include <unicode/ucsdet.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
+
+#include <cstddef>
+#include <memory>
+#include <string>
+
+#include "easylogging++.h"
+
+GpgFrontend::CharsetOperator::CharsetInfo GpgFrontend::CharsetOperator::Detect(
+ const std::string &buffer) {
+ const UCharsetMatch *ucm;
+ UErrorCode status = U_ZERO_ERROR;
+ UCharsetDetector *csd = ucsdet_open(&status);
+
+ status = U_ZERO_ERROR;
+ if (U_FAILURE(status)) {
+ LOG(ERROR) << "Failed to open charset detector: " << u_errorName(status);
+ return {"unknown", "unknown", 0};
+ }
+
+ LOG(INFO) << "Detecting charset buffer:" << buffer.size() << "bytes";
+
+ status = U_ZERO_ERROR;
+ ucsdet_setText(csd, buffer.data(), buffer.size(), &status);
+ if (U_FAILURE(status)) {
+ LOG(ERROR) << "Failed to set text to charset detector: "
+ << u_errorName(status);
+ return {"unknown", "unknown", 0};
+ }
+
+ status = U_ZERO_ERROR;
+ ucm = ucsdet_detect(csd, &status);
+
+ if (U_FAILURE(status)) return {"unknown", "unknown", 0};
+
+ status = U_ZERO_ERROR;
+ const char *name = ucsdet_getName(ucm, &status);
+ if (U_FAILURE(status)) return {"unknown", "unknown", 0};
+
+ status = U_ZERO_ERROR;
+ int confidence = ucsdet_getConfidence(ucm, &status);
+ if (U_FAILURE(status)) return {name, "unknown", 0};
+
+ status = U_ZERO_ERROR;
+ const char *language = ucsdet_getLanguage(ucm, &status);
+ if (U_FAILURE(status)) return {name, "unknown", confidence};
+
+ LOG(INFO) << "Detected charset: " << name << language << confidence;
+ return {name, language, confidence};
+}
+
+bool GpgFrontend::CharsetOperator::Convert2Utf8(const std::string &buffer,
+ std::string &out_buffer,
+ std::string from_charset_name) {
+ UErrorCode status = U_ZERO_ERROR;
+ const auto from_encode = std::string("utf-8");
+ const auto to_encode = from_charset_name;
+
+ LOG(INFO) << "Converting buffer:" << buffer.size();
+
+ // test if the charset is supported
+ UConverter *conv = ucnv_open(from_encode.c_str(), &status);
+ ucnv_close(conv);
+ if (U_FAILURE(status)) {
+ LOG(ERROR) << "Failed to open converter: " << u_errorName(status) << ":"
+ << from_encode;
+ return false;
+ }
+
+ // test if the charset is supported
+ conv = ucnv_open(to_encode.c_str(), &status);
+ ucnv_close(conv);
+ if (U_FAILURE(status)) {
+ LOG(ERROR) << "Failed to open converter: " << u_errorName(status) << ":"
+ << to_encode;
+ return false;
+ }
+
+ status = U_ZERO_ERROR;
+ int32_t target_limit = 0, target_capacity = 0;
+
+ target_capacity =
+ ucnv_convert(from_encode.c_str(), to_encode.c_str(), nullptr,
+ target_limit, buffer.data(), buffer.size(), &status);
+
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ status = U_ZERO_ERROR;
+ target_limit = target_capacity + 1;
+ out_buffer.clear();
+ out_buffer.resize(target_capacity);
+ target_capacity =
+ ucnv_convert(from_encode.c_str(), to_encode.c_str(), out_buffer.data(),
+ out_buffer.size(), buffer.data(), buffer.size(), &status);
+ }
+
+ if (U_FAILURE(status)) {
+ LOG(ERROR) << "Failed to convert to utf-8: " << u_errorName(status);
+ return false;
+ }
+
+ LOG(INFO) << "Converted buffer:" << out_buffer.size() << "bytes";
+ return true;
+} \ No newline at end of file
diff --git a/src/core/function/CharsetOperator.h b/src/core/function/CharsetOperator.h
new file mode 100644
index 00000000..c04430f2
--- /dev/null
+++ b/src/core/function/CharsetOperator.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * All the source code of GpgFrontend was modified and released by
+ * Saturneric<[email protected]> starting on May 12, 2021.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef GPGFRONTEND_CHARSETDETECTOR_H
+#define GPGFRONTEND_CHARSETDETECTOR_H
+
+#include <string>
+
+#include "core/GpgFrontendCore.h"
+
+namespace GpgFrontend {
+
+class GPGFRONTEND_CORE_EXPORT CharsetOperator {
+ public:
+ using CharsetInfo = std::tuple<std::string, std::string, int>;
+
+ static CharsetInfo Detect(const std::string &buffer);
+
+ static bool Convert2Utf8(const std::string &buffer, std::string &out_buffer,
+ std::string from_charset_name);
+};
+} // namespace GpgFrontend
+
+#endif // GPGFRONTEND_CHARSETDETECTOR_H
diff --git a/src/core/function/gpg/GpgKeyGetter.cpp b/src/core/function/gpg/GpgKeyGetter.cpp
index 9a7b505c..ff848e0e 100644
--- a/src/core/function/gpg/GpgKeyGetter.cpp
+++ b/src/core/function/gpg/GpgKeyGetter.cpp
@@ -44,16 +44,14 @@ GpgFrontend::GpgKeyGetter::GpgKeyGetter(int channel)
<< "channel:" << channel;
}
-GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetKey(const std::string& fpr) {
+GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetKey(const std::string& fpr,
+ bool use_cache) {
LOG(INFO) << "called";
// find in cache first
- {
- std::lock_guard<std::mutex> lock(keys_cache_mutex_);
- if (keys_cache_.find(fpr) != keys_cache_.end()) {
- std::lock_guard<std::mutex> lock(ctx_mutex_);
- return keys_cache_[fpr].Copy();
- }
+ if (use_cache) {
+ auto key = get_key_in_cache(fpr);
+ if (key.IsGood()) return key;
}
gpgme_key_t _p_key = nullptr;
@@ -66,15 +64,12 @@ GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetKey(const std::string& fpr) {
}
}
-GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetPubkey(
- const std::string& fpr) {
+GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::GetPubkey(const std::string& fpr,
+ bool use_cache) {
// find in cache first
- {
- std::lock_guard<std::mutex> lock(keys_cache_mutex_);
- if (keys_cache_.find(fpr) != keys_cache_.end()) {
- std::lock_guard<std::mutex> lock(ctx_mutex_);
- return keys_cache_[fpr].Copy();
- }
+ if (use_cache) {
+ auto key = get_key_in_cache(fpr);
+ if (key.IsGood()) return key;
}
gpgme_key_t _p_key = nullptr;
@@ -124,8 +119,18 @@ void GpgFrontend::GpgKeyGetter::FlushKeyCache() {
std::lock_guard<std::mutex> lock(keys_cache_mutex_);
gpgme_key_t key;
while ((err = gpgme_op_keylist_next(ctx_, &key)) == GPG_ERR_NO_ERROR) {
- LOG(INFO) << "LoadKey Fpr:" << key->fpr << "Id:" << key->subkeys->keyid;
- keys_cache_.insert({key->subkeys->keyid, GpgKey(std::move(key))});
+ auto gpg_key = GpgKey(std::move(key));
+
+ // detect if the key is in a smartcard
+ // if so, try to get full information using gpgme_get_key()
+ // this maybe a bug in gpgme
+ if (gpg_key.IsHasCardKey()) {
+ gpg_key = GetKey(gpg_key.GetId(), false);
+ }
+
+ LOG(INFO) << "LoadKey Fpr:" << gpg_key.GetFingerprint()
+ << "Id:" << gpg_key.GetId();
+ keys_cache_.insert({gpg_key.GetId(), std::move(gpg_key)});
}
}
@@ -143,7 +148,7 @@ void GpgFrontend::GpgKeyGetter::FlushKeyCache() {
GpgFrontend::KeyListPtr GpgFrontend::GpgKeyGetter::GetKeys(
const KeyIdArgsListPtr& ids) {
auto keys = std::make_unique<KeyArgsList>();
- for (const auto& id : *ids) keys->push_back(GetKey(id));
+ for (const auto& id : *ids) keys->emplace_back(GetKey(id));
return keys;
}
@@ -152,7 +157,7 @@ GpgFrontend::KeyLinkListPtr GpgFrontend::GpgKeyGetter::GetKeysCopy(
// get the lock
std::lock_guard<std::mutex> lock(ctx_mutex_);
auto keys_copy = std::make_unique<GpgKeyLinkList>();
- for (const auto& key : *keys) keys_copy->push_back(key.Copy());
+ for (const auto& key : *keys) keys_copy->emplace_back(key.Copy());
return keys_copy;
}
@@ -161,6 +166,18 @@ GpgFrontend::KeyListPtr GpgFrontend::GpgKeyGetter::GetKeysCopy(
// get the lock
std::lock_guard<std::mutex> lock(ctx_mutex_);
auto keys_copy = std::make_unique<KeyArgsList>();
- for (const auto& key : *keys) keys_copy->push_back(key.Copy());
+ for (const auto& key : *keys) keys_copy->emplace_back(key.Copy());
return keys_copy;
}
+
+GpgFrontend::GpgKey GpgFrontend::GpgKeyGetter::get_key_in_cache(
+ const std::string& id) {
+ std::lock_guard<std::mutex> lock(keys_cache_mutex_);
+ if (keys_cache_.find(id) != keys_cache_.end()) {
+ std::lock_guard<std::mutex> lock(ctx_mutex_);
+ // return a copy of the key in cache
+ return keys_cache_[id].Copy();
+ }
+ // return a bad key
+ return GpgKey();
+}
diff --git a/src/core/function/gpg/GpgKeyGetter.h b/src/core/function/gpg/GpgKeyGetter.h
index 72cd777c..c96dbea7 100644
--- a/src/core/function/gpg/GpgKeyGetter.h
+++ b/src/core/function/gpg/GpgKeyGetter.h
@@ -59,7 +59,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyGetter
* @param fpr
* @return GpgKey
*/
- GpgKey GetKey(const std::string& id);
+ GpgKey GetKey(const std::string& id, bool use_cache = true);
/**
* @brief Get the Keys object
@@ -75,7 +75,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyGetter
* @param fpr
* @return GpgKey
*/
- GpgKey GetPubkey(const std::string& id);
+ GpgKey GetPubkey(const std::string& id, bool use_cache = true);
/**
* @brief Get all the keys by receiving a linked list
@@ -108,7 +108,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyGetter
private:
/**
- * @brief
+ * @brief Get the gpgme context object
*
*/
GpgContext& ctx_ =
@@ -121,7 +121,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyGetter
mutable std::mutex ctx_mutex_;
/**
- * @brief cache the keys with key fpr
+ * @brief cache the keys with key id
*
*/
std::map<std::string, GpgKey> keys_cache_;
@@ -131,6 +131,14 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyGetter
*
*/
mutable std::mutex keys_cache_mutex_;
+
+ /**
+ * @brief Get the Key object
+ *
+ * @param id
+ * @return GpgKey
+ */
+ GpgKey get_key_in_cache(const std::string& id);
};
} // namespace GpgFrontend
diff --git a/src/core/thread/CtxCheckThread.cpp b/src/core/thread/CtxCheckTask.cpp
index edec8855..ee170fbc 100644
--- a/src/core/thread/CtxCheckThread.cpp
+++ b/src/core/thread/CtxCheckTask.cpp
@@ -24,20 +24,20 @@
*
*/
-#include "core/thread/CtxCheckThread.h"
+#include "core/thread/CtxCheckTask.h"
#include "core/GpgContext.h"
#include "core/GpgCoreInit.h"
#include "core/common/CoreCommonUtil.h"
#include "core/function/gpg/GpgKeyGetter.h"
-GpgFrontend::CtxCheckThread::CtxCheckThread() : QThread(nullptr) {
- connect(this, &CtxCheckThread::SignalGnupgNotInstall,
+GpgFrontend::Thread::CtxCheckTask::CtxCheckTask() {
+ connect(this, &CtxCheckTask::SignalGnupgNotInstall,
CoreCommonUtil::GetInstance(),
&CoreCommonUtil::SignalGnupgNotInstall);
}
-void GpgFrontend::CtxCheckThread::run() {
+void GpgFrontend::Thread::CtxCheckTask::Run() {
// init logging
init_logging();
diff --git a/src/core/thread/CtxCheckThread.h b/src/core/thread/CtxCheckTask.h
index c597141f..06ddfd82 100644
--- a/src/core/thread/CtxCheckThread.h
+++ b/src/core/thread/CtxCheckTask.h
@@ -28,20 +28,21 @@
#define GPGFRONTEND_CTXCHECKTRHEAD_H
#include "core/GpgFrontendCore.h"
+#include "core/thread/Task.h"
-namespace GpgFrontend {
+namespace GpgFrontend::Thread {
/**
* @brief
*
*/
-class GPGFRONTEND_CORE_EXPORT CtxCheckThread : public QThread {
+class GPGFRONTEND_CORE_EXPORT CtxCheckTask : public Task {
Q_OBJECT
public:
/**
* @brief Construct a new Ctx Check Thread object
*
*/
- CtxCheckThread();
+ CtxCheckTask();
signals:
/**
@@ -55,8 +56,8 @@ class GPGFRONTEND_CORE_EXPORT CtxCheckThread : public QThread {
* @brief
*
*/
- void run() override;
+ void Run() override;
};
-} // namespace GpgFrontend
+} // namespace GpgFrontend::Thread
#endif // GPGFRONTEND_CTXCHECKTRHEAD_H
diff --git a/src/core/thread/FileReadTask.cpp b/src/core/thread/FileReadTask.cpp
new file mode 100644
index 00000000..3a235390
--- /dev/null
+++ b/src/core/thread/FileReadTask.cpp
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "core/thread/FileReadTask.h"
+
+#include <utility>
+
+namespace GpgFrontend::UI {
+
+FileReadTask::FileReadTask(std::string path) {
+ connect(this, &FileReadTask::SignalFileBytesReadNext, this,
+ &FileReadTask::read_bytes);
+
+#ifdef WINDOWS
+ std::filesystem::path read_file_path(
+ QString::fromStdString(path).toStdU16String());
+#else
+ std::filesystem::path read_file_path(
+ QString::fromStdString(path).toStdString());
+#endif
+ read_file_path_ = read_file_path;
+}
+
+void FileReadTask::Run() {
+ SetFinishAfterRun(false);
+
+ if (is_regular_file(read_file_path_)) {
+ LOG(INFO) << "read open file" << read_file_path_;
+
+ target_file_.setFileName(
+ QString::fromStdString(read_file_path_.u8string()));
+ target_file_.open(QIODevice::ReadOnly);
+
+ if (!(target_file_.isOpen() && target_file_.isReadable())) {
+ LOG(ERROR) << "file not open or not readable";
+ if (target_file_.isOpen()) target_file_.close();
+ return;
+ }
+ LOG(INFO) << "started reading" << read_file_path_;
+ read_bytes();
+ } else {
+ emit SignalFileBytesReadEnd();
+ }
+}
+
+void FileReadTask::read_bytes() {
+ QByteArray read_buffer;
+ if (!target_file_.atEnd() &&
+ (read_buffer = target_file_.read(buffer_size_)).size() > 0) {
+ LOG(INFO) << "read bytes" << read_buffer.size();
+ emit SignalFileBytesRead(std::move(read_buffer));
+ } else {
+ LOG(INFO) << "read bytes end";
+ emit SignalFileBytesReadEnd();
+ // finish task
+ emit SignalTaskFinished();
+ }
+}
+
+FileReadTask::~FileReadTask() {
+ LOG(INFO) << "close file" << read_file_path_;
+ if (target_file_.isOpen()) target_file_.close();
+}
+
+} // namespace GpgFrontend::UI
diff --git a/src/ui/thread/FileReadThread.h b/src/core/thread/FileReadTask.h
index e7573af8..d4e61cbe 100644
--- a/src/ui/thread/FileReadThread.h
+++ b/src/core/thread/FileReadTask.h
@@ -27,7 +27,8 @@
#ifndef GPGFRONTEND_FILEREADTHREAD_H
#define GPGFRONTEND_FILEREADTHREAD_H
-#include "ui/GpgFrontendUI.h"
+#include "core/GpgFrontendCore.h"
+#include "core/thread/Task.h"
namespace GpgFrontend::UI {
@@ -35,41 +36,28 @@ namespace GpgFrontend::UI {
* @brief
*
*/
-class FileReadThread : public QThread {
+class GPGFRONTEND_CORE_EXPORT FileReadTask : public GpgFrontend::Thread::Task {
Q_OBJECT
-
public:
- /**
- * @brief Construct a new File Read Thread object
- *
- * @param path
- */
- explicit FileReadThread(std::string path);
-
- signals:
+ explicit FileReadTask(std::string path);
- /**
- * @brief
- *
- * @param block
- */
- void SignalSendReadBlock(const std::string& block);
+ virtual ~FileReadTask() override;
- /**
- * @brief
- *
- */
- void SignalReadDone();
+ void Run() override;
- protected:
- /**
- * @brief
- *
- */
- void run() override;
+ signals:
+ void SignalFileBytesRead(QByteArray bytes);
+ void SignalFileBytesReadEnd();
+ void SignalFileBytesReadNext();
private:
- std::string path_; ///<
+ std::filesystem::path read_file_path_;
+ QFile target_file_;
+ const size_t buffer_size_ = 4096;
+ QEventLoop looper;
+
+ private slots:
+ void read_bytes();
};
} // namespace GpgFrontend::UI
diff --git a/src/core/thread/Task.cpp b/src/core/thread/Task.cpp
new file mode 100644
index 00000000..9626ba69
--- /dev/null
+++ b/src/core/thread/Task.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "core/thread/Task.h"
+
+#include <functional>
+
+#include "core/thread/TaskRunner.h"
+
+GpgFrontend::Thread::Task::Task() { init(); }
+
+GpgFrontend::Thread::Task::Task(TaskCallback callback)
+ : callback_(std::move(callback)) {
+ init();
+}
+
+GpgFrontend::Thread::Task::Task(TaskRunnable runnable, TaskCallback callback)
+ : runnable_(runnable), callback_(std::move(callback)) {
+ init();
+}
+
+GpgFrontend::Thread::Task::~Task() = default;
+
+void GpgFrontend::Thread::Task::SetFinishAfterRun(bool finish_after_run) {
+ this->finish_after_run_ = finish_after_run;
+}
+
+void GpgFrontend::Thread::Task::SetRTN(int rtn) { this->rtn_ = rtn; }
+
+void GpgFrontend::Thread::Task::init() {
+ LOG(INFO) << "called";
+ connect(this, &Task::SignalTaskFinished, this, &Task::before_finish_task);
+ connect(this, &Task::SignalTaskFinished, this, &Task::deleteLater);
+}
+
+void GpgFrontend::Thread::Task::before_finish_task() {
+ LOG(INFO) << "called";
+ if (callback_) callback_(rtn_);
+}
+
+void GpgFrontend::Thread::Task::run() {
+ LOG(INFO) << "called";
+ Run();
+ if (finish_after_run_) emit SignalTaskFinished();
+}
+
+void GpgFrontend::Thread::Task::Run() {
+ if (runnable_) {
+ rtn_ = runnable_();
+ }
+}
diff --git a/src/core/thread/Task.h b/src/core/thread/Task.h
new file mode 100644
index 00000000..4b536176
--- /dev/null
+++ b/src/core/thread/Task.h
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_TASK_H
+#define GPGFRONTEND_TASK_H
+
+#include <functional>
+
+#include "core/GpgFrontendCore.h"
+
+namespace GpgFrontend::Thread {
+
+class TaskRunner;
+
+class GPGFRONTEND_CORE_EXPORT Task : public QObject, public QRunnable {
+ Q_OBJECT
+ public:
+ using TaskRunnable = std::function<int()>; ///<
+ using TaskCallback = std::function<void(int)>; ///<
+ friend class TaskRunner;
+
+ /**
+ * @brief Construct a new Task object
+ *
+ */
+ Task();
+
+ /**
+ * @brief Construct a new Task object
+ *
+ * @param callback The callback function to be executed.
+ * callback must not be nullptr, and not tp opreate UI object.
+ */
+ Task(TaskCallback callback);
+
+ /**
+ * @brief Construct a new Task object
+ *
+ * @param runnable
+ */
+ Task(
+ TaskRunnable runnable, TaskCallback callback = [](int) {});
+
+ /**
+ * @brief Destroy the Task object
+ *
+ */
+ virtual ~Task() override;
+
+ /**
+ * @brief Run - run the task
+ *
+ */
+ virtual void Run();
+
+ signals:
+ void SignalTaskFinished();
+
+ protected:
+ void SetFinishAfterRun(bool finish_after_run);
+
+ void SetRTN(int rtn);
+
+ private:
+ TaskCallback callback_; ///<
+ TaskRunnable runnable_; ///<
+ bool finish_after_run_ = true; ///<
+ int rtn_ = 0; ///<
+
+ /**
+ * @brief
+ *
+ */
+ void before_finish_task();
+
+ /**
+ * @brief
+ *
+ */
+ void init();
+
+ /**
+ * @brief
+ *
+ */
+ virtual void run() override;
+};
+} // namespace GpgFrontend::Thread
+
+#endif // GPGFRONTEND_TASK_H \ No newline at end of file
diff --git a/src/core/thread/TaskRunner.cpp b/src/core/thread/TaskRunner.cpp
new file mode 100644
index 00000000..2223bdda
--- /dev/null
+++ b/src/core/thread/TaskRunner.cpp
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "core/thread/TaskRunner.h"
+
+#include "core/thread/Task.h"
+#include "easylogging++.h"
+
+GpgFrontend::Thread::TaskRunner::TaskRunner() = default;
+
+GpgFrontend::Thread::TaskRunner::~TaskRunner() = default;
+
+void GpgFrontend::Thread::TaskRunner::PostTask(Task* task) {
+ LOG(INFO) << "called";
+ if (task == nullptr) return;
+ task->setParent(nullptr);
+ task->moveToThread(this);
+ {
+ std::lock_guard<std::mutex> lock(tasks_mutex_);
+ tasks.push(task);
+ }
+ quit();
+}
+
+void GpgFrontend::Thread::TaskRunner::run() {
+ LOG(INFO) << "called";
+ while (true) {
+ if (tasks.empty()) {
+ LOG(INFO) << "TaskRunner: No tasks to run";
+ exec();
+ } else {
+ LOG(INFO) << "TaskRunner: Running task, queue size:" << tasks.size();
+
+ Task* task = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(tasks_mutex_);
+ task = std::move(tasks.front());
+ tasks.pop();
+ }
+ if (task != nullptr) task->run();
+ }
+ }
+}
diff --git a/src/core/thread/TaskRunner.h b/src/core/thread/TaskRunner.h
new file mode 100644
index 00000000..14eaeae7
--- /dev/null
+++ b/src/core/thread/TaskRunner.h
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_TASKRUNNER_H
+#define GPGFRONTEND_TASKRUNNER_H
+
+#include <mutex>
+#include <queue>
+
+#include "core/GpgFrontendCore.h"
+
+namespace GpgFrontend::Thread {
+
+class Task;
+
+class GPGFRONTEND_CORE_EXPORT TaskRunner : public QThread {
+ Q_OBJECT
+ public:
+ /**
+ * @brief Construct a new Task Runner object
+ *
+ */
+ TaskRunner();
+
+ /**
+ * @brief Destroy the Task Runner object
+ *
+ */
+ virtual ~TaskRunner() override;
+
+ /**
+ * @brief
+ *
+ */
+ void run() override;
+
+ public slots:
+
+ /**
+ * @brief
+ *
+ * @param task
+ */
+ void PostTask(Task* task);
+
+ private:
+ std::queue<Task*> tasks; ///< The task queue
+ std::mutex tasks_mutex_; ///< The task queue mutex
+};
+} // namespace GpgFrontend::Thread
+
+#endif // GPGFRONTEND_TASKRUNNER_H \ No newline at end of file
diff --git a/src/core/thread/TaskRunnerGetter.cpp b/src/core/thread/TaskRunnerGetter.cpp
new file mode 100644
index 00000000..186483ec
--- /dev/null
+++ b/src/core/thread/TaskRunnerGetter.cpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#include "core/thread/TaskRunnerGetter.h"
+
+GpgFrontend::Thread::TaskRunnerGetter::TaskRunnerGetter(int channel)
+ : SingletonFunctionObject<TaskRunnerGetter>(channel) {}
+
+GpgFrontend::Thread::TaskRunner*
+GpgFrontend::Thread::TaskRunnerGetter::GetTaskRunner(
+ TaskRunnerType runner_type) {
+ while (true) {
+ auto it = task_runners_.find(runner_type);
+ if (it != task_runners_.end()) {
+ return it->second;
+ } else {
+ auto runner = new TaskRunner();
+ task_runners_[runner_type] = runner;
+ runner->start();
+ continue;
+ }
+ }
+}
diff --git a/src/core/thread/TaskRunnerGetter.h b/src/core/thread/TaskRunnerGetter.h
new file mode 100644
index 00000000..722484b5
--- /dev/null
+++ b/src/core/thread/TaskRunnerGetter.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2021 Saturneric
+ *
+ * This file is part of GpgFrontend.
+ *
+ * GpgFrontend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GpgFrontend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * The initial version of the source code is inherited from
+ * the gpg4usb project, which is under GPL-3.0-or-later.
+ *
+ * The source code version of this software was modified and released
+ * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
+ *
+ */
+
+#ifndef GPGFRONTEND_TASKRUNNERGETTER_H
+#define GPGFRONTEND_TASKRUNNERGETTER_H
+
+#include "core/GpgFrontendCore.h"
+#include "core/GpgFunctionObject.h"
+#include "core/thread/TaskRunner.h"
+
+namespace GpgFrontend::Thread {
+
+class GPGFRONTEND_CORE_EXPORT TaskRunnerGetter
+ : public GpgFrontend::SingletonFunctionObject<TaskRunnerGetter> {
+ public:
+ enum TaskRunnerType {
+ kTaskRunnerType_Default,
+ kTaskRunnerType_GPG,
+ kTaskRunnerType_IO,
+ };
+
+ TaskRunnerGetter(int channel = SingletonFunctionObject::GetDefaultChannel());
+
+ TaskRunner *GetTaskRunner(
+ TaskRunnerType runner_type = kTaskRunnerType_Default);
+
+ private:
+ std::map<TaskRunnerType, TaskRunner *> task_runners_;
+};
+
+} // namespace GpgFrontend::Thread
+
+#endif // GPGFRONTEND_TASKRUNNERGETTER_H \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 24f2b7fa..fd20a664 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -36,7 +36,6 @@
#include "GpgFrontendBuildInfo.h"
#include "core/GpgFunctionObject.h"
-#include "core/thread/CtxCheckThread.h"
#include "ui/GpgFrontendUIInit.h"
#include "ui/main_window/MainWindow.h"
diff --git a/src/ui/GpgFrontendUIInit.cpp b/src/ui/GpgFrontendUIInit.cpp
index 2a8ac8b4..e7751ee6 100644
--- a/src/ui/GpgFrontendUIInit.cpp
+++ b/src/ui/GpgFrontendUIInit.cpp
@@ -29,7 +29,8 @@
#include "GpgFrontendUIInit.h"
#include "core/function/GlobalSettingStation.h"
-#include "core/thread/CtxCheckThread.h"
+#include "core/thread/CtxCheckTask.h"
+#include "core/thread/TaskRunnerGetter.h"
#include "ui/SignalStation.h"
#include "ui/UserInterfaceUtils.h"
#include "ui/main_window/MainWindow.h"
@@ -49,9 +50,7 @@ void InitGpgFrontendUI() {
CommonUtils::GetInstance();
// create the thread to load the gpg context
- auto* init_ctx_thread = new GpgFrontend::CtxCheckThread();
- QApplication::connect(init_ctx_thread, &QThread::finished, init_ctx_thread,
- &QThread::deleteLater);
+ auto* init_ctx_task = new Thread::CtxCheckTask();
// create and show loading window before starting the main window
auto* waiting_dialog = new QProgressDialog();
@@ -66,27 +65,37 @@ void InitGpgFrontendUI() {
waiting_dialog_label->setWordWrap(true);
waiting_dialog->setLabel(waiting_dialog_label);
waiting_dialog->resize(420, 120);
- QApplication::connect(init_ctx_thread, &QThread::finished, [=]() {
- waiting_dialog->finished(0);
- waiting_dialog->deleteLater();
- });
+ QApplication::connect(init_ctx_task,
+ &Thread::CtxCheckTask::SignalTaskFinished,
+ waiting_dialog, [=]() {
+ LOG(INFO) << "Gpg context loaded";
+ waiting_dialog->finished(0);
+ waiting_dialog->deleteLater();
+ });
+
QApplication::connect(waiting_dialog, &QProgressDialog::canceled, [=]() {
LOG(INFO) << "cancel clicked";
- if (init_ctx_thread->isRunning()) init_ctx_thread->terminate();
QCoreApplication::quit();
exit(0);
});
// show the loading window
+ waiting_dialog->setModal(true);
waiting_dialog->show();
waiting_dialog->setFocus();
- // start the thread to load the gpg context
- init_ctx_thread->start();
- QEventLoop loop;
- QApplication::connect(init_ctx_thread, &QThread::finished, &loop,
+ // new local event looper
+ QEventLoop looper;
+ QApplication::connect(init_ctx_task,
+ &Thread::CtxCheckTask::SignalTaskFinished, &looper,
&QEventLoop::quit);
- loop.exec();
+
+ // start the thread to load the gpg context
+ Thread::TaskRunnerGetter::GetInstance().GetTaskRunner()->PostTask(
+ init_ctx_task);
+
+ // block the main thread until the gpg context is loaded
+ looper.exec();
}
int RunGpgFrontendUI() {
diff --git a/src/ui/UserInterfaceUtils.cpp b/src/ui/UserInterfaceUtils.cpp
index 724a467e..65c55c8f 100644
--- a/src/ui/UserInterfaceUtils.cpp
+++ b/src/ui/UserInterfaceUtils.cpp
@@ -35,9 +35,13 @@
#include "core/function/FileOperator.h"
#include "core/function/GlobalSettingStation.h"
#include "core/function/gpg/GpgKeyGetter.h"
+#include "core/thread/Task.h"
+#include "core/thread/TaskRunner.h"
+#include "core/thread/TaskRunnerGetter.h"
#include "easylogging++.h"
#include "ui/SignalStation.h"
#include "ui/dialog/WaitingDialog.h"
+#include "ui/struct/SettingsObject.h"
#include "ui/widgets/TextEdit.h"
namespace GpgFrontend::UI {
@@ -111,17 +115,21 @@ void process_result_analyse(TextEdit *edit, InfoBoardWidget *info_board,
void process_operation(QWidget *parent, const std::string &waiting_title,
const std::function<void()> &func) {
+ auto *dialog =
+ new WaitingDialog(QString::fromStdString(waiting_title), parent);
+
auto thread = QThread::create(func);
QApplication::connect(thread, &QThread::finished, thread,
&QThread::deleteLater);
- thread->start();
+ QApplication::connect(thread, &QThread::finished, dialog, &QDialog::close);
+ QApplication::connect(thread, &QThread::finished, dialog,
+ &QDialog::deleteLater);
- auto *dialog =
- new WaitingDialog(QString::fromStdString(waiting_title), parent);
- while (thread->isRunning()) {
- QApplication::processEvents();
- }
- dialog->close();
+ QEventLoop looper;
+ QApplication::connect(dialog, &QDialog::finished, &looper, &QEventLoop::quit);
+
+ thread->start();
+ looper.exec();
}
CommonUtils *CommonUtils::GetInstance() {
@@ -245,11 +253,23 @@ void CommonUtils::SlotExecuteGpgCommand(
void CommonUtils::SlotImportKeyFromKeyServer(
const KeyIdArgsList &key_ids, const ImportCallbackFunctiopn &callback) {
std::string target_keyserver;
+
if (target_keyserver.empty()) {
try {
auto &settings = GlobalSettingStation::GetInstance().GetUISettings();
+ SettingsObject key_server_json("key_server");
+
+ // get key servers from settings
+ const auto key_server_list =
+ key_server_json.Check("server_list", nlohmann::json::array());
+ if (key_server_list.empty()) {
+ throw std::runtime_error("No key server configured");
+ }
- target_keyserver = settings.lookup("keyserver.default_server").c_str();
+ const int target_key_server_index =
+ key_server_json.Check("default_server", 0);
+ target_keyserver =
+ key_server_list[target_key_server_index].get<std::string>();
LOG(INFO) << _("Set target Key Server to default Key Server")
<< target_keyserver;
@@ -323,39 +343,26 @@ void CommonUtils::SlotImportKeyFromKeyServer(
current_index++;
}
});
-
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonUtils::slot_update_key_status() {
LOG(INFO) << "called";
- auto *thread = QThread::create([this]() {
- std::vector<QThread *> threads;
+ auto refresh_task = new Thread::Task([]() -> int {
// flush key cache for all GpgKeyGetter Intances.
for (const auto &channel_id : GpgKeyGetter::GetAllChannelId()) {
- // multi threading
- auto *refresh_thread = QThread::create([channel_id]() {
- LOG(INFO) << "FlushKeyCache thread start"
- << "channel:" << channel_id;
- GpgKeyGetter::GetInstance(channel_id).FlushKeyCache();
- });
- refresh_thread->start();
- threads.push_back(refresh_thread);
+ GpgKeyGetter::GetInstance(channel_id).FlushKeyCache();
}
-
- for (auto *thread : threads) {
- thread->wait();
- thread->deleteLater();
- }
-
- emit SignalKeyDatabaseRefreshDone();
- LOG(INFO) << "finished";
+ return 0;
});
- connect(thread, &QThread::finished, thread, &QThread::deleteLater);
- LOG(INFO) << "start thread";
- thread->start();
+ connect(refresh_task, &Thread::Task::SignalTaskFinished, this,
+ &CommonUtils::SignalKeyDatabaseRefreshDone);
+
+ // post the task to the default task runner
+ Thread::TaskRunnerGetter::GetInstance().GetTaskRunner()->PostTask(
+ refresh_task);
}
} // namespace GpgFrontend::UI \ No newline at end of file
diff --git a/src/ui/keypair_details/KeyPairOperaTab.cpp b/src/ui/keypair_details/KeyPairOperaTab.cpp
index 988ce527..4f7cd66f 100644
--- a/src/ui/keypair_details/KeyPairOperaTab.cpp
+++ b/src/ui/keypair_details/KeyPairOperaTab.cpp
@@ -145,8 +145,12 @@ void KeyPairOperaTab::slot_export_public_key() {
_("An error occurred during the export operation."));
return;
}
- auto file_string = m_key_.GetName() + " " + m_key_.GetEmail() + "(" +
+
+ // generate a file name
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
m_key_.GetId() + ")_pub.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
auto file_name =
QFileDialog::getSaveFileName(
this, _("Export Key To File"), QString::fromStdString(file_string),
@@ -188,8 +192,11 @@ void KeyPairOperaTab::slot_export_short_private_key() {
_("An error occurred during the export operation."));
return;
}
- auto file_string = m_key_.GetName() + " " + m_key_.GetEmail() + "(" +
+
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
m_key_.GetId() + ")_short_secret.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
auto file_name =
QFileDialog::getSaveFileName(
this, _("Export Key To File"), QString::fromStdString(file_string),
@@ -228,8 +235,10 @@ void KeyPairOperaTab::slot_export_private_key() {
_("An error occurred during the export operation."));
return;
}
- auto file_string = m_key_.GetName() + " " + m_key_.GetEmail() + "(" +
+ auto file_string = m_key_.GetName() + "<" + m_key_.GetEmail() + ">(" +
m_key_.GetId() + ")_full_secret.asc";
+ std::replace(file_string.begin(), file_string.end(), ' ', '_');
+
auto file_name =
QFileDialog::getSaveFileName(
this, _("Export Key To File"), QString::fromStdString(file_string),
diff --git a/src/ui/settings/SettingsKeyServer.cpp b/src/ui/settings/SettingsKeyServer.cpp
index f2eaf9a9..e80e4598 100644
--- a/src/ui/settings/SettingsKeyServer.cpp
+++ b/src/ui/settings/SettingsKeyServer.cpp
@@ -225,7 +225,7 @@ void KeyserverTab::slot_refresh_table() {
void KeyserverTab::slot_test_listed_key_server() {
auto timeout =
QInputDialog::getInt(this, _("Set TCP Timeout"), tr("timeout(ms): "),
- QLineEdit::Normal, 500, 2000);
+ QLineEdit::Normal, 800, 3000);
QStringList urls;
const auto row_size = ui_->keyServerListTable->rowCount();
diff --git a/src/ui/thread/FileReadThread.cpp b/src/ui/thread/FileReadThread.cpp
deleted file mode 100644
index 83731c8a..00000000
--- a/src/ui/thread/FileReadThread.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (C) 2021 Saturneric
- *
- * This file is part of GpgFrontend.
- *
- * GpgFrontend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GpgFrontend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
- *
- * The initial version of the source code is inherited from
- * the gpg4usb project, which is under GPL-3.0-or-later.
- *
- * The source code version of this software was modified and released
- * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021.
- *
- */
-
-#include "FileReadThread.h"
-
-
-#include <utility>
-
-namespace GpgFrontend::UI {
-
-FileReadThread::FileReadThread(std::string path) : path_(std::move(path)) {
- qRegisterMetaType<std::string>("std::string");
-}
-
-void FileReadThread::run() {
- LOG(INFO) << "started reading" << path_;
-
-#ifdef WINDOWS
- std::filesystem::path read_file_path(QString::fromStdString(path_).toStdU16String());
-#else
- std::filesystem::path read_file_path(QString::fromStdString(path_).toStdString());
-#endif
-
- if (is_regular_file(read_file_path)) {
- LOG(INFO) << "read open" << read_file_path;
-
- QFile target_file;
- target_file.setFileName(QString::fromStdString(read_file_path.u8string()));
- target_file.open(QIODevice::ReadOnly);
- QByteArray read_buffer;
- LOG(INFO) << "thread start reading";
-
- const size_t buffer_size = 4096;
- if(!(target_file.isOpen() && target_file.isReadable())) {
- LOG(ERROR) << "file not open or not readable";
- if(target_file.isOpen())
- target_file.close();
- return;
- }
-
- while (!target_file.atEnd() && (read_buffer = target_file.read(buffer_size)).size() > 0) {
- // Check isInterruptionRequested
- if (QThread::currentThread()->isInterruptionRequested()) {
- LOG(INFO) << "thread is interruption requested ";
- target_file.close();
- return;
- }
- LOG(INFO) << "block size " << read_buffer.size();
- std::string buffer_str(read_buffer.toStdString());
-
- emit SignalSendReadBlock(buffer_str);
-#ifdef RELEASE
- QThread::msleep(32);
-#else
- QThread::msleep(128);
-#endif
- }
- target_file.close();
- emit SignalReadDone();
- LOG(INFO) << "thread end reading";
- }
-}
-
-} // namespace GpgFrontend::UI
diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp
index 1047be75..fe188e93 100644
--- a/src/ui/widgets/FilePage.cpp
+++ b/src/ui/widgets/FilePage.cpp
@@ -118,29 +118,30 @@ void FilePage::slot_file_tree_view_item_clicked(const QModelIndex& index) {
QFileInfo info(QString::fromStdString(selected_path_.u8string()));
if ((info.isDir() || info.isFile()) &&
- (info.suffix() != "gpg" && info.suffix() != "sig" &&
- info.suffix() != "asc")) {
+ (info.suffix() != "gpg" && info.suffix() != "pgp" &&
+ info.suffix() != "sig" && info.suffix() != "asc")) {
operation_type |= MainWindow::CryptoMenu::Encrypt;
}
if ((info.isDir() || info.isFile()) &&
- (info.suffix() != "gpg" && info.suffix() != "sig" &&
- info.suffix() != "asc")) {
+ (info.suffix() != "gpg" && info.suffix() != "pgp" &&
+ info.suffix() != "sig" && info.suffix() != "asc")) {
operation_type |= MainWindow::CryptoMenu::EncryptAndSign;
}
- if (info.isFile() && (info.suffix() == "gpg" || info.suffix() == "asc")) {
+ if (info.isFile() && (info.suffix() == "gpg" || info.suffix() == "pgp" ||
+ info.suffix() == "asc")) {
operation_type |= MainWindow::CryptoMenu::Decrypt;
operation_type |= MainWindow::CryptoMenu::DecryptAndVerify;
}
- if (info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig" &&
- info.suffix() != "asc")) {
+ if (info.isFile() && (info.suffix() != "gpg" && info.suffix() != "pgp" &&
+ info.suffix() != "sig" && info.suffix() != "asc")) {
operation_type |= MainWindow::CryptoMenu::Sign;
}
if (info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg" ||
- info.suffix() == "asc")) {
+ info.suffix() == "pgp" || info.suffix() == "asc")) {
operation_type |= MainWindow::CryptoMenu::Verify;
}
}
@@ -155,8 +156,10 @@ void FilePage::slot_up_level() {
auto str_path =
dir_model_->fileInfo(currentRoot).absoluteFilePath().toStdU16String();
#else
- auto str_path =
- dir_model_->fileInfo(currentRoot).absoluteFilePath().toUtf8().toStdString();
+ auto str_path = dir_model_->fileInfo(currentRoot)
+ .absoluteFilePath()
+ .toUtf8()
+ .toStdString();
#endif
std::filesystem::path path_obj(str_path);
@@ -291,9 +294,11 @@ void FilePage::onCustomContextMenu(const QPoint& point) {
LOG(INFO) << "right click" << selected_path_.u8string();
#ifdef WINDOWS
- auto index_dir_str = dir_model_->fileInfo(index).absoluteFilePath().toStdU16String();
+ auto index_dir_str =
+ dir_model_->fileInfo(index).absoluteFilePath().toStdU16String();
#else
- auto index_dir_str = dir_model_->fileInfo(index).absoluteFilePath().toStdString();
+ auto index_dir_str =
+ dir_model_->fileInfo(index).absoluteFilePath().toStdString();
#endif
selected_path_ = std::filesystem::path(index_dir_str);
@@ -350,9 +355,9 @@ void FilePage::slot_rename_item() {
new_name_path = new_name_path.remove_filename();
bool ok;
- auto text =
- QInputDialog::getText(this, _("Rename"), _("New Filename"),
- QLineEdit::Normal, QString::fromStdString(old_name.u8string()), &ok);
+ auto text = QInputDialog::getText(
+ this, _("Rename"), _("New Filename"), QLineEdit::Normal,
+ QString::fromStdString(old_name.u8string()), &ok);
if (ok && !text.isEmpty()) {
try {
#ifdef WINDOWS
diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp
index 237576ad..0bd65f25 100644
--- a/src/ui/widgets/KeyList.cpp
+++ b/src/ui/widgets/KeyList.cpp
@@ -66,8 +66,11 @@ void KeyList::init() {
connect(SignalStation::GetInstance(),
&SignalStation::SignalKeyDatabaseRefreshDone, this,
&KeyList::SlotRefresh);
+
+ // register key database sync signal for refresh button
connect(ui_->refreshKeyListButton, &QPushButton::clicked, this,
- &KeyList::SlotRefresh);
+ &KeyList::SignalRefreshDatabase);
+
connect(ui_->uncheckButton, &QPushButton::clicked, this,
&KeyList::uncheck_all);
connect(ui_->checkALLButton, &QPushButton::clicked, this,
@@ -161,7 +164,6 @@ void KeyList::SlotRefresh() {
ui_->syncButton->setDisabled(true);
emit SignalRefreshStatusBar(_("Refreshing Key List..."), 3000);
-
this->buffered_keys_list_ = GpgKeyGetter::GetInstance().FetchKey();
this->slot_refresh_ui();
}
@@ -460,7 +462,7 @@ void KeyList::slot_sync_with_key_server() {
ui_->syncButton->setDisabled(false);
ui_->refreshKeyListButton->setDisabled(false);
emit SignalRefreshStatusBar(_("Key List Sync Done."), 3000);
- emit SignalRefreshDatabase();
+ emit this->SignalRefreshDatabase();
}
});
}
@@ -498,8 +500,9 @@ KeyIdArgsListPtr& KeyTable::GetChecked() {
if (checked_key_ids_ == nullptr)
checked_key_ids_ = std::make_unique<KeyIdArgsList>();
auto& ret = checked_key_ids_;
- for (int i = 0; i < key_list_->rowCount(); i++) {
+ for (int i = 0; i < buffered_keys_.size(); i++) {
auto key_id = buffered_keys_[i].GetId();
+ LOG(INFO) << "i: " << i << " key_id: " << key_id;
if (key_list_->item(i, 0)->checkState() == Qt::Checked &&
std::find(ret->begin(), ret->end(), key_id) == ret->end()) {
ret->push_back(key_id);
@@ -514,7 +517,7 @@ void KeyTable::SetChecked(KeyIdArgsListPtr key_ids) {
}
void KeyTable::Refresh(KeyLinkListPtr m_keys) {
- LOG(INFO) << "Called";
+ LOG(INFO) << "called";
auto& checked_key_list = GetChecked();
// while filling the table, sort enabled causes errors
@@ -529,16 +532,19 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) {
else
keys = std::move(m_keys);
+ LOG(INFO) << "keys size: " << keys->size();
auto it = keys->begin();
int row_count = 0;
while (it != keys->end()) {
+ LOG(INFO) << "filtering key id: " << it->GetId();
if (filter_ != nullptr) {
if (!filter_(*it)) {
it = keys->erase(it);
continue;
}
}
+ LOG(INFO) << "adding key id: " << it->GetId();
if (select_type_ == KeyListRow::ONLY_SECRET_KEY && !it->IsPrivateKey()) {
it = keys->erase(it);
continue;
@@ -547,18 +553,15 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) {
it++;
}
+ LOG(INFO) << "row_count: " << row_count;
key_list_->setRowCount(row_count);
int row_index = 0;
it = keys->begin();
- auto& table_buffered_keys = buffered_keys_;
-
- table_buffered_keys.clear();
+ buffered_keys_.clear();
while (it != keys->end()) {
- table_buffered_keys.push_back(it->Copy());
-
auto* tmp0 = new QTableWidgetItem(QString::number(row_index));
tmp0->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled |
Qt::ItemIsSelectable);
@@ -624,6 +627,12 @@ void KeyTable::Refresh(KeyLinkListPtr m_keys) {
tmp2->setFont(strike);
tmp3->setFont(strike);
}
+
+ LOG(INFO) << "key id: " << it->GetId() << "added into key_list_:" << this;
+
+ // move to buffered keys
+ buffered_keys_.emplace_back(std::move(*it));
+
it++;
++row_index;
}
diff --git a/src/ui/widgets/PlainTextEditorPage.cpp b/src/ui/widgets/PlainTextEditorPage.cpp
index c70991b9..7eb682cc 100644
--- a/src/ui/widgets/PlainTextEditorPage.cpp
+++ b/src/ui/widgets/PlainTextEditorPage.cpp
@@ -24,14 +24,16 @@
*
*/
-#include "ui/widgets/PlainTextEditorPage.h"
-
-#include <encoding-detect/TextEncodingDetect.h>
+#include "PlainTextEditorPage.h"
#include <boost/format.hpp>
+#include <string>
#include <utility>
-#include "ui/thread/FileReadThread.h"
+#include "core/function/CharsetOperator.h"
+#include "core/thread/FileReadTask.h"
+#include "core/thread/Task.h"
+#include "core/thread/TaskRunnerGetter.h"
#include "ui_PlainTextEditor.h"
namespace GpgFrontend::UI {
@@ -42,8 +44,6 @@ PlainTextEditorPage::PlainTextEditorPage(QString file_path, QWidget *parent)
full_file_path_(std::move(file_path)) {
ui_->setupUi(this);
- if (full_file_path_.isEmpty()) read_done_ = true;
-
ui_->textPage->setFocus();
ui_->loadingLabel->setHidden(true);
@@ -52,21 +52,26 @@ PlainTextEditorPage::PlainTextEditorPage(QString file_path, QWidget *parent)
this->setAttribute(Qt::WA_DeleteOnClose);
this->ui_->characterLabel->setText(_("0 character"));
- this->ui_->lfLabel->setText(_("None"));
- this->ui_->encodingLabel->setText(_("Binary"));
+ this->ui_->lfLabel->setText(_("lf"));
+ this->ui_->encodingLabel->setText(_("utf-8"));
connect(ui_->textPage, &QPlainTextEdit::textChanged, this, [=]() {
+ // if file is loading
if (!read_done_) return;
auto text = ui_->textPage->document()->toPlainText();
auto str = boost::format(_("%1% character(s)")) % text.size();
this->ui_->characterLabel->setText(str.str().c_str());
-
- detect_cr_lf(text);
- detect_encoding(text.toStdString());
});
- ui_->loadingLabel->setText(_("Loading..."));
+ if (full_file_path_.isEmpty()) {
+ read_done_ = true;
+ ui_->loadingLabel->setHidden(true);
+ } else {
+ read_done_ = false;
+ ui_->loadingLabel->setText(_("Loading..."));
+ ui_->loadingLabel->setHidden(false);
+ }
}
const QString &PlainTextEditorPage::GetFilePath() const {
@@ -75,6 +80,26 @@ const QString &PlainTextEditorPage::GetFilePath() const {
QPlainTextEdit *PlainTextEditorPage::GetTextPage() { return ui_->textPage; }
+bool PlainTextEditorPage::WillCharsetChange() const {
+ // detect if the line-ending will change
+ if (is_crlf_) return true;
+
+ // detect if the charset of the file will change
+ if (charset_name_ != "UTF-8" && charset_name_ != "ISO-8859-1")
+ return true;
+ else
+ return false;
+}
+
+void PlainTextEditorPage::NotifyFileSaved() {
+ this->is_crlf_ = false;
+ this->charset_confidence_ = 100;
+ this->charset_name_ = "UTF-8";
+
+ this->ui_->lfLabel->setText(_("lf"));
+ this->ui_->encodingLabel->setText(_("UTF-8"));
+}
+
void PlainTextEditorPage::SetFilePath(const QString &filePath) {
full_file_path_ = filePath;
}
@@ -140,37 +165,34 @@ void PlainTextEditorPage::ReadFile() {
auto text_page = this->GetTextPage();
text_page->setReadOnly(true);
- auto thread = new FileReadThread(this->full_file_path_.toStdString());
-
- connect(thread, &FileReadThread::SignalSendReadBlock, this,
- &PlainTextEditorPage::slot_insert_text);
-
- connect(thread, &FileReadThread::SignalReadDone, this, [=]() {
- LOG(INFO) << "thread read done";
- if (!binary_mode_) {
- text_page->setReadOnly(false);
- }
- });
- connect(thread, &FileReadThread::finished, this, [=]() {
- LOG(INFO) << "thread finished";
- thread->deleteLater();
- read_done_ = true;
- read_thread_ = nullptr;
- ui_->textPage->setEnabled(true);
+ const auto target_path = this->full_file_path_.toStdString();
+
+ auto *task_runner =
+ GpgFrontend::Thread::TaskRunnerGetter::GetInstance().GetTaskRunner();
+
+ auto *read_task = new FileReadTask(target_path);
+ connect(read_task, &FileReadTask::SignalFileBytesRead, this,
+ &PlainTextEditorPage::slot_insert_text, Qt::QueuedConnection);
+ connect(this, &PlainTextEditorPage::SignalUIBytesDisplayed, read_task,
+ &FileReadTask::SignalFileBytesReadNext, Qt::QueuedConnection);
+
+ connect(read_task, &FileReadTask::SignalTaskFinished, this,
+ []() { LOG(INFO) << "read thread closed"; });
+ connect(this, &PlainTextEditorPage::close, read_task,
+ &FileReadTask::SignalTaskFinished);
+ connect(read_task, &FileReadTask::SignalFileBytesReadEnd, this, [=]() {
+ // set the UI
+ if (!binary_mode_) text_page->setReadOnly(false);
+ this->read_done_ = true;
+ this->ui_->textPage->setEnabled(true);
text_page->document()->setModified(false);
- ui_->textPage->blockSignals(false);
- ui_->textPage->document()->blockSignals(false);
- ui_->loadingLabel->setHidden(true);
+ this->ui_->textPage->blockSignals(false);
+ this->ui_->textPage->document()->blockSignals(false);
+ this->ui_->loadingLabel->setHidden(true);
});
- connect(this, &PlainTextEditorPage::destroyed, [=]() {
- LOG(INFO) << "request interruption for read thread";
- if (read_thread_ && thread->isRunning()) thread->requestInterruption();
- read_thread_ = nullptr;
- });
- this->read_thread_ = thread;
- thread->start();
+ task_runner->PostTask(read_task);
}
std::string binary_to_string(const std::string &source) {
@@ -181,17 +203,19 @@ std::string binary_to_string(const std::string &source) {
return ss.str();
}
-void PlainTextEditorPage::slot_insert_text(const std::string &data) {
+void PlainTextEditorPage::slot_insert_text(QByteArray bytes_data) {
+ std::string data = bytes_data.toStdString();
LOG(INFO) << "data size" << data.size();
read_bytes_ += data.size();
- // If binary format is detected, the entire file is converted to binary format
- // for display
+ // If binary format is detected, the entire file is converted to binary
+ // format for display.
bool if_last_binary_mode = binary_mode_;
- if (!binary_mode_) {
+ if (!binary_mode_ && !read_done_) {
detect_encoding(data);
}
if (binary_mode_) {
+ // change formery displayed text to binary format
if (if_last_binary_mode != binary_mode_) {
auto text_buffer =
ui_->textPage->document()->toRawText().toLocal8Bit().toStdString();
@@ -200,60 +224,75 @@ void PlainTextEditorPage::slot_insert_text(const std::string &data) {
binary_to_string(text_buffer).c_str());
this->ui_->lfLabel->setText("None");
}
+
+ // insert new data
this->GetTextPage()->insertPlainText(binary_to_string(data).c_str());
+ // update the size of the file
auto str = boost::format(_("%1% byte(s)")) % read_bytes_;
this->ui_->characterLabel->setText(str.str().c_str());
} else {
- this->GetTextPage()->insertPlainText(data.c_str());
+ // detect crlf/lf line ending
+ detect_cr_lf(data);
+
+ // when reding from a text file
+ // try convert the any of thetext to utf8
+ std::string utf8_data;
+ if (!read_done_ && charset_confidence_ > 25) {
+ CharsetOperator::Convert2Utf8(data, utf8_data, charset_name_);
+ } else {
+ // when editing a text file, do nothing.
+ utf8_data = data;
+ }
+
+ // insert the text to the text page
+ this->GetTextPage()->insertPlainText(utf8_data.c_str());
auto text = this->GetTextPage()->toPlainText();
auto str = boost::format(_("%1% character(s)")) % text.size();
this->ui_->characterLabel->setText(str.str().c_str());
- detect_cr_lf(text);
- }
-}
-
-void PlainTextEditorPage::PrepareToDestroy() {
- if (read_thread_) {
- read_thread_->requestInterruption();
- read_thread_ = nullptr;
}
+ QTimer::singleShot(25, this, &PlainTextEditorPage::SignalUIBytesDisplayed);
+ LOG(INFO) << "end";
}
void PlainTextEditorPage::detect_encoding(const std::string &data) {
- AutoIt::Common::TextEncodingDetect text_detect;
- AutoIt::Common::TextEncodingDetect::Encoding encoding =
- text_detect.DetectEncoding((unsigned char *)(data.data()), data.size());
+ // skip the binary data to avoid the false detection of the encoding
+ if (binary_mode_) return;
+
+ // detect the encoding
+ auto charset = CharsetOperator::Detect(data);
+ this->charset_name_ = std::get<0>(charset).c_str();
+ this->language_name_ = std::get<1>(charset).c_str();
+ this->charset_confidence_ = std::get<2>(charset);
- if (encoding == AutoIt::Common::TextEncodingDetect::None) {
+ // probably there is no need to detect the encoding again
+ if (this->charset_confidence_ < 10) {
binary_mode_ = true;
- ui_->encodingLabel->setText(_("Binary"));
- } else if (encoding == AutoIt::Common::TextEncodingDetect::ASCII) {
- ui_->encodingLabel->setText(_("ASCII(7 bits)"));
- } else if (encoding == AutoIt::Common::TextEncodingDetect::ANSI) {
- ui_->encodingLabel->setText(_("ASCII(8 bits)"));
- } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF8_BOM ||
- encoding == AutoIt::Common::TextEncodingDetect::UTF8_NOBOM) {
- ui_->encodingLabel->setText(_("UTF-8"));
- } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF16_LE_BOM ||
- encoding == AutoIt::Common::TextEncodingDetect::UTF16_LE_NOBOM) {
- ui_->encodingLabel->setText(_("UTF-16"));
- } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF16_BE_BOM ||
- encoding == AutoIt::Common::TextEncodingDetect::UTF16_BE_NOBOM) {
- ui_->encodingLabel->setText(_("UTF-16(BE)"));
+ }
+
+ if (binary_mode_) {
+ // hide the line ending label, when the file is binary
+ this->ui_->lfLabel->setHidden(true);
+ this->ui_->encodingLabel->setText(_("binary"));
+ } else {
+ ui_->encodingLabel->setText(this->charset_name_.c_str());
}
}
-void PlainTextEditorPage::detect_cr_lf(const QString &data) {
+void PlainTextEditorPage::detect_cr_lf(const std::string &data) {
if (binary_mode_) {
- this->ui_->lfLabel->setText("None");
return;
}
- if (data.contains("\r\n")) {
- this->ui_->lfLabel->setText("CRLF");
+
+ // if contain crlf, set the label to crlf
+ if (is_crlf_) return;
+
+ if (data.find("\r\n") != std::string::npos) {
+ this->ui_->lfLabel->setText("crlf");
+ is_crlf_ = true;
} else {
- this->ui_->lfLabel->setText("LF");
+ this->ui_->lfLabel->setText("lf");
}
}
diff --git a/src/ui/widgets/PlainTextEditorPage.h b/src/ui/widgets/PlainTextEditorPage.h
index e76c11e3..e5c1c89d 100644
--- a/src/ui/widgets/PlainTextEditorPage.h
+++ b/src/ui/widgets/PlainTextEditorPage.h
@@ -29,6 +29,8 @@
#ifndef __EDITORPAGE_H__
#define __EDITORPAGE_H__
+#include <string>
+
#include "core/GpgConstants.h"
#include "ui/GpgFrontendUI.h"
@@ -49,7 +51,7 @@ class PlainTextEditorPage : public QWidget {
* @param file_path Path of the file handled in this tab
* @param parent Pointer to the parent widget
*/
- explicit PlainTextEditorPage(QString file_path = "",
+ explicit PlainTextEditorPage(QString file_path = {},
QWidget* parent = nullptr);
/**
@@ -99,19 +101,36 @@ class PlainTextEditorPage : public QWidget {
[[nodiscard]] bool ReadDone() const { return this->read_done_; }
/**
- * @brief
+ * @brief detect if the charset of the file will change
+ *
+ */
+ bool WillCharsetChange() const;
+
+ /**
+ * @brief notify the user that the file has been saved.
+ *
+ */
+ void NotifyFileSaved();
+
+ signals:
+
+ /**
+ * @brief this signal is emitted when the bytes has been append in texteditor.
*
*/
- void PrepareToDestroy();
+ void SignalUIBytesDisplayed();
private:
std::shared_ptr<Ui_PlainTextEditor> ui_; ///<
QString full_file_path_; ///< The path to the file handled in the tab
bool sign_marked_{}; ///< true, if the signed header is marked, false if not
- bool read_done_ = false; ///<
- QThread* read_thread_ = nullptr; ///<
- bool binary_mode_ = false; ///<
- size_t read_bytes_ = 0; ///<
+ bool read_done_ = false; ///<
+ bool binary_mode_ = false; ///<
+ size_t read_bytes_ = 0; ///<
+ std::string charset_name_; ///<
+ std::string language_name_; ///<
+ int32_t charset_confidence_; ///<
+ bool is_crlf_ = false; ///<
/**
* @brief
@@ -125,7 +144,7 @@ class PlainTextEditorPage : public QWidget {
*
* @param data
*/
- void detect_cr_lf(const QString& data);
+ void detect_cr_lf(const std::string& data);
private slots:
@@ -139,7 +158,7 @@ class PlainTextEditorPage : public QWidget {
*
* @param data
*/
- void slot_insert_text(const std::string& data);
+ void slot_insert_text(QByteArray bytes_data);
};
} // namespace GpgFrontend::UI
diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp
index ecf1a4bd..713dbb80 100644
--- a/src/ui/widgets/TextEdit.cpp
+++ b/src/ui/widgets/TextEdit.cpp
@@ -166,11 +166,30 @@ bool TextEdit::save_file(const QString& fileName) {
return false;
}
+ PlainTextEditorPage* page = SlotCurPageTextEdit();
+ if (page == nullptr) return false;
+
+ if (page->WillCharsetChange()) {
+ auto result = QMessageBox::warning(
+ this, _("Save"),
+ QString("<p>") +
+ _("After saving, the encoding of the current file will be "
+ "converted to UTF-8 and the line endings will be changed to "
+ "LF. ") +
+ "</p>" + "<p>" +
+ _("If this is not the result you expect, please use \"save "
+ "as\".") +
+ "</p>",
+ QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (result == QMessageBox::Cancel) {
+ return false;
+ }
+ }
+
QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- PlainTextEditorPage* page = SlotCurPageTextEdit();
-
QTextStream outputStream(&file);
QApplication::setOverrideCursor(Qt::WaitCursor);
outputStream << page->GetTextPage()->toPlainText();
@@ -182,7 +201,8 @@ bool TextEdit::save_file(const QString& fileName) {
int curIndex = tab_widget_->currentIndex();
tab_widget_->setTabText(curIndex, stripped_name(fileName));
page->SetFilePath(fileName);
- // statusBar()->showMessage(_("File saved"), 2000);
+ page->NotifyFileSaved();
+
file.close();
return true;
} else {
@@ -295,9 +315,7 @@ bool TextEdit::maybe_save_current_tab(bool askToSave) {
return false;
}
}
-
- // destroy
- page->PrepareToDestroy();
+ page->deleteLater();
return true;
}