diff options
author | Saturn&Eric <[email protected]> | 2021-12-13 14:50:57 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-12-13 14:50:57 +0000 |
commit | ee4752ed756e3114f740efddf63ce987ac154d03 (patch) | |
tree | 27653d56c88849c99c70b8fc7223f8ae98090f0b /src | |
parent | Update CI for Release and Debug. (diff) | |
parent | Added & Fixed (diff) | |
download | GpgFrontend-ee4752ed756e3114f740efddf63ce987ac154d03.tar.gz GpgFrontend-ee4752ed756e3114f740efddf63ce987ac154d03.zip |
Merge pull request #31 from saturneric/develop-ui
v2.0.2-beta.1
Diffstat (limited to 'src')
23 files changed, 593 insertions, 331 deletions
diff --git a/src/gpg/GpgConstants.cpp b/src/gpg/GpgConstants.cpp index e3de1d06..2454daa2 100644 --- a/src/gpg/GpgConstants.cpp +++ b/src/gpg/GpgConstants.cpp @@ -28,6 +28,7 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> +#include <string> const char* GpgFrontend::GpgConstants::PGP_CRYPT_BEGIN = "-----BEGIN PGP MESSAGE-----"; @@ -110,16 +111,17 @@ static inline std::string trim(std::string& s) { return s; } -std::string GpgFrontend::read_all_data_in_file(const std::string& path) { +std::string GpgFrontend::read_all_data_in_file(const std::string& utf8_path) { using namespace boost::filesystem; - class path file_info(path.c_str()); - - if (!exists(file_info) || !is_regular_file(path)) - throw std::runtime_error("no permission"); - + class path file_info(utf8_path.c_str()); + if (!exists(file_info) || !is_regular_file(file_info)) return {}; std::ifstream in_file; - in_file.open(path, std::ios::in); - if (!in_file.good()) throw std::runtime_error("cannot open file"); +#ifndef WINDOWS + in_file.open(file_info.string(), 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); @@ -127,9 +129,16 @@ std::string GpgFrontend::read_all_data_in_file(const std::string& path) { return in_buffer; } -bool GpgFrontend::write_buffer_to_file(const std::string& path, +bool GpgFrontend::write_buffer_to_file(const std::string& utf8_path, const std::string& out_buffer) { - std::ofstream out_file(boost::filesystem::path(path).string(), std::ios::out); + using namespace boost::filesystem; + class path file_info(utf8_path.c_str()); +#ifndef WINDOWS + std::ofstream out_file(file_info.string(), 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(); @@ -137,8 +146,9 @@ bool GpgFrontend::write_buffer_to_file(const std::string& path, } std::string GpgFrontend::get_file_extension(const std::string& path) { - // Create a Path object from given string + // Create a path object from given string boost::filesystem::path path_obj(path); + // Check if file name in the path object has extension if (path_obj.has_extension()) { // Fetch the extension from path object and return @@ -149,7 +159,7 @@ std::string GpgFrontend::get_file_extension(const std::string& path) { } std::string GpgFrontend::get_only_file_name_with_path(const std::string& path) { - // Create a Path object from given string + // Create a path object from given string boost::filesystem::path path_obj(path); // Check if file name in the path object has extension if (path_obj.has_filename()) { @@ -157,7 +167,7 @@ std::string GpgFrontend::get_only_file_name_with_path(const std::string& path) { return (path_obj.parent_path() / path_obj.stem()).string(); } // In case of no extension return empty string - throw std::runtime_error("invalid file path"); + return {}; } /* diff --git a/src/gpg/function/BasicOperator.cpp b/src/gpg/function/BasicOperator.cpp index 912119e2..5f6ffb85 100644 --- a/src/gpg/function/BasicOperator.cpp +++ b/src/gpg/function/BasicOperator.cpp @@ -76,13 +76,15 @@ GpgFrontend::GpgError GpgFrontend::BasicOperator::Verify( GpgVerifyResult& result) const { gpgme_error_t err; + LOG(INFO) << "in buffer size" << in_buffer.size(); GpgData data_in(in_buffer.data(), in_buffer.size()); + GpgData data_out; if (sig_buffer != nullptr) { GpgData sig_data(sig_buffer->data(), sig_buffer->size()); err = check_gpg_error(gpgme_op_verify(ctx, sig_data, data_in, nullptr)); } else - err = check_gpg_error(gpgme_op_verify(ctx, data_in, nullptr, data_in)); + err = check_gpg_error(gpgme_op_verify(ctx, data_in, nullptr, data_out)); auto temp_result = GpgVerifyResult(gpgme_op_verify_result(ctx)); std::swap(result, temp_result); @@ -204,3 +206,19 @@ GpgFrontend::BasicOperator::GetSigners() { } return signers; } +gpg_error_t GpgFrontend::BasicOperator::EncryptSymmetric( + GpgFrontend::ByteArray& in_buffer, GpgFrontend::ByteArrayPtr& out_buffer, + GpgFrontend::GpgEncrResult& result) { + GpgData data_in(in_buffer.data(), in_buffer.size()), data_out; + + gpgme_error_t err = check_gpg_error(gpgme_op_encrypt( + ctx, nullptr, GPGME_ENCRYPT_SYMMETRIC, data_in, data_out)); + + auto temp_data_out = data_out.Read2Buffer(); + std::swap(temp_data_out, out_buffer); + + auto temp_result = GpgEncrResult(gpgme_op_encrypt_result(ctx)); + std::swap(result, temp_result); + + return err; +} diff --git a/src/gpg/function/BasicOperator.h b/src/gpg/function/BasicOperator.h index 39f93668..4ea70eea 100644 --- a/src/gpg/function/BasicOperator.h +++ b/src/gpg/function/BasicOperator.h @@ -37,6 +37,9 @@ class BasicOperator : public SingletonFunctionObject<BasicOperator> { gpg_error_t Encrypt(KeyListPtr keys, BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, GpgEncrResult& result); + gpg_error_t EncryptSymmetric(BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, + GpgEncrResult& result); + gpgme_error_t EncryptSign(KeyListPtr keys, KeyListPtr signers, BypeArrayRef in_buffer, ByteArrayPtr& out_buffer, GpgEncrResult& encr_result, diff --git a/src/gpg/function/GpgFileOpera.cpp b/src/gpg/function/GpgFileOpera.cpp index c3f75cf8..42b37c71 100644 --- a/src/gpg/function/GpgFileOpera.cpp +++ b/src/gpg/function/GpgFileOpera.cpp @@ -41,7 +41,7 @@ GpgFrontend::GpgError GpgFrontend::GpgFileOpera::EncryptFile( if (!write_buffer_to_file(path + ".asc", *out_buffer)) { throw std::runtime_error("write_buffer_to_file error"); }; - + return err; } @@ -94,7 +94,6 @@ gpgme_error_t GpgFrontend::GpgFileOpera::VerifyFile(const std::string& path, if (get_file_extension(path) == ".gpg") { auto err = BasicOperator::GetInstance().Verify(in_buffer, sign_buffer, result); - assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); return err; } else { sign_buffer = @@ -150,8 +149,7 @@ gpg_error_t GpgFrontend::GpgFileOpera::DecryptVerifyFile( if (!(file_extension == ".asc" || file_extension == ".gpg")) out_file_name = path + ".out"; - LOG(INFO) << "GpgFrontend::GpgFileOpera::DecryptVerifyFile out_file_name" - << out_file_name; + LOG(INFO) << "out_file_name" << out_file_name; if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) if (!write_buffer_to_file(out_file_name, *out_buffer)) { diff --git a/src/gpg/function/GpgKeyImportExportor.cpp b/src/gpg/function/GpgKeyImportExportor.cpp index d8812839..89d3b002 100644 --- a/src/gpg/function/GpgKeyImportExportor.cpp +++ b/src/gpg/function/GpgKeyImportExportor.cpp @@ -130,3 +130,31 @@ bool GpgFrontend::GpgKeyImportExportor::ExportKey( std::swap(out_buffer, temp_out_buffer); return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; } + +bool GpgFrontend::GpgKeyImportExportor::ExportKeyOpenSSH( + const GpgFrontend::GpgKey& key, + GpgFrontend::ByteArrayPtr& out_buffer) const { + GpgData data_out; + auto err = + gpgme_op_export(ctx, key.id().c_str(), GPGME_EXPORT_MODE_SSH, data_out); + + DLOG(INFO) << "read_bytes" << gpgme_data_seek(data_out, 0, SEEK_END); + + auto temp_out_buffer = data_out.Read2Buffer(); + std::swap(out_buffer, temp_out_buffer); + return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; +} + +bool GpgFrontend::GpgKeyImportExportor::ExportSecretKeyShortest( + const GpgFrontend::GpgKey& key, + GpgFrontend::ByteArrayPtr& out_buffer) const { + GpgData data_out; + auto err = gpgme_op_export(ctx, key.id().c_str(), GPGME_EXPORT_MODE_MINIMAL, + data_out); + + DLOG(INFO) << "read_bytes" << gpgme_data_seek(data_out, 0, SEEK_END); + + auto temp_out_buffer = data_out.Read2Buffer(); + std::swap(out_buffer, temp_out_buffer); + return check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR; +} diff --git a/src/gpg/function/GpgKeyImportExportor.h b/src/gpg/function/GpgKeyImportExportor.h index 35a237ba..ad43d539 100644 --- a/src/gpg/function/GpgKeyImportExportor.h +++ b/src/gpg/function/GpgKeyImportExportor.h @@ -90,8 +90,13 @@ class GpgKeyImportExportor bool ExportKey(const GpgKey& key, ByteArrayPtr& out_buffer) const; + bool ExportKeyOpenSSH(const GpgKey& key, ByteArrayPtr& out_buffer) const; + bool ExportSecretKey(const GpgKey& key, ByteArrayPtr& outBuffer) const; + bool ExportSecretKeyShortest(const GpgKey& key, + ByteArrayPtr& outBuffer) const; + private: GpgContext& ctx = GpgContext::GetInstance(SingletonFunctionObject::GetDefaultChannel()); diff --git a/src/gpg/result_analyse/DecryptResultAnalyse.cpp b/src/gpg/result_analyse/DecryptResultAnalyse.cpp index 4ff32c59..f7fc70fe 100644 --- a/src/gpg/result_analyse/DecryptResultAnalyse.cpp +++ b/src/gpg/result_analyse/DecryptResultAnalyse.cpp @@ -51,6 +51,9 @@ void GpgFrontend::DecryptResultAnalyse::do_analyse() { stream << _("File Name") << ": " << result->file_name << std::endl; stream << std::endl; } + if (result->is_mime) { + stream << _("MIME") << ": " << _("true") << std::endl; + } auto reci = result->recipients; if (reci != nullptr) stream << _("Recipient(s)") << ": " << std::endl; diff --git a/src/main.cpp b/src/main.cpp index 1dbf1d5e..aad4b8fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,7 +25,9 @@ #include <cstdlib> #include "GpgFrontendBuildInfo.h" +#include "gpg/GpgContext.h" #include "ui/MainWindow.h" +#include "ui/WaitingDialog.h" #include "ui/settings/GlobalSettingStation.h" // Easy Logging Cpp @@ -40,7 +42,10 @@ int main(int argc, char* argv[]) { // Qt App QApplication app(argc, argv); + +#ifndef MACOS QApplication::setWindowIcon(QIcon(":gpgfrontend.png")); +#endif #ifdef MACOS // support retina screen @@ -60,7 +65,7 @@ int main(int argc, char* argv[]) { // unicode in source QTextCodec::setCodecForLocale(QTextCodec::codecForName("utf-8")); -#if !defined(RELEASE) +#if !defined(RELEASE) && defined(WINDOWS) // css QFile file(RESOURCE_DIR(qApp->applicationDirPath()) + "/css/default.qss"); file.open(QFile::ReadOnly); @@ -75,17 +80,37 @@ int main(int argc, char* argv[]) { */ int return_from_event_loop_code; - do { - // i18n - init_locale(); - - QApplication::setQuitOnLastWindowClosed(true); - - auto main_window = std::make_unique<GpgFrontend::UI::MainWindow>(); - main_window->init(); - main_window->show(); - return_from_event_loop_code = QApplication::exec(); + // Create & Check Gnupg Context Status + if (!GpgFrontend::GpgContext::GetInstance().good()) { + QMessageBox::critical( + nullptr, _("ENV Loading Failed"), + _("Gnupg(gpg) is not installed correctly, please follow the " + "ReadME " + "instructions in Github to install Gnupg and then open " + "GpgFrontend.")); + QCoreApplication::quit(); + exit(0); + } + do { + try { + // i18n + init_locale(); + + QApplication::setQuitOnLastWindowClosed(true); + + auto main_window = std::make_unique<GpgFrontend::UI::MainWindow>(); + main_window->init(); + main_window->show(); + return_from_event_loop_code = QApplication::exec(); + + } catch (...) { + QMessageBox::information(nullptr, _("Unhandled Exception Thrown"), + _("Oops, an unhandled exception was thrown " + "during the running of the " + "program, and now it needs to be restarted.")); + continue; + } } while (return_from_event_loop_code == RESTART_CODE); return return_from_event_loop_code; diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index f4b00d22..7c6bf732 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -13,10 +13,11 @@ if (SMTP_SUPPORT) aux_source_directory(./smtp UI_SOURCE) endif () - add_library(gpgfrontend-ui STATIC ${UI_SOURCE}) - -target_link_libraries(gpgfrontend-ui +set(GPGFRONTEND_UI_LIB_NAME gpgfrontend-ui) +target_link_libraries(${GPGFRONTEND_UI_LIB_NAME} Qt5::Network Qt5::PrintSupport Qt5::Widgets Qt5::Test Qt5::Core) +target_include_directories(gpgfrontend-ui PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/${GPGFRONTEND_UI_LIB_NAME}_autogen/include) target_compile_features(gpgfrontend-ui PUBLIC cxx_std_17)
\ No newline at end of file diff --git a/src/ui/KeyMgmt.cpp b/src/ui/KeyMgmt.cpp index aa6df120..f28e6587 100755 --- a/src/ui/KeyMgmt.cpp +++ b/src/ui/KeyMgmt.cpp @@ -207,6 +207,13 @@ void KeyMgmt::createActions() { connect(exportKeyToFileAct, SIGNAL(triggered()), this, SLOT(slotExportKeyToFile())); + exportKeyAsOpenSSHFormat = new QAction(_("Export As OpenSSH"), this); + exportKeyAsOpenSSHFormat->setIcon(QIcon(":ssh-key.png")); + exportKeyAsOpenSSHFormat->setToolTip( + _("Export Selected Key(s) As OpenSSH Format to File")); + connect(exportKeyAsOpenSSHFormat, SIGNAL(triggered()), this, + SLOT(slotExportAsOpenSSHFormat())); + deleteSelectedKeysAct = new QAction(_("Delete Selected Key(s)"), this); deleteSelectedKeysAct->setToolTip(_("Delete the Selected keys")); connect(deleteSelectedKeysAct, SIGNAL(triggered()), this, @@ -240,6 +247,7 @@ void KeyMgmt::createMenus() { importKeyMenu->addAction(importKeyFromKeyServerAct); keyMenu->addAction(exportKeyToFileAct); keyMenu->addAction(exportKeyToClipboardAct); + keyMenu->addAction(exportKeyAsOpenSSHFormat); keyMenu->addSeparator(); keyMenu->addAction(deleteCheckedKeysAct); } @@ -273,6 +281,7 @@ void KeyMgmt::createToolBars() { keyToolBar->addSeparator(); keyToolBar->addAction(exportKeyToFileAct); keyToolBar->addAction(exportKeyToClipboardAct); + keyToolBar->addAction(exportKeyAsOpenSSHFormat); } void KeyMgmt::slotDeleteSelectedKeys() { @@ -446,4 +455,47 @@ void KeyMgmt::slotSaveWindowState() { GlobalSettingStation::GetInstance().Sync(); } +void KeyMgmt::slotExportAsOpenSSHFormat() { + ByteArrayPtr key_export_data = nullptr; + auto keys_checked = mKeyList->getChecked(); + + if (keys_checked->empty()) { + QMessageBox::critical(nullptr, _("Error"), _("No Key Checked.")); + return; + } + + auto key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); + if (!GpgKeyImportExportor::GetInstance().ExportKeyOpenSSH(key, + key_export_data)) { + QMessageBox::critical(nullptr, _("Error"), + _("An error occur in exporting.")); + return; + } + + if (key_export_data->empty()) { + QMessageBox::critical( + nullptr, _("Error"), + _("This key may not be able to export as OpenSSH format. Please check " + "the key-size of the subkey(s) used to sign.")); + return; + } + + key = GpgKeyGetter::GetInstance().GetKey(keys_checked->front()); + if (!key.good()) { + QMessageBox::critical(nullptr, _("Error"), _("Key Not Found.")); + return; + } + QString fileString = QString::fromStdString(key.name() + " " + key.email() + + "(" + key.id() + ").pub"); + + QString file_name = QFileDialog::getSaveFileName( + this, _("Export OpenSSH Key To File"), fileString, + QString(_("OpenSSH Public Key Files")) + " (*.pub);;All Files (*)"); + + if (!file_name.isEmpty()) { + write_buffer_to_file(file_name.toStdString(), *key_export_data); + emit signalStatusBarChanged(QString(_("key(s) exported"))); + } +} + } // namespace GpgFrontend::UI diff --git a/src/ui/KeyMgmt.h b/src/ui/KeyMgmt.h index bf1c9b5a..7edb1b5c 100755 --- a/src/ui/KeyMgmt.h +++ b/src/ui/KeyMgmt.h @@ -48,6 +48,8 @@ class KeyMgmt : public QMainWindow { void slotExportKeyToClipboard(); + void slotExportAsOpenSSHFormat(); + void slotDeleteSelectedKeys(); void slotDeleteCheckedKeys(); @@ -80,6 +82,7 @@ class KeyMgmt : public QMainWindow { QMenu* importKeyMenu{}; QAction* openKeyFileAct{}; QAction* exportKeyToFileAct{}; + QAction* exportKeyAsOpenSSHFormat{}; QAction* exportKeyToClipboardAct{}; QAction* deleteCheckedKeysAct{}; QAction* deleteSelectedKeysAct{}; diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 9aceed96..6d55aeb5 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -38,18 +38,7 @@ MainWindow::MainWindow() { } void MainWindow::init() noexcept { - LOG(INFO) << _("Called"); try { - // Check Context Status - if (!GpgContext::GetInstance().good()) { - QMessageBox::critical( - nullptr, _("ENV Loading Failed"), - _("Gnupg is not installed correctly, please follow the ReadME " - "instructions to install gnupg and then open GpgFrontend.")); - QCoreApplication::quit(); - exit(0); - } - networkAccessManager = new QNetworkAccessManager(this); /* get path where app was started */ @@ -64,7 +53,7 @@ void MainWindow::init() noexcept { mKeyList->slotRefresh(); - infoBoard = new InfoBoardWidget(this, mKeyList); + infoBoard = new InfoBoardWidget(this); /* List of binary Attachments */ attachmentDockCreated = false; @@ -139,7 +128,6 @@ void MainWindow::init() noexcept { version_thread->start(); #endif - } catch (...) { LOG(FATAL) << _("Critical error occur while loading GpgFrontend."); QMessageBox::critical(nullptr, _("Loading Failed"), diff --git a/src/ui/keypair_details/KeyPairDetailTab.cpp b/src/ui/keypair_details/KeyPairDetailTab.cpp index de16ccf7..3de83e38 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.cpp +++ b/src/ui/keypair_details/KeyPairDetailTab.cpp @@ -81,15 +81,24 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) vboxKD->addWidget(new QLabel(QString(_("Master Key Existence")) + ": "), 8, 0); - vboxKD->addWidget(keyidVarLabel, 0, 1); - vboxKD->addWidget(algorithmVarLabel, 1, 1); - vboxKD->addWidget(keySizeVarLabel, 2, 1); - vboxKD->addWidget(usageVarLabel, 3, 1); - vboxKD->addWidget(actualUsageVarLabel, 4, 1); - vboxKD->addWidget(createdVarLabel, 5, 1); - vboxKD->addWidget(expireVarLabel, 6, 1); - vboxKD->addWidget(lastUpdateVarLabel, 7, 1); - vboxKD->addWidget(masterKeyExistVarLabel, 8, 1); + vboxKD->addWidget(keyidVarLabel, 0, 1, 1, 1); + vboxKD->addWidget(algorithmVarLabel, 1, 1, 1, 2); + vboxKD->addWidget(keySizeVarLabel, 2, 1, 1, 2); + vboxKD->addWidget(usageVarLabel, 3, 1, 1, 2); + vboxKD->addWidget(actualUsageVarLabel, 4, 1, 1, 2); + vboxKD->addWidget(createdVarLabel, 5, 1, 1, 2); + vboxKD->addWidget(expireVarLabel, 6, 1, 1, 2); + vboxKD->addWidget(lastUpdateVarLabel, 7, 1, 1, 2); + vboxKD->addWidget(masterKeyExistVarLabel, 8, 1, 1, 2); + + auto* copyKeyIdButton = new QPushButton(_("Copy")); + copyKeyIdButton->setFlat(true); + vboxKD->addWidget(copyKeyIdButton, 0, 2); + connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() { + QString fpr = keyidVarLabel->text().trimmed(); + QClipboard* cb = QApplication::clipboard(); + cb->setText(fpr); + }); ownerBox->setLayout(vboxOD); mvbox->addWidget(ownerBox); @@ -119,6 +128,9 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) mvbox->addStretch(); mvbox->addWidget(fingerprintBox); + // Set Menu + createOperaMenu(); + auto* opera_key_box = new QGroupBox(_("Operations")); auto* vbox_p_k = new QVBoxLayout(); @@ -131,10 +143,9 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) SLOT(slotExportPublicKey())); if (mKey.is_private_key()) { - auto* export_private_button = - new QPushButton(_("Export Private Key (Include Subkey)")); - connect(export_private_button, SIGNAL(clicked()), this, - SLOT(slotExportPrivateKey())); + auto* export_private_button = new QPushButton(_("Export Private Key")); + export_private_button->setStyleSheet("text-align:center;"); + export_private_button->setMenu(secretKeyExportOperaMenu); export_h_box_layout->addWidget(export_private_button); if (mKey.has_master_key()) { @@ -157,10 +168,6 @@ KeyPairDetailTab::KeyPairDetailTab(const std::string& key_id, QWidget* parent) auto* key_server_opera_button = new QPushButton(_("Key Server Operation (Pubkey)")); key_server_opera_button->setStyleSheet("text-align:center;"); - connect(key_server_opera_button, SIGNAL(clicked()), this, - SLOT(slotModifyEditDatetime())); - // Set Menu - createKeyServerOperaMenu(); key_server_opera_button->setMenu(keyServerOperaMenu); advance_h_box_layout->addWidget(key_server_opera_button); @@ -230,6 +237,48 @@ void KeyPairDetailTab::slotExportPublicKey() { } } +void KeyPairDetailTab::slotExportShortPrivateKey() { + // Show a information box with explanation about private key + int ret = QMessageBox::information( + this, _("Exporting short private Key"), + "<h3>" + QString(_("You are about to export your")) + + "<font color=\"red\">" + _(" PRIVATE KEY ") + "</font>!</h3>\n" + + _("This is NOT your Public Key, so DON'T give it away.") + "<br />" + + _("Do you REALLY want to export your PRIVATE KEY in a Minimum " + "Size?") + + "<br />" + + _("For OpenPGP keys it removes all signatures except for the latest " + "self-signatures."), + QMessageBox::Cancel | QMessageBox::Ok); + + // export key, if ok was clicked + if (ret == QMessageBox::Ok) { + ByteArrayPtr keyArray = nullptr; + + if (!GpgKeyImportExportor::GetInstance().ExportSecretKeyShortest( + mKey, keyArray)) { + QMessageBox::critical( + this, _("Error"), + _("An error occurred during the export operation.")); + return; + } + auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_short_secret.asc"; + auto fileName = + QFileDialog::getSaveFileName( + this, _("Export Key To File"), QString::fromStdString(fileString), + QString(_("Key Files")) + " (*.asc *.txt);;All Files (*)") + .toStdString(); + + if (!write_buffer_to_file(fileName, *keyArray)) { + QMessageBox::critical( + this, _("Export Error"), + QString(_("Couldn't open %1 for writing")).arg(fileName.c_str())); + return; + } + } +} + void KeyPairDetailTab::slotExportPrivateKey() { // Show a information box with explanation about private key int ret = QMessageBox::information( @@ -250,8 +299,8 @@ void KeyPairDetailTab::slotExportPrivateKey() { _("An error occurred during the export operation.")); return; } - auto fileString = - mKey.name() + " " + mKey.email() + "(" + mKey.id() + ")_secret.asc"; + auto fileString = mKey.name() + " " + mKey.email() + "(" + mKey.id() + + ")_full_secret.asc"; auto fileName = QFileDialog::getSaveFileName( this, _("Export Key To File"), QString::fromStdString(fileString), @@ -370,7 +419,7 @@ void KeyPairDetailTab::slotRefreshKeyInfo() { } } -void KeyPairDetailTab::createKeyServerOperaMenu() { +void KeyPairDetailTab::createOperaMenu() { keyServerOperaMenu = new QMenu(this); auto* uploadKeyPair = new QAction(_("Upload Key Pair to Key Server"), this); @@ -384,6 +433,21 @@ void KeyPairDetailTab::createKeyServerOperaMenu() { keyServerOperaMenu->addAction(uploadKeyPair); keyServerOperaMenu->addAction(updateKeyPair); + + secretKeyExportOperaMenu = new QMenu(this); + + auto* exportFullSecretKey = new QAction(_("Export Full Secret Key"), this); + connect(exportFullSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportPrivateKey())); + if (!mKey.is_private_key()) exportFullSecretKey->setDisabled(true); + + auto* exportShortestSecretKey = + new QAction(_("Export Shortest Secret Key"), this); + connect(exportShortestSecretKey, SIGNAL(triggered()), this, + SLOT(slotExportShortPrivateKey())); + + secretKeyExportOperaMenu->addAction(exportFullSecretKey); + secretKeyExportOperaMenu->addAction(exportShortestSecretKey); } void KeyPairDetailTab::slotUploadKeyToServer() { @@ -403,7 +467,7 @@ void KeyPairDetailTab::slotUpdateKeyFromServer() { } void KeyPairDetailTab::slotGenRevokeCert() { - auto literal = QStringLiteral("%1 (*.rev)").arg(_("Revocation Certificates")); + auto literal = QString("%1 (*.rev)").arg(_("Revocation Certificates")); QString m_output_file_name; QFileDialog dialog(this, "Generate revocation certificate", QString(), diff --git a/src/ui/keypair_details/KeyPairDetailTab.h b/src/ui/keypair_details/KeyPairDetailTab.h index 68ca1ebd..3e2f2298 100644 --- a/src/ui/keypair_details/KeyPairDetailTab.h +++ b/src/ui/keypair_details/KeyPairDetailTab.h @@ -36,7 +36,7 @@ namespace GpgFrontend::UI { class KeyPairDetailTab : public QWidget { Q_OBJECT - void createKeyServerOperaMenu(); + void createOperaMenu(); private slots: @@ -45,6 +45,8 @@ class KeyPairDetailTab : public QWidget { */ void slotExportPrivateKey(); + void slotExportShortPrivateKey(); + void slotExportPublicKey(); /** @@ -95,6 +97,7 @@ class KeyPairDetailTab : public QWidget { QLabel* expLabel; QMenu* keyServerOperaMenu{}; + QMenu* secretKeyExportOperaMenu{}; public: explicit KeyPairDetailTab(const std::string& key_id, diff --git a/src/ui/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/keypair_details/KeyPairSubkeyTab.cpp index 93e07875..cb71c09f 100644 --- a/src/ui/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/keypair_details/KeyPairSubkeyTab.cpp @@ -64,7 +64,7 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) subkeyDetailLayout->addWidget(new QLabel(QString(_("Usage")) + ": "), 3, 0); subkeyDetailLayout->addWidget(new QLabel(QString(_("Expires On")) + ": "), 4, 0); - subkeyDetailLayout->addWidget(new QLabel(QString(_("Last Update")) + ": "), 5, + subkeyDetailLayout->addWidget(new QLabel(QString(_("Create Date")) + ": "), 5, 0); subkeyDetailLayout->addWidget(new QLabel(QString(_("Existence")) + ": "), 6, 0); @@ -80,14 +80,23 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(const std::string& key_id, QWidget* parent) masterKeyExistVarLabel = new QLabel(); fingerPrintVarLabel = new QLabel(); - subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1); - subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1); - subkeyDetailLayout->addWidget(expireVarLabel, 4, 1); - subkeyDetailLayout->addWidget(algorithmVarLabel, 1, 1); - subkeyDetailLayout->addWidget(createdVarLabel, 5, 1); - subkeyDetailLayout->addWidget(usageVarLabel, 3, 1); - subkeyDetailLayout->addWidget(masterKeyExistVarLabel, 6, 1); - subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1); + subkeyDetailLayout->addWidget(keyidVarLabel, 0, 1, 1, 1); + subkeyDetailLayout->addWidget(keySizeVarLabel, 2, 1, 1, 2); + subkeyDetailLayout->addWidget(expireVarLabel, 4, 1, 1, 2); + subkeyDetailLayout->addWidget(algorithmVarLabel, 1, 1, 1, 2); + subkeyDetailLayout->addWidget(createdVarLabel, 5, 1, 1, 2); + subkeyDetailLayout->addWidget(usageVarLabel, 3, 1, 1, 2); + subkeyDetailLayout->addWidget(masterKeyExistVarLabel, 6, 1, 1, 2); + subkeyDetailLayout->addWidget(fingerPrintVarLabel, 7, 1, 1, 2); + + auto* copyKeyIdButton = new QPushButton(_("Copy")); + copyKeyIdButton->setFlat(true); + subkeyDetailLayout->addWidget(copyKeyIdButton, 0, 2); + connect(copyKeyIdButton, &QPushButton::clicked, this, [=]() { + QString fpr = keyidVarLabel->text().trimmed(); + QClipboard* cb = QApplication::clipboard(); + cb->setText(fpr); + }); listBox->setLayout(subkeyListLayout); listBox->setContentsMargins(0, 12, 0, 0); diff --git a/src/ui/main_window/MainWindowFileSlotFunction.cpp b/src/ui/main_window/MainWindowFileSlotFunction.cpp index 19be9769..133a7820 100644 --- a/src/ui/main_window/MainWindowFileSlotFunction.cpp +++ b/src/ui/main_window/MainWindowFileSlotFunction.cpp @@ -245,14 +245,16 @@ void MainWindow::slotFileVerify() { signFilePath = path + ".sig"; } - bool ok; - QString text = - QInputDialog::getText(this, _("Origin file to verify"), _("Filepath"), - QLineEdit::Normal, dataFilePath, &ok); - if (ok && !text.isEmpty()) { - dataFilePath = text; - } else { - return; + if (fileInfo.suffix() != "gpg") { + bool ok; + QString text = + QInputDialog::getText(this, _("Origin file to verify"), _("Filepath"), + QLineEdit::Normal, dataFilePath, &ok); + if (ok && !text.isEmpty()) { + dataFilePath = text; + } else { + return; + } } QFileInfo dataFileInfo(dataFilePath), signFileInfo(signFilePath); @@ -395,13 +397,23 @@ void MainWindow::slotFileDecryptVerify() { if (!file_pre_check(this, path)) return; - QString outFileName, fileExtension = QFileInfo(path).suffix(); - - if (fileExtension == "asc" || fileExtension == "gpg") { - int pos = path.lastIndexOf(QChar('.')); - outFileName = path.left(pos); + boost::filesystem::path out_path(path.toStdString()); + if (out_path.extension() == ".asc" || out_path.extension() == ".gpg") { + out_path = out_path.parent_path() / out_path.filename(); } else { - outFileName = path + ".out"; + out_path = out_path.replace_extension(".out").string(); + } + LOG(INFO) << "out path" << out_path; + + if (QFile::exists(out_path.string().c_str())) { + auto ret = + QMessageBox::warning(this, _("Warning"), + QString(_("The output file %1 already exists, do " + "you need to overwrite it?")) + .arg(out_path.filename().string().c_str()), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; } GpgDecrResult d_result = nullptr; diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index 4ad33ac3..41549182 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -51,40 +51,56 @@ void MainWindow::slotEncrypt() { auto key_ids = mKeyList->getChecked(); - if (key_ids->empty()) { - QMessageBox::critical(nullptr, _("No Key Selected"), _("No Key Selected")); - return; - } - - auto key_getter = GpgFrontend::GpgKeyGetter::GetInstance(); - auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); - for (const auto& key : *keys) { - if (!key.CanEncrActual()) { - QMessageBox::information( - nullptr, _("Invalid Operation"), - QString( - _("The selected key contains a key that does not actually have a " - "encrypt usage.")) + - "<br/><br/>" + _("For example the Following Key:") + " <br/>" + - QString::fromStdString(key.uids()->front().uid())); - return; - } - } - - auto tmp = std::make_unique<ByteArray>(); - GpgEncrResult result = nullptr; GpgError error; bool if_error = false; - process_operation(this, _("Encrypting"), [&]() { - try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); - error = GpgFrontend::BasicOperator::GetInstance().Encrypt( - std::move(keys), buffer, tmp, result); - } catch (const std::runtime_error& e) { - if_error = true; + auto tmp = std::make_unique<ByteArray>(); + + if (key_ids->empty()) { + // Symmetric Encrypt + auto ret = + QMessageBox::warning(this, _("Warning"), + _("No Key Selected. Do you want to encrypt with a " + "symmetric cipher using a passphrase?"), + QMessageBox::Ok | QMessageBox::Cancel); + + if (ret == QMessageBox::Cancel) return; + + process_operation(this, _("Symmetrically Encrypting"), [&]() { + try { + auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + error = GpgFrontend::BasicOperator::GetInstance().EncryptSymmetric( + buffer, tmp, result); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } else { + auto key_getter = GpgFrontend::GpgKeyGetter::GetInstance(); + auto keys = GpgKeyGetter::GetInstance().GetKeys(key_ids); + for (const auto& key : *keys) { + if (!key.CanEncrActual()) { + QMessageBox::information( + nullptr, _("Invalid Operation"), + QString(_( + "The selected key contains a key that does not actually have a " + "encrypt usage.")) + + "<br/><br/>" + _("For example the Following Key:") + " <br/>" + + QString::fromStdString(key.uids()->front().uid())); + return; + } } - }); + + process_operation(this, _("Encrypting"), [&]() { + try { + auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + error = GpgFrontend::BasicOperator::GetInstance().Encrypt( + std::move(keys), buffer, tmp, result); + } catch (const std::runtime_error& e) { + if_error = true; + } + }); + } if (!if_error) { auto resultAnalyse = EncryptResultAnalyse(error, std::move(result)); diff --git a/src/ui/widgets/FilePage.cpp b/src/ui/widgets/FilePage.cpp index c51e0c76..ca6dedc1 100644 --- a/src/ui/widgets/FilePage.cpp +++ b/src/ui/widgets/FilePage.cpp @@ -25,94 +25,55 @@ #include "ui/widgets/FilePage.h" #include <boost/filesystem.hpp> +#include <string> #include "ui/MainWindow.h" #include "ui/SignalStation.h" +#include "ui_FilePage.h" namespace GpgFrontend::UI { -FilePage::FilePage(QWidget* parent) : QWidget(parent) { +FilePage::FilePage(QWidget* parent) + : QWidget(parent), ui(std::make_shared<Ui_FilePage>()) { + ui->setupUi(this); + firstParent = parent; dirModel = new QFileSystemModel(); dirModel->setRootPath(QDir::currentPath()); + dirModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - dirTreeView = new QTreeView(); - dirTreeView->setModel(dirModel); - dirTreeView->setAnimated(true); - dirTreeView->setIndentation(20); - dirTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - dirTreeView->setColumnWidth(0, 320); - dirTreeView->setRootIndex(dirModel->index(QDir::currentPath())); + ui->fileTreeView->setModel(dirModel); + ui->fileTreeView->setColumnWidth(0, 320); + ui->fileTreeView->sortByColumn(0, Qt::AscendingOrder); mPath = boost::filesystem::path(dirModel->rootPath().toStdString()); createPopupMenu(); - upLevelButton = new QPushButton(); - connect(upLevelButton, SIGNAL(clicked(bool)), this, SLOT(slotUpLevel())); - - QString buttonStyle = - "QPushButton{border:none;background-color:rgba(255, 255, 255, 0);}"; - - auto upPixmap = QPixmap(":up.png"); - upPixmap = - upPixmap.scaled(18, 18, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QIcon upButtonIcon(upPixmap); - upLevelButton->setIcon(upButtonIcon); - upLevelButton->setIconSize(upPixmap.rect().size()); - upLevelButton->setStyleSheet(buttonStyle); - - refreshButton = new QPushButton(_("Refresh")); - connect(refreshButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); - - goPathButton = new QPushButton(); - connect(goPathButton, SIGNAL(clicked(bool)), this, SLOT(slotGoPath())); + connect(ui->upPathButton, &QPushButton::clicked, this, + &FilePage::slotUpLevel); + connect(ui->refreshButton, &QPushButton::clicked, this, + &FilePage::slotGoPath); + ui->optionsButton->setMenu(optionPopUpMenu); - auto updatePixmap = QPixmap(":refresh.png"); - updatePixmap = updatePixmap.scaled(18, 18, Qt::KeepAspectRatio, - Qt::SmoothTransformation); - QIcon updateButtonIcon(updatePixmap); - goPathButton->setIcon(updateButtonIcon); - goPathButton->setIconSize(updatePixmap.rect().size()); - goPathButton->setStyleSheet(buttonStyle); - - pathEdit = new QLineEdit(); - pathEdit->setText(dirModel->rootPath()); + ui->pathEdit->setText(dirModel->rootPath()); pathEditCompleter = new QCompleter(this); pathCompleteModel = new QStringListModel(); pathEditCompleter->setModel(pathCompleteModel); pathEditCompleter->setCaseSensitivity(Qt::CaseInsensitive); pathEditCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion); - pathEdit->setCompleter(pathEditCompleter); - - auto* menuLayout = new QHBoxLayout(); - menuLayout->addWidget(upLevelButton); - menuLayout->setStretchFactor(upLevelButton, 1); - menuLayout->addWidget(pathEdit); - menuLayout->setStretchFactor(pathEdit, 10); - menuLayout->addWidget(goPathButton); - menuLayout->setStretchFactor(goPathButton, 1); - - auto* layout = new QVBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addLayout(menuLayout); - layout->setStretchFactor(menuLayout, 1); - layout->addWidget(dirTreeView); - layout->setStretchFactor(dirTreeView, 8); - - this->setLayout(layout); - - connect(dirTreeView, &QTreeView::clicked, this, + ui->pathEdit->setCompleter(pathEditCompleter); + + connect(ui->fileTreeView, &QTreeView::clicked, this, &FilePage::fileTreeViewItemClicked); - connect(dirTreeView, &QTreeView::doubleClicked, this, + connect(ui->fileTreeView, &QTreeView::doubleClicked, this, &FilePage::fileTreeViewItemDoubleClicked); - connect(dirTreeView, &QTreeView::customContextMenuRequested, this, + connect(ui->fileTreeView, &QTreeView::customContextMenuRequested, this, &FilePage::onCustomContextMenu); - connect(pathEdit, &QLineEdit::textChanged, [=]() { - auto path = pathEdit->text(); + connect(ui->pathEdit, &QLineEdit::textChanged, [=]() { + auto path = ui->pathEdit->text(); auto dir = QDir(path); if (path.endsWith("/") && dir.isReadable()) { auto dir_list = dir.entryInfoList(QDir::AllEntries); @@ -139,22 +100,30 @@ void FilePage::fileTreeViewItemClicked(const QModelIndex& index) { } void FilePage::slotUpLevel() { - QModelIndex currentRoot = dirTreeView->rootIndex(); + QModelIndex currentRoot = ui->fileTreeView->rootIndex(); - mPath = boost::filesystem::path( - dirModel->fileInfo(currentRoot).absoluteFilePath().toStdString()); + auto utf8_path = + dirModel->fileInfo(currentRoot).absoluteFilePath().toStdString(); + boost::filesystem::path path_obj(utf8_path); + + mPath = path_obj; LOG(INFO) << "get path" << mPath; - if (mPath.has_parent_path()) { + if (mPath.has_parent_path() && !mPath.parent_path().empty()) { mPath = mPath.parent_path(); LOG(INFO) << "parent path" << mPath; - pathEdit->setText(mPath.string().c_str()); - slotGoPath(); + ui->pathEdit->setText(mPath.string().c_str()); + this->slotGoPath(); } } void FilePage::fileTreeViewItemDoubleClicked(const QModelIndex& index) { - pathEdit->setText(dirModel->fileInfo(index).absoluteFilePath()); - slotGoPath(); + QFileInfo file_info(dirModel->fileInfo(index).absoluteFilePath()); + if (file_info.isFile()) { + slotOpenItem(); + } else { + ui->pathEdit->setText(file_info.filePath()); + slotGoPath(); + } } QString FilePage::getSelected() const { @@ -162,19 +131,20 @@ QString FilePage::getSelected() const { } void FilePage::slotGoPath() { - const auto path_edit = pathEdit->text().toStdString(); - LOG(INFO) << "get path edit" << path_edit; - if (mPath.string() != path_edit) mPath = path_edit; + const auto path_edit = ui->pathEdit->text().toStdString(); + boost::filesystem::path path_obj(path_edit); + + if (mPath.string() != path_edit) mPath = path_obj; auto fileInfo = QFileInfo(mPath.string().c_str()); if (fileInfo.isDir() && fileInfo.isReadable() && fileInfo.isExecutable()) { mPath = boost::filesystem::path(fileInfo.filePath().toStdString()); LOG(INFO) << "set path" << mPath; - dirTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); + ui->fileTreeView->setRootIndex(dirModel->index(fileInfo.filePath())); + dirModel->setRootPath(fileInfo.filePath()); for (int i = 1; i < dirModel->columnCount(); ++i) { - dirTreeView->resizeColumnToContents(i); + ui->fileTreeView->resizeColumnToContents(i); } - pathEdit->setText(mPath.generic_path().string().c_str()); - + ui->pathEdit->setText(mPath.generic_path().string().c_str()); } else { QMessageBox::critical( this, _("Error"), @@ -186,26 +156,35 @@ void FilePage::slotGoPath() { void FilePage::createPopupMenu() { popUpMenu = new QMenu(); - auto openItemAct = new QAction(_("Open"), this); - connect(openItemAct, SIGNAL(triggered()), this, SLOT(slotOpenItem())); - auto renameItemAct = new QAction(_("Rename"), this); - connect(renameItemAct, SIGNAL(triggered()), this, SLOT(slotRenameItem())); - auto deleteItemAct = new QAction(_("Delete"), this); - connect(deleteItemAct, SIGNAL(triggered()), this, SLOT(slotDeleteItem())); + openItemAct = new QAction(_("Open"), this); + connect(openItemAct, &QAction::triggered, this, &FilePage::slotOpenItem); + renameItemAct = new QAction(_("Rename"), this); + connect(renameItemAct, &QAction::triggered, this, &FilePage::slotRenameItem); + deleteItemAct = new QAction(_("Delete"), this); + connect(deleteItemAct, &QAction::triggered, this, &FilePage::slotDeleteItem); encryptItemAct = new QAction(_("Encrypt Sign"), this); - connect(encryptItemAct, SIGNAL(triggered()), this, SLOT(slotEncryptItem())); + connect(encryptItemAct, &QAction::triggered, this, + &FilePage::slotEncryptItem); decryptItemAct = new QAction(QString(_("Decrypt Verify")) + " " + _("(.gpg .asc)"), this); - connect(decryptItemAct, SIGNAL(triggered()), this, SLOT(slotDecryptItem())); + connect(decryptItemAct, &QAction::triggered, this, + &FilePage::slotDecryptItem); signItemAct = new QAction(_("Sign"), this); - connect(signItemAct, SIGNAL(triggered()), this, SLOT(slotSignItem())); + connect(signItemAct, &QAction::triggered, this, &FilePage::slotSignItem); verifyItemAct = new QAction(QString(_("Verify")) + " " + _("(.sig .gpg .asc)"), this); - connect(verifyItemAct, SIGNAL(triggered()), this, SLOT(slotVerifyItem())); + connect(verifyItemAct, &QAction::triggered, this, &FilePage::slotVerifyItem); hashCalculateAct = new QAction(_("Calculate Hash"), this); - connect(hashCalculateAct, SIGNAL(triggered()), this, - SLOT(slotCalculateHash())); + connect(hashCalculateAct, &QAction::triggered, this, + &FilePage::slotCalculateHash); + + mkdirAct = new QAction(_("Make New Directory"), this); + connect(mkdirAct, &QAction::triggered, this, &FilePage::slotMkdir); + + createEmptyFileAct = new QAction(_("Create Empty File"), this); + connect(createEmptyFileAct, &QAction::triggered, this, + &FilePage::slotCreateEmptyFile); popUpMenu->addAction(openItemAct); popUpMenu->addAction(renameItemAct); @@ -216,15 +195,47 @@ void FilePage::createPopupMenu() { popUpMenu->addAction(signItemAct); popUpMenu->addAction(verifyItemAct); popUpMenu->addSeparator(); + popUpMenu->addAction(mkdirAct); + popUpMenu->addAction(createEmptyFileAct); popUpMenu->addAction(hashCalculateAct); + + optionPopUpMenu = new QMenu(); + + auto showHiddenAct = new QAction(_("Show Hidden File"), this); + showHiddenAct->setCheckable(true); + connect(showHiddenAct, &QAction::triggered, this, [&](bool checked) { + LOG(INFO) << "Set Hidden" << checked; + if (checked) + dirModel->setFilter(dirModel->filter() | QDir::Hidden); + else + dirModel->setFilter(dirModel->filter() & ~QDir::Hidden); + dirModel->setRootPath(mPath.string().c_str()); + }); + optionPopUpMenu->addAction(showHiddenAct); + + auto showSystemAct = new QAction(_("Show System File"), this); + showSystemAct->setCheckable(true); + connect(showSystemAct, &QAction::triggered, this, [&](bool checked) { + LOG(INFO) << "Set Hidden" << checked; + if (checked) + dirModel->setFilter(dirModel->filter() | QDir::System); + else + dirModel->setFilter(dirModel->filter() & ~QDir::System); + dirModel->setRootPath(mPath.string().c_str()); + }); + optionPopUpMenu->addAction(showSystemAct); } void FilePage::onCustomContextMenu(const QPoint& point) { - QModelIndex index = dirTreeView->indexAt(point); + QModelIndex index = ui->fileTreeView->indexAt(point); selectedPath = boost::filesystem::path( dirModel->fileInfo(index).absoluteFilePath().toStdString()); LOG(INFO) << "right click" << selectedPath; if (index.isValid()) { + openItemAct->setEnabled(true); + renameItemAct->setEnabled(true); + deleteItemAct->setEnabled(true); + QFileInfo info(QString::fromStdString(selectedPath.string())); encryptItemAct->setEnabled( info.isFile() && (info.suffix() != "gpg" && info.suffix() != "sig")); @@ -236,9 +247,19 @@ void FilePage::onCustomContextMenu(const QPoint& point) { verifyItemAct->setEnabled( info.isFile() && (info.suffix() == "sig" || info.suffix() == "gpg")); hashCalculateAct->setEnabled(info.isFile() && info.isReadable()); - - popUpMenu->exec(dirTreeView->viewport()->mapToGlobal(point)); + } else { + openItemAct->setEnabled(false); + renameItemAct->setEnabled(false); + deleteItemAct->setEnabled(false); + + encryptItemAct->setEnabled(false); + encryptItemAct->setEnabled(false); + decryptItemAct->setEnabled(false); + signItemAct->setEnabled(false); + verifyItemAct->setEnabled(false); + hashCalculateAct->setEnabled(false); } + popUpMenu->exec(ui->fileTreeView->viewport()->mapToGlobal(point)); } void FilePage::slotOpenItem() { @@ -247,7 +268,7 @@ void FilePage::slotOpenItem() { if (info.isReadable() && info.isExecutable()) { const auto file_path = info.filePath().toStdString(); LOG(INFO) << "set path" << file_path; - pathEdit->setText(info.filePath()); + ui->pathEdit->setText(info.filePath()); slotGoPath(); } else { QMessageBox::critical(this, _("Error"), @@ -291,8 +312,8 @@ void FilePage::slotRenameItem() { } void FilePage::slotDeleteItem() { - QModelIndex index = dirTreeView->currentIndex(); - QVariant data = dirTreeView->model()->data(index); + QModelIndex index = ui->fileTreeView->currentIndex(); + QVariant data = ui->fileTreeView->model()->data(index); auto ret = QMessageBox::warning(this, _("Warning"), _("Are you sure you want to delete it?"), @@ -300,7 +321,7 @@ void FilePage::slotDeleteItem() { if (ret == QMessageBox::Cancel) return; - qDebug() << "Delete Item" << data.toString(); + LOG(INFO) << "Delete Item" << data.toString().toStdString(); if (!dirModel->remove(index)) { QMessageBox::critical(this, _("Error"), @@ -377,10 +398,49 @@ void FilePage::slotCalculateHash() { } } +void FilePage::slotMkdir() { + auto index = ui->fileTreeView->rootIndex(); + + QString new_dir_name; + bool ok; + new_dir_name = + QInputDialog::getText(this, _("Make New Directory"), _("Directory Name"), + QLineEdit::Normal, new_dir_name, &ok); + if (ok && !new_dir_name.isEmpty()) { + dirModel->mkdir(index, new_dir_name); + } +} + +void FilePage::slotCreateEmptyFile() { + auto root_path_str = dirModel->rootPath().toStdString(); + boost::filesystem::path root_path(root_path_str); + + QString new_file_name; + bool ok; + new_file_name = QInputDialog::getText(this, _("Create Empty File"), + _("Filename (you can given extension)"), + QLineEdit::Normal, new_file_name, &ok); + if (ok && !new_file_name.isEmpty()) { + auto file_path = root_path / new_file_name.toStdString(); + QFile new_file(file_path.string().c_str()); + if (!new_file.open(QIODevice::WriteOnly | QIODevice::NewOnly)) { + QMessageBox::critical(this, _("Error"), _("Unable to create the file.")); + } + new_file.close(); + } +} + void FilePage::keyPressEvent(QKeyEvent* event) { - qDebug() << "Key Press" << event->key(); - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + LOG(INFO) << "Key Press" << event->key(); + if (ui->pathEdit->hasFocus() && + (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) { slotGoPath(); + } else if (ui->fileTreeView->currentIndex().isValid()) { + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + slotOpenItem(); + else if (event->key() == Qt::Key_Delete || + event->key() == Qt::Key_Backspace) + slotDeleteItem(); } } diff --git a/src/ui/widgets/FilePage.h b/src/ui/widgets/FilePage.h index 2a9f5b57..03caf36b 100644 --- a/src/ui/widgets/FilePage.h +++ b/src/ui/widgets/FilePage.h @@ -30,6 +30,8 @@ #include "ui/GpgFrontendUI.h" #include "ui/widgets/InfoBoardWidget.h" +class Ui_FilePage; + namespace GpgFrontend::UI { class FilePage : public QWidget { @@ -63,6 +65,8 @@ class FilePage : public QWidget { void slotSignItem(); void slotVerifyItem(); void slotCalculateHash(); + void slotMkdir(); + void slotCreateEmptyFile(); void onCustomContextMenu(const QPoint& point); @@ -72,9 +76,9 @@ class FilePage : public QWidget { private: void createPopupMenu(); + std::shared_ptr<Ui_FilePage> ui; + QFileSystemModel* dirModel; - QTreeView* dirTreeView; - QLineEdit* pathEdit; QCompleter* pathEditCompleter; QStringListModel* pathCompleteModel; @@ -82,16 +86,18 @@ class FilePage : public QWidget { boost::filesystem::path mPath; boost::filesystem::path selectedPath; - QPushButton* upLevelButton; - QPushButton* goPathButton; - QPushButton* refreshButton; - QMenu* popUpMenu{}; + QMenu* optionPopUpMenu{}; QAction* encryptItemAct{}; QAction* decryptItemAct{}; QAction* signItemAct{}; QAction* verifyItemAct{}; QAction* hashCalculateAct{}; + QAction* mkdirAct{}; + QAction* openItemAct{}; + QAction* renameItemAct{}; + QAction* deleteItemAct{}; + QAction* createEmptyFileAct{}; QWidget* firstParent; }; diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index e0de75f9..1b7dbda0 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -26,87 +26,35 @@ #include "ui/SignalStation.h" #include "ui/settings/GlobalSettingStation.h" +#include "ui_InfoBoard.h" namespace GpgFrontend::UI { -InfoBoardWidget::InfoBoardWidget(QWidget* parent, KeyList* keyList) - : QWidget(parent), mKeyList(keyList) { - infoBoard = new QTextEdit(this); - infoBoard->setReadOnly(true); - infoBoard->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - infoBoard->setMinimumWidth(480); - infoBoard->setContentsMargins(0, 0, 0, 0); - - importFromKeyserverAct = - new QAction(_("Import missing key from Keyserver"), this); - connect(importFromKeyserverAct, SIGNAL(triggered()), this, - SLOT(slotImportFromKeyserver())); - - detailMenu = new QMenu(this); - detailMenu->addAction(importFromKeyserverAct); - importFromKeyserverAct->setVisible(false); - - auto* action_button_menu = new QWidget(); - action_button_menu->setContentsMargins(0, 0, 0, 0); - action_button_menu->setSizePolicy(QSizePolicy::Preferred, - QSizePolicy::Minimum); - action_button_menu->setFixedHeight(40); - - actionButtonLayout = new QHBoxLayout(); - actionButtonLayout->setContentsMargins(0, 0, 0, 0); - actionButtonLayout->setSpacing(0); - - auto* label = new QLabel(_("Actions")); - label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - label->setContentsMargins(0, 0, 0, 0); - mButtonGroup = new QButtonGroup(this); - - auto* bottom_layout = new QHBoxLayout(this); - bottom_layout->addWidget(label); - actionButtonLayout->addStretch(); - bottom_layout->addLayout(actionButtonLayout); - action_button_menu->setLayout(bottom_layout); - - QFrame* line; - line = new QFrame(this); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - line->setContentsMargins(0, 0, 0, 0); - - auto* notificationWidgetLayout = new QVBoxLayout(this); - notificationWidgetLayout->setContentsMargins(0, 0, 0, 0); - notificationWidgetLayout->setSpacing(0); - - notificationWidgetLayout->addWidget(infoBoard); - notificationWidgetLayout->setStretchFactor(infoBoard, 10); - notificationWidgetLayout->addWidget(action_button_menu); - notificationWidgetLayout->setStretchFactor(action_button_menu, 1); - notificationWidgetLayout->addWidget(line); - notificationWidgetLayout->setStretchFactor(line, 1); - notificationWidgetLayout->addStretch(0); - this->setLayout(notificationWidgetLayout); +InfoBoardWidget::InfoBoardWidget(QWidget* parent) + : QWidget(parent), ui(std::make_shared<Ui_InfoBoard>()) { + ui->setupUi(this); - connect(SignalStation::GetInstance(), &SignalStation::signalRefreshInfoBoard, - this, &InfoBoardWidget::slotRefresh); + ui->actionButtonLayout->addStretch(); + ui->actionLabel->setText(_("InfoBoard's Actions Menu")); + ui->copyButton->setText(_("Copy")); + ui->saveButton->setText(_("Save")); + ui->clearButton->setText(_("Clear")); - // set default size - infoBoard->resize(480, 120); - resize(480, 120); -} + connect(ui->copyButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotCopy); + connect(ui->saveButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotSave); + connect(ui->clearButton, &QPushButton::clicked, this, + &InfoBoardWidget::slotReset); -void InfoBoardWidget::slotImportFromKeyserver() { - auto* importDialog = new KeyServerImportDialog(false, this); - auto key_ids = std::make_unique<KeyIdArgsList>(); - for (const auto& key_id : *keysNotInList) { - key_ids->push_back(key_id.toStdString()); - } - importDialog->slotImport(key_ids); + connect(SignalStation::GetInstance(), &SignalStation::signalRefreshInfoBoard, + this, &InfoBoardWidget::slotRefresh); } void InfoBoardWidget::setInfoBoard(const QString& text, InfoBoardStatus verifyLabelStatus) { QString color; - infoBoard->clear(); + ui->infoBoard->clear(); switch (verifyLabelStatus) { case INFO_ERROR_OK: color = "#008000"; @@ -120,12 +68,12 @@ void InfoBoardWidget::setInfoBoard(const QString& text, default: break; } - infoBoard->append(text); + ui->infoBoard->append(text); - infoBoard->setAutoFillBackground(true); - QPalette status = infoBoard->palette(); + ui->infoBoard->setAutoFillBackground(true); + QPalette status = ui->infoBoard->palette(); status.setColor(QPalette::Text, color); - infoBoard->setPalette(status); + ui->infoBoard->setPalette(status); auto& settings = GlobalSettingStation::GetInstance().GetUISettings(); @@ -137,13 +85,13 @@ void InfoBoardWidget::setInfoBoard(const QString& text, } catch (...) { LOG(ERROR) << _("Setting Operation Error") << _("info_font_size"); } - infoBoard->setFont(QFont("Times", info_font_size)); + ui->infoBoard->setFont(QFont("Times", info_font_size)); } void InfoBoardWidget::slotRefresh(const QString& text, InfoBoardStatus status) { - infoBoard->clear(); + ui->infoBoard->clear(); setInfoBoard(text, status); - infoBoard->verticalScrollBar()->setValue(0); + ui->infoBoard->verticalScrollBar()->setValue(0); } void InfoBoardWidget::associateTextEdit(QTextEdit* edit) { @@ -156,9 +104,6 @@ void InfoBoardWidget::associateTextEdit(QTextEdit* edit) { void InfoBoardWidget::associateTabWidget(QTabWidget* tab) { if (mTextPage != nullptr) disconnect(mTextPage, SIGNAL(textChanged()), this, SLOT(slotReset())); - // if (mFileTreeView != nullptr) - // disconnect(mFileTreeView, &FilePage::pathChanged, this, - // &InfoBoardWidget::slotReset); if (mTabWidget != nullptr) { disconnect(mTabWidget, SIGNAL(tabBarClicked(int)), this, SLOT(slotReset())); connect(mTabWidget, SIGNAL(tabCloseRequested(int)), this, @@ -173,13 +118,14 @@ void InfoBoardWidget::associateTabWidget(QTabWidget* tab) { void InfoBoardWidget::addOptionalAction(const QString& name, const std::function<void()>& action) { + LOG(INFO) << "add option" << name.toStdString(); auto actionButton = new QPushButton(name); auto layout = new QHBoxLayout(); layout->setContentsMargins(5, 0, 5, 0); - infoBoard->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + ui->infoBoard->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // set margin from surroundings layout->addWidget(actionButton); - actionButtonLayout->addLayout(layout); + ui->actionButtonLayout->addLayout(layout); connect(actionButton, &QPushButton::clicked, this, [=]() { action(); }); } @@ -188,11 +134,11 @@ void InfoBoardWidget::addOptionalAction(const QString& name, */ void InfoBoardWidget::resetOptionActionsMenu() { // skip stretch - deleteWidgetsInLayout(actionButtonLayout, 1); + deleteWidgetsInLayout(ui->actionButtonLayout, 1); } void InfoBoardWidget::slotReset() { - this->infoBoard->clear(); + ui->infoBoard->clear(); resetOptionActionsMenu(); } @@ -214,4 +160,24 @@ void InfoBoardWidget::deleteWidgetsInLayout(QLayout* layout, int start_index) { } } +void InfoBoardWidget::slotCopy() { + auto* clipboard = QGuiApplication::clipboard(); + clipboard->setText(ui->infoBoard->toPlainText()); +} + +void InfoBoardWidget::slotSave() { + auto file_path = QFileDialog::getSaveFileName( + this, _("Save Information Board's Content"), {}, tr("Text (*.txt)")); + LOG(INFO) << "file path" << file_path.toStdString(); + QFile file(file_path); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + file.write(ui->infoBoard->toPlainText().toUtf8()); + } else { + QMessageBox::critical( + this, _("Error"), + _("The file path is not exists, unprivileged or unreachable.")); + } + file.close(); +} + } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/InfoBoardWidget.h b/src/ui/widgets/InfoBoardWidget.h index b7239adb..8d37be6c 100644 --- a/src/ui/widgets/InfoBoardWidget.h +++ b/src/ui/widgets/InfoBoardWidget.h @@ -29,6 +29,8 @@ #include "gpg/result_analyse/VerifyResultAnalyse.h" #include "ui/details/VerifyDetailsDialog.h" +class Ui_InfoBoard; + namespace GpgFrontend::UI { /** @@ -53,7 +55,7 @@ class InfoBoardWidget : public QWidget { * @param ctx The GPGme-Context * @param parent The parent widget */ - explicit InfoBoardWidget(QWidget* parent, KeyList* keyList); + explicit InfoBoardWidget(QWidget* parent); void associateTextEdit(QTextEdit* edit); @@ -72,17 +74,8 @@ class InfoBoardWidget : public QWidget { */ void setInfoBoard(const QString& text, InfoBoardStatus verifyLabelStatus); - QStringList* keysNotInList; /** List with keys, which are in signature but not - in keylist */ - public slots: - /** - * @details Import the keys contained in keysNotInList from keyserver - * - */ - void slotImportFromKeyserver(); - void slotReset(); /** @@ -90,19 +83,17 @@ class InfoBoardWidget : public QWidget { */ void slotRefresh(const QString& text, InfoBoardStatus status); + private slots: + + void slotCopy(); + + void slotSave(); + private: - QMenu* detailMenu; /** Menu for te Button in verfiyNotification */ - QAction* importFromKeyserverAct; /** Action for importing keys from keyserver - which are notin keylist */ - QTextEdit* infoBoard; - KeyList* mKeyList; /** Table holding the keys */ + std::shared_ptr<Ui_InfoBoard> ui; QTextEdit* mTextPage{nullptr}; /** TextEdit associated to the notification */ - QTabWidget* mTabWidget{ - nullptr}; /** TreeView associated to the notification */ - - QHBoxLayout* actionButtonLayout; - QButtonGroup* mButtonGroup; + QTabWidget* mTabWidget{nullptr}; void deleteWidgetsInLayout(QLayout* layout, int start_index = 0); }; diff --git a/src/ui/widgets/KeyList.cpp b/src/ui/widgets/KeyList.cpp index 5931e337..02a12e80 100644 --- a/src/ui/widgets/KeyList.cpp +++ b/src/ui/widgets/KeyList.cpp @@ -46,6 +46,7 @@ void KeyList::init() { mGroupTab = new QTabWidget(); mGroupTab->setMovable(true); mGroupTab->setTabsClosable(false); + mGroupTab->setDocumentMode(true); auto* layout = new QVBoxLayout; layout->addWidget(mGroupTab); diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index ec6c6c7a..036c69d4 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -77,12 +77,13 @@ void TextEdit::slotNewFileTab() const { void TextEdit::slotOpenFile(QString& path) { QFile file(path); - LOG(INFO) << " path" << path.toStdString(); + LOG(INFO) << "path" << path.toStdString(); auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); if (result) { auto* page = new EditorPage(path); QApplication::setOverrideCursor(Qt::WaitCursor); - tabWidget->addTab(page, strippedName(path)); + auto index = tabWidget->addTab(page, strippedName(path)); + tabWidget->setTabIcon(index, QIcon(":file.png")); tabWidget->setCurrentIndex(tabWidget->count() - 1); QApplication::restoreOverrideCursor(); page->getTextPage()->setFocus(); @@ -96,7 +97,6 @@ void TextEdit::slotOpenFile(QString& path) { } file.close(); - LOG(INFO) << "done"; } void TextEdit::slotOpen() { |