safePasswords #36

Manually merged
blue merged 9 commits from safePasswords into master 2020-04-14 16:32:29 +00:00
15 changed files with 315 additions and 163 deletions
Showing only changes of commit b95028e33e - Show all commits

View File

@ -6,6 +6,7 @@
- store it in plain text with the config (like it always was) - store it in plain text with the config (like it always was)
- store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is) - store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is)
- ask the account password on each program launch - ask the account password on each program launch
- store it in KWallet which is dynamically loaded
### Bug fixes ### Bug fixes
- never updating MUC avatars now update - never updating MUC avatars now update

View File

@ -62,15 +62,39 @@ add_custom_target(translations ALL DEPENDS ${QM_FILES})
qt5_add_resources(RCC resources/resources.qrc) qt5_add_resources(RCC resources/resources.qrc)
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
target_link_libraries(squawk Qt5::Widgets)
option(SYSTEM_QXMPP "Use system qxmpp lib" ON) option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
option(WITH_KWALLET "Build KWallet support module" ON)
if (SYSTEM_QXMPP)
find_package(QXmpp CONFIG)
if (NOT QXmpp_FOUND)
set(SYSTEM_QXMPP OFF)
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
else()
message("Building with system QXmpp")
endif()
endif()
if(NOT SYSTEM_QXMPP) if(NOT SYSTEM_QXMPP)
add_subdirectory(external/qxmpp) add_subdirectory(external/qxmpp)
endif() endif()
if (WITH_KWALLET)
find_package(KF5Wallet CONFIG)
if (NOT KF5Wallet_FOUND)
set(WITH_KWALLET OFF)
message("KWallet package wasn't found, KWallet support module wouldn't be built")
else()
add_definitions(-DWITH_KWALLET)
message("Building with support of KWallet")
endif()
endif()
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
target_link_libraries(squawk Qt5::Widgets)
add_subdirectory(ui) add_subdirectory(ui)
add_subdirectory(core) add_subdirectory(core)

View File

@ -60,6 +60,16 @@ $ cmake .. -D SYSTEM_QXMPP=False
$ cmake --build . $ cmake --build .
``` ```
### List of keys
Here is the list of keys you can pass to configuration phase of `cmake ..`.
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
Each key is supposed to be passed like that
## License ## License
This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details

View File

@ -20,13 +20,13 @@ set(squawkCORE_SRC
adapterFuctions.cpp adapterFuctions.cpp
) )
add_subdirectory(passwordStorageEngines)
# Tell CMake to create the helloworld executable # Tell CMake to create the helloworld executable
add_library(squawkCORE ${squawkCORE_SRC}) add_library(squawkCORE ${squawkCORE_SRC})
add_subdirectory(passwordStorageEngines)
if(SYSTEM_QXMPP) if(SYSTEM_QXMPP)
find_package(QXmpp CONFIG REQUIRED)
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES) get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES}) target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
endif() endif()
@ -39,4 +39,6 @@ target_link_libraries(squawkCORE Qt5::Xml)
target_link_libraries(squawkCORE qxmpp) target_link_libraries(squawkCORE qxmpp)
target_link_libraries(squawkCORE lmdb) target_link_libraries(squawkCORE lmdb)
target_link_libraries(squawkCORE simpleCrypt) target_link_libraries(squawkCORE simpleCrypt)
if (WITH_KWALLET)
target_link_libraries(squawkCORE kwalletPSE) target_link_libraries(squawkCORE kwalletPSE)
endif()

View File

@ -1,11 +1,12 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(pse) project(pse)
if (WITH_KWALLET)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
find_package(Qt5Core CONFIG REQUIRED) find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Gui CONFIG REQUIRED) find_package(Qt5Gui CONFIG REQUIRED)
find_package(KF5Wallet CONFIG REQUIRED)
get_target_property(KWALLET_INTERFACE_INCLUDE_DIRECTORIES KF5::Wallet INTERFACE_INCLUDE_DIRECTORIES) get_target_property(KWALLET_INTERFACE_INCLUDE_DIRECTORIES KF5::Wallet INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES Qt5::Gui INTERFACE_INCLUDE_DIRECTORIES) get_target_property(Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES Qt5::Gui INTERFACE_INCLUDE_DIRECTORIES)
@ -34,3 +35,8 @@ target_link_libraries(kwalletWrapper KF5::Wallet)
target_link_libraries(kwalletWrapper Qt5::Core) target_link_libraries(kwalletWrapper Qt5::Core)
install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

View File

@ -28,7 +28,7 @@ Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0;
Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0; Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0;
Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial; Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial;
QLibrary Core::PSE::KWallet::lib("./libkwalletWrapper.so"); QLibrary Core::PSE::KWallet::lib("kwalletWrapper");
Core::PSE::KWallet::KWallet(): Core::PSE::KWallet::KWallet():
QObject(), QObject(),
@ -41,6 +41,11 @@ Core::PSE::KWallet::KWallet():
if (sState == initial) { if (sState == initial) {
lib.load(); lib.load();
if (!lib.isLoaded()) { //fallback from the build directory
lib.setFileName("./core/passwordStorageEngines/libkwalletWrapper.so");
lib.load();
}
if (lib.isLoaded()) { if (lib.isLoaded()) {
openWallet = (OpenWallet) lib.resolve("openWallet"); openWallet = (OpenWallet) lib.resolve("openWallet");
networkWallet = (NetworkWallet) lib.resolve("networkWallet"); networkWallet = (NetworkWallet) lib.resolve("networkWallet");

View File

@ -27,8 +27,10 @@ Core::Squawk::Squawk(QObject* parent):
accounts(), accounts(),
amap(), amap(),
network(), network(),
waitingForAccounts(0), waitingForAccounts(0)
kwallet() #ifdef WITH_KWALLET
,kwallet()
#endif
{ {
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse); connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress); connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
@ -36,13 +38,15 @@ Core::Squawk::Squawk(QObject* parent):
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress); connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError); connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
#ifdef WITH_KWALLET
if (kwallet.supportState() == PSE::KWallet::success) { if (kwallet.supportState() == PSE::KWallet::success) {
qDebug() << "KWallet support detected";
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened); connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword); connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword); connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword);
Shared::Global::setSupported("KWallet", true);
} }
#endif
} }
Core::Squawk::~Squawk() Core::Squawk::~Squawk()
@ -236,9 +240,11 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
} }
break; break;
case Shared::ConnectionState::connected: case Shared::ConnectionState::connected:
#ifdef WITH_KWALLET
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) { if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true); kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true);
} }
#endif
break; break;
default: default:
break; break;
@ -415,12 +421,14 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt())); acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
} }
#ifdef WITH_KWALLET
if (acc->getPasswordType() == Shared::AccountPassword::kwallet if (acc->getPasswordType() == Shared::AccountPassword::kwallet
&& kwallet.supportState() == PSE::KWallet::success && kwallet.supportState() == PSE::KWallet::success
&& !needToReconnect && !needToReconnect
) { ) {
kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true); kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true);
} }
#endif
emit changeAccount(name, map); emit changeAccount(name, map);
} }
@ -716,11 +724,15 @@ void Core::Squawk::parseAccount(
break; break;
case Shared::AccountPassword::kwallet: { case Shared::AccountPassword::kwallet: {
addAccount(login, server, QString(), name, resource, passwordType); addAccount(login, server, QString(), name, resource, passwordType);
#ifdef WITH_KWALLET
if (kwallet.supportState() == PSE::KWallet::success) { if (kwallet.supportState() == PSE::KWallet::success) {
kwallet.requestReadPassword(name); kwallet.requestReadPassword(name);
} else { } else {
#endif
emit requestPassword(name); emit requestPassword(name);
#ifdef WITH_KWALLET
} }
#endif
} }
} }
} }

View File

@ -34,7 +34,9 @@
#include "networkaccess.h" #include "networkaccess.h"
#include "external/simpleCrypt/simplecrypt.h" #include "external/simpleCrypt/simplecrypt.h"
#ifdef WITH_KWALLET
#include "passwordStorageEngines/kwallet.h" #include "passwordStorageEngines/kwallet.h"
#endif
namespace Core namespace Core
{ {
@ -116,7 +118,10 @@ private:
Shared::Availability state; Shared::Availability state;
NetworkAccess network; NetworkAccess network;
uint8_t waitingForAccounts; uint8_t waitingForAccounts;
#ifdef WITH_KWALLET
PSE::KWallet kwallet; PSE::KWallet kwallet;
#endif
private slots: private slots:
void addAccount( void addAccount(

View File

@ -24,53 +24,62 @@ Shared::Global* Shared::Global::instance = 0;
Shared::Global::Global(): Shared::Global::Global():
availability({ availability({
tr("Online"), tr("Online", "Availability"),
tr("Away"), tr("Away", "Availability"),
tr("Absent"), tr("Absent", "Availability"),
tr("Busy"), tr("Busy", "Availability"),
tr("Chatty"), tr("Chatty", "Availability"),
tr("Invisible"), tr("Invisible", "Availability"),
tr("Offline") tr("Offline", "Availability")
}), }),
connectionState({ connectionState({
tr("Disconnected"), tr("Disconnected", "ConnectionState"),
tr("Connecting"), tr("Connecting", "ConnectionState"),
tr("Connected"), tr("Connected", "ConnectionState"),
tr("Error") tr("Error", "ConnectionState")
}), }),
subscriptionState({ subscriptionState({
tr("None"), tr("None", "SubscriptionState"),
tr("From"), tr("From", "SubscriptionState"),
tr("To"), tr("To", "SubscriptionState"),
tr("Both"), tr("Both", "SubscriptionState"),
tr("Unknown") tr("Unknown", "SubscriptionState")
}), }),
affiliation({ affiliation({
tr("Unspecified"), tr("Unspecified", "Affiliation"),
tr("Outcast"), tr("Outcast", "Affiliation"),
tr("Nobody"), tr("Nobody", "Affiliation"),
tr("Member"), tr("Member", "Affiliation"),
tr("Admin"), tr("Admin", "Affiliation"),
tr("Owner") tr("Owner", "Affiliation")
}), }),
role({ role({
tr("Unspecified"), tr("Unspecified", "Role"),
tr("Nobody"), tr("Nobody", "Role"),
tr("Visitor"), tr("Visitor", "Role"),
tr("Participant"), tr("Participant", "Role"),
tr("Moderator") tr("Moderator", "Role")
}), }),
messageState({ messageState({
tr("Pending"), tr("Pending", "MessageState"),
tr("Sent"), tr("Sent", "MessageState"),
tr("Delivered"), tr("Delivered", "MessageState"),
tr("Error") tr("Error", "MessageState")
}), }),
accountPassword({ accountPassword({
tr("Plain"), tr("Plain", "AccountPassword"),
tr("Jammed"), tr("Jammed", "AccountPassword"),
tr("Always Ask"), tr("Always Ask", "AccountPassword"),
tr("KWallet") tr("KWallet", "AccountPassword")
}),
accountPasswordDescription({
tr("Your password is going to be stored in config file in plain text", "AccountPasswordDescription"),
tr("Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"),
tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"),
tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription")
}),
pluginSupport({
{"KWallet", false}
}) })
{ {
if (instance != 0) { if (instance != 0) {
@ -120,6 +129,28 @@ QString Shared::Global::getName(Shared::AccountPassword ap)
return instance->accountPassword[static_cast<int>(ap)]; return instance->accountPassword[static_cast<int>(ap)];
} }
void Shared::Global::setSupported(const QString& pluginName, bool support)
{
std::map<QString, bool>::iterator itr = instance->pluginSupport.find(pluginName);
if (itr != instance->pluginSupport.end()) {
itr->second = support;
}
}
bool Shared::Global::supported(const QString& pluginName)
{
std::map<QString, bool>::iterator itr = instance->pluginSupport.find(pluginName);
if (itr != instance->pluginSupport.end()) {
return itr->second;
}
return false;
}
QString Shared::Global::getDescription(Shared::AccountPassword ap)
{
return instance->accountPasswordDescription[static_cast<int>(ap)];
}
#define FROM_INT_INPL(Enum) \ #define FROM_INT_INPL(Enum) \
template<> \ template<> \
Enum Shared::Global::fromInt(int src) \ Enum Shared::Global::fromInt(int src) \

View File

@ -45,6 +45,8 @@ namespace Shared {
static QString getName(Message::State rl); static QString getName(Message::State rl);
static QString getName(AccountPassword ap); static QString getName(AccountPassword ap);
static QString getDescription(AccountPassword ap);
const std::deque<QString> availability; const std::deque<QString> availability;
const std::deque<QString> connectionState; const std::deque<QString> connectionState;
const std::deque<QString> subscriptionState; const std::deque<QString> subscriptionState;
@ -53,6 +55,11 @@ namespace Shared {
const std::deque<QString> messageState; const std::deque<QString> messageState;
const std::deque<QString> accountPassword; const std::deque<QString> accountPassword;
const std::deque<QString> accountPasswordDescription;
static bool supported(const QString& pluginName);
static void setSupported(const QString& pluginName, bool support);
template<typename T> template<typename T>
static T fromInt(int src); static T fromInt(int src);
@ -74,6 +81,8 @@ namespace Shared {
private: private:
static Global* instance; static Global* instance;
std::map<QString, bool> pluginSupport;
}; };
} }

View File

@ -67,7 +67,7 @@
</message> </message>
<message> <message>
<source>Password storage</source> <source>Password storage</source>
<translation type="unfinished"></translation> <translation>Хранение пароля</translation>
</message> </message>
</context> </context>
<context> <context>
@ -122,173 +122,200 @@ p, li { white-space: pre-wrap; }
</context> </context>
<context> <context>
<name>Global</name> <name>Global</name>
<message>
<source>Disconnected</source>
<translation>Отключен</translation>
</message>
<message>
<source>Connecting</source>
<translation>Подключается</translation>
</message>
<message>
<source>Connected</source>
<translation>Подключен</translation>
</message>
<message>
<source>Error</source>
<translation>Ошибка</translation>
</message>
<message> <message>
<source>Online</source> <source>Online</source>
<comment>Availability</comment>
<translation>В сети</translation> <translation>В сети</translation>
</message> </message>
<message> <message>
<source>Away</source> <source>Away</source>
<comment>Availability</comment>
<translation>Отошел</translation> <translation>Отошел</translation>
</message> </message>
<message>
<source>Busy</source>
<translation>Занят</translation>
</message>
<message> <message>
<source>Absent</source> <source>Absent</source>
<comment>Availability</comment>
<translation>Недоступен</translation> <translation>Недоступен</translation>
</message> </message>
<message>
<source>Busy</source>
<comment>Availability</comment>
<translation>Занят</translation>
</message>
<message> <message>
<source>Chatty</source> <source>Chatty</source>
<comment>Availability</comment>
<translation>Готов поболтать</translation> <translation>Готов поболтать</translation>
</message> </message>
<message> <message>
<source>Invisible</source> <source>Invisible</source>
<comment>Availability</comment>
<translation>Невидимый</translation> <translation>Невидимый</translation>
</message> </message>
<message> <message>
<source>Offline</source> <source>Offline</source>
<comment>Availability</comment>
<translation>Отключен</translation> <translation>Отключен</translation>
</message> </message>
<message>
<source>Disconnected</source>
<comment>ConnectionState</comment>
<translation>Отключен</translation>
</message>
<message>
<source>Connecting</source>
<comment>ConnectionState</comment>
<translation>Подключается</translation>
</message>
<message>
<source>Connected</source>
<comment>ConnectionState</comment>
<translation>Подключен</translation>
</message>
<message>
<source>Error</source>
<comment>ConnectionState</comment>
<translation>Ошибка</translation>
</message>
<message> <message>
<source>None</source> <source>None</source>
<comment>SubscriptionState</comment>
<translation>Нет</translation> <translation>Нет</translation>
</message> </message>
<message> <message>
<source>From</source> <source>From</source>
<comment>SubscriptionState</comment>
<translation>Входящая</translation> <translation>Входящая</translation>
</message> </message>
<message> <message>
<source>To</source> <source>To</source>
<comment>SubscriptionState</comment>
<translation>Исходящая</translation> <translation>Исходящая</translation>
</message> </message>
<message> <message>
<source>Both</source> <source>Both</source>
<comment>SubscriptionState</comment>
<translation>Взаимная</translation> <translation>Взаимная</translation>
</message> </message>
<message> <message>
<source>Unknown</source> <source>Unknown</source>
<comment>SubscriptionState</comment>
<translation>Неизвестно</translation> <translation>Неизвестно</translation>
</message> </message>
<message> <message>
<source>Unspecified</source> <source>Unspecified</source>
<comment>Affiliation</comment>
<translation>Не назначено</translation> <translation>Не назначено</translation>
</message> </message>
<message> <message>
<source>Outcast</source> <source>Outcast</source>
<comment>Affiliation</comment>
<translation>Изгой</translation> <translation>Изгой</translation>
</message> </message>
<message> <message>
<source>Nobody</source> <source>Nobody</source>
<comment>Affiliation</comment>
<translation>Никто</translation> <translation>Никто</translation>
</message> </message>
<message> <message>
<source>Member</source> <source>Member</source>
<comment>Affiliation</comment>
<translation>Участник</translation> <translation>Участник</translation>
</message> </message>
<message> <message>
<source>Admin</source> <source>Admin</source>
<comment>Affiliation</comment>
<translation>Администратор</translation> <translation>Администратор</translation>
</message> </message>
<message> <message>
<source>Owner</source> <source>Owner</source>
<comment>Affiliation</comment>
<translation>Владелец</translation> <translation>Владелец</translation>
</message> </message>
<message>
<source>Unspecified</source>
<comment>Role</comment>
<translation>Не назначено</translation>
</message>
<message>
<source>Nobody</source>
<comment>Role</comment>
<translation>Никто</translation>
</message>
<message> <message>
<source>Visitor</source> <source>Visitor</source>
<comment>Role</comment>
<translation>Гость</translation> <translation>Гость</translation>
</message> </message>
<message> <message>
<source>Participant</source> <source>Participant</source>
<comment>Role</comment>
<translation>Участник</translation> <translation>Участник</translation>
</message> </message>
<message> <message>
<source>Moderator</source> <source>Moderator</source>
<comment>Role</comment>
<translation>Модератор</translation> <translation>Модератор</translation>
</message> </message>
<message>
<source>Not specified</source>
<translation type="vanished">Не указан</translation>
</message>
<message>
<source>Personal</source>
<translation type="vanished">Личный</translation>
</message>
<message>
<source>Business</source>
<translation type="vanished">Рабочий</translation>
</message>
<message>
<source>Fax</source>
<translation type="vanished">Факс</translation>
</message>
<message>
<source>Pager</source>
<translation type="vanished">Пэйджер</translation>
</message>
<message>
<source>Voice</source>
<translation type="vanished">Стационарный</translation>
</message>
<message>
<source>Cell</source>
<translation type="vanished">Мобильный</translation>
</message>
<message>
<source>Video</source>
<translation type="vanished">Видеофон</translation>
</message>
<message>
<source>Modem</source>
<translation type="vanished">Модем</translation>
</message>
<message>
<source>Other</source>
<translation type="vanished">Другой</translation>
</message>
<message> <message>
<source>Pending</source> <source>Pending</source>
<translation>В процессе</translation> <comment>MessageState</comment>
<translation>В процессе отправки</translation>
</message> </message>
<message> <message>
<source>Sent</source> <source>Sent</source>
<comment>MessageState</comment>
<translation>Отправлено</translation> <translation>Отправлено</translation>
</message> </message>
<message> <message>
<source>Delivered</source> <source>Delivered</source>
<comment>MessageState</comment>
<translation>Доставлено</translation> <translation>Доставлено</translation>
</message> </message>
<message>
<source>Error</source>
<comment>MessageState</comment>
<translation>Ошибка</translation>
</message>
<message> <message>
<source>Plain</source> <source>Plain</source>
<translation type="unfinished"></translation> <comment>AccountPassword</comment>
<translation>Открытый текст</translation>
</message> </message>
<message> <message>
<source>Jammed</source> <source>Jammed</source>
<translation type="unfinished"></translation> <comment>AccountPassword</comment>
</message> <translation>Обфусцированный</translation>
<message>
<source>KWallet</source>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Always Ask</source> <source>Always Ask</source>
<translation type="unfinished"></translation> <comment>AccountPassword</comment>
<translation>Всегда спрашивать</translation>
</message>
<message>
<source>KWallet</source>
<comment>AccountPassword</comment>
<translation>KWallet</translation>
</message>
<message>
<source>Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it&apos;s not</source>
<comment>AccountPasswordDescription</comment>
<translation>Ваш пароль будет храниться в обфусцированном виде в конфигурационном файле. Обфускация производится с помощью постоянного числа, которое можно найти в исходном коде программы. Это может и выглядит как шифрование но им не является</translation>
</message>
<message>
<source>Squawk is going to query you for the password on every start of the program</source>
<comment>AccountPasswordDescription</comment>
<translation>Squawk будет спрашивать пароль от этой учетной записи каждый раз при запуске</translation>
</message>
<message>
<source>Your password is going to be stored in config file in plain text</source>
<comment>AccountPasswordDescription</comment>
<translation>Ваш пароль будет храниться в конфигурационном файле открытым текстром</translation>
</message>
<message>
<source>Your password is going to be stored in KDE wallet storage (KWallet). You&apos;re going to be queried for permissions</source>
<comment>AccountPasswordDescription</comment>
<translation>Ваш пароль будет храниться в бумажнике KDE (KWallet). В первый раз программа попросит разрешения для доступа к бумажнику</translation>
</message> </message>
</context> </context>
<context> <context>
@ -337,10 +364,6 @@ p, li { white-space: pre-wrap; }
</context> </context>
<context> <context>
<name>Message</name> <name>Message</name>
<message>
<source>Download</source>
<translation type="vanished">Скачать</translation>
</message>
<message> <message>
<source>Open</source> <source>Open</source>
<translation>Открыть</translation> <translation>Открыть</translation>
@ -383,25 +406,6 @@ You can try again</source>
<translation>Загружается...</translation> <translation>Загружается...</translation>
</message> </message>
</context> </context>
<context>
<name>Models::Accounts</name>
<message>
<source>Name</source>
<translation type="vanished">Имя</translation>
</message>
<message>
<source>Server</source>
<translation type="vanished">Сервер</translation>
</message>
<message>
<source>State</source>
<translation type="vanished">Состояние</translation>
</message>
<message>
<source>Error</source>
<translation type="vanished">Ошибка</translation>
</message>
</context>
<context> <context>
<name>Models::Room</name> <name>Models::Room</name>
<message> <message>
@ -625,6 +629,14 @@ to be displayed as %1</source>
<source>Attached file</source> <source>Attached file</source>
<translation>Прикрепленный файл</translation> <translation>Прикрепленный файл</translation>
</message> </message>
<message>
<source>Input the password for account %1</source>
<translation>Введите пароль для учетной записи %1</translation>
</message>
<message>
<source>Password for account %1</source>
<translation>Пароль для учетной записи %1</translation>
</message>
</context> </context>
<context> <context>
<name>VCard</name> <name>VCard</name>

View File

@ -25,11 +25,19 @@ Account::Account():
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
connect(m_ui->passwordType, qOverload<int>(&QComboBox::currentIndexChanged), this, &Account::onComboboxChange);
for (int i = static_cast<int>(Shared::AccountPasswordLowest); i < static_cast<int>(Shared::AccountPasswordHighest) + 1; ++i) { for (int i = static_cast<int>(Shared::AccountPasswordLowest); i < static_cast<int>(Shared::AccountPasswordHighest) + 1; ++i) {
Shared::AccountPassword ap = static_cast<Shared::AccountPassword>(i); Shared::AccountPassword ap = static_cast<Shared::AccountPassword>(i);
m_ui->passwordType->addItem(Shared::Global::getName(ap)); m_ui->passwordType->addItem(Shared::Global::getName(ap));
} }
m_ui->passwordType->setCurrentIndex(static_cast<int>(Shared::AccountPassword::plain)); m_ui->passwordType->setCurrentIndex(static_cast<int>(Shared::AccountPassword::plain));
if (!Shared::Global::supported("KWallet")) {
QStandardItemModel *model = static_cast<QStandardItemModel*>(m_ui->passwordType->model());
QStandardItem *item = model->item(static_cast<int>(Shared::AccountPassword::kwallet));
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
} }
Account::~Account() Account::~Account()
@ -63,3 +71,9 @@ void Account::setData(const QMap<QString, QVariant>& data)
m_ui->resource->setText(data.value("resource").toString()); m_ui->resource->setText(data.value("resource").toString());
m_ui->passwordType->setCurrentIndex(data.value("passwordType").toInt()); m_ui->passwordType->setCurrentIndex(data.value("passwordType").toInt());
} }
void Account::onComboboxChange(int index)
{
QString description = Shared::Global::getDescription(Shared::Global::fromInt<Shared::AccountPassword>(index));
m_ui->comment->setText(description);
}

View File

@ -24,6 +24,7 @@
#include <QMap> #include <QMap>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QStandardItemModel>
#include "shared/global.h" #include "shared/global.h"
@ -44,6 +45,9 @@ public:
void setData(const QMap<QString, QVariant>& data); void setData(const QMap<QString, QVariant>& data);
void lockId(); void lockId();
private slots:
void onComboboxChange(int index);
private: private:
QScopedPointer<Ui::Account> m_ui; QScopedPointer<Ui::Account> m_ui;
}; };

View File

@ -114,14 +114,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Resource</string> <string>Resource</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QLineEdit" name="resource"> <widget class="QLineEdit" name="resource">
<property name="toolTip"> <property name="toolTip">
<string>A resource name like &quot;Home&quot; or &quot;Work&quot;</string> <string>A resource name like &quot;Home&quot; or &quot;Work&quot;</string>
@ -141,6 +141,22 @@
<item row="4" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="passwordType"/> <widget class="QComboBox" name="passwordType"/>
</item> </item>
<item row="5" column="1">
<widget class="QLabel" name="comment">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View File

@ -37,6 +37,7 @@ Accounts::Accounts(Models::Accounts* p_model, QWidget *parent) :
m_ui->tableView->setModel(model); m_ui->tableView->setModel(model);
connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Accounts::onSelectionChanged); connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Accounts::onSelectionChanged);
connect(p_model, &Models::Accounts::changed, this, &Accounts::updateConnectButton); connect(p_model, &Models::Accounts::changed, this, &Accounts::updateConnectButton);
connect(m_ui->tableView, &QTableView::doubleClicked, this, &Accounts::onEditButton);
} }
Accounts::~Accounts() = default; Accounts::~Accounts() = default;