Browse Source

Merge pull request 'safePasswords' (#36) from safePasswords into master

pull/39/head 0.1.4
Blue 2 years ago
parent
commit
bdca0aa6b1
  1. 76
      CHANGELOG.md
  2. 44
      CMakeLists.txt
  3. 10
      README.md
  4. 9
      core/CMakeLists.txt
  5. 26
      core/account.cpp
  6. 11
      core/account.h
  7. 33
      core/archive.cpp
  8. 3
      core/archive.h
  9. 98
      core/conference.cpp
  10. 15
      core/conference.h
  11. 37
      core/passwordStorageEngines/CMakeLists.txt
  12. 230
      core/passwordStorageEngines/kwallet.cpp
  13. 112
      core/passwordStorageEngines/kwallet.h
  14. 33
      core/passwordStorageEngines/wrappers/kwallet.cpp
  15. 34
      core/rosteritem.cpp
  16. 8
      core/rosteritem.h
  17. 250
      core/squawk.cpp
  18. 43
      core/squawk.h
  19. 16
      external/simpleCrypt/CMakeLists.txt
  20. 252
      external/simpleCrypt/simplecrypt.cpp
  21. 225
      external/simpleCrypt/simplecrypt.h
  22. 6
      main.cpp
  23. 2
      order.h
  24. 6
      packaging/Archlinux/PKGBUILD
  25. 37
      shared/enums.h
  26. 168
      shared/global.cpp
  27. 29
      shared/global.h
  28. 2
      shared/message.h
  29. 19
      shared/utils.cpp
  30. 2
      shared/utils.h
  31. 182
      translations/squawk.ru.ts
  32. 32
      ui/models/account.cpp
  33. 7
      ui/models/account.h
  34. 29
      ui/models/participant.cpp
  35. 1
      ui/models/participant.h
  36. 27
      ui/models/room.cpp
  37. 2
      ui/models/room.h
  38. 10
      ui/models/roster.cpp
  39. 2
      ui/models/roster.h
  40. 327
      ui/squawk.cpp
  41. 18
      ui/squawk.h
  42. 160
      ui/squawk.ui
  43. 29
      ui/utils/message.cpp
  44. 1
      ui/utils/message.h
  45. 67
      ui/utils/messageline.cpp
  46. 4
      ui/utils/messageline.h
  47. 29
      ui/widgets/account.cpp
  48. 8
      ui/widgets/account.h
  49. 34
      ui/widgets/account.ui
  50. 15
      ui/widgets/accounts.cpp
  51. 10
      ui/widgets/accounts.h
  52. 100
      ui/widgets/conversation.cpp
  53. 12
      ui/widgets/conversation.h
  54. 53
      ui/widgets/conversation.ui
  55. 4
      ui/widgets/room.cpp
  56. 6
      ui/widgets/vcard/vcard.cpp

76
CHANGELOG.md

@ -0,0 +1,76 @@
# Changelog
## Squawk 0.1.4 (Apr 14, 2020)
### New features
- message line now is in the same window with roster (new window dialog is still able to opened on context menu)
- several new ways to manage your account password:
- 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)
- ask the account password on each program launch
- store it in KWallet which is dynamically loaded
- dragging into conversation now attach files
### Bug fixes
- never updating MUC avatars now get updated
- going offline related segfault fix
- statuses now behave better: they wrap if they don't fit, you can select them, you can follow links from there
- messages and statuses don't loose content if you use < ore > symbols
- now avatars of those who are not in the MUC right now but was also display next to the message
- fix crash on attempt to attach the same file for the second time
## Squawk 0.1.3 (Mar 31, 2020)
### New features
- delivery states for the messages
- delivery receipts now work for real
- avatars in conferences
- edited messages now display correctly
- restyling to get better look with different desktop themes
### Bug fixes
- clickable links now detects better
- fixed lost messages that came with no ID
- avatar related fixes
- message carbons now get turned on only if the server supports them
- progress spinner fix
- files in dialog now have better comment
## Squawk 0.1.2 (Dec 25, 2019)
### New features
- icons in roster for conferences
- pal avatar in dialog window
- avatars next to every message in dialog windows (not in conferences yet)
- roster window position and size now are stored in config
- expanded accounts and groups are stored in config
- availability (from top combobox) now is stored in config
### Bug fixes
- segfault when sending more then one attached file
- wrong path and name of saving file
- wrong message syntax when attaching file and writing text in the save message
- problem with links highlighting in dialog
- project building fixes
## Squawk 0.1.1 (Nov 16, 2019)
A lot of bug fixes, memory leaks fixes
### New features
- exchange files via HTTP File Upload
- download VCards of your contacts
- upload your VCard with information about your contact phones email addresses, names, career information and avatar
- avatars of your contacts in roster and in notifications
## Squawk 0.0.5 (Oct 10, 2019)
### Features
- chat directly
- have multiple accounts
- add contacts
- remove contacts
- assign contact to different groups
- chat in MUCs
- join MUCs, leave them, keep them subscribed or unsubscribed
- download attachmets
- local history
- desktop notifications of new messages

44
CMakeLists.txt

@ -62,18 +62,44 @@ add_custom_target(translations ALL DEPENDS ${QM_FILES})
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(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)
add_subdirectory(external/qxmpp)
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(core)
add_subdirectory(external/simpleCrypt)
target_link_libraries(squawk squawkUI)
target_link_libraries(squawk squawkCORE)
target_link_libraries(squawk uuid)
@ -83,9 +109,9 @@ add_dependencies(${CMAKE_PROJECT_NAME} translations)
# Install the executable
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
install(FILES squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
install(FILES squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
install(FILES squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
install(FILES squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
install(FILES squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
install(FILES squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)

10
README.md

@ -4,7 +4,7 @@
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
![Squawk screenshot](https://macaw.me/images/squawk/0.1.3.png)
![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png)
### Prerequisites
@ -13,6 +13,7 @@
- lmdb
- CMake 3.0 or higher
- qxmpp 1.1.0 or higher
- kwallet (optional)
### Getting
@ -60,6 +61,13 @@ $ cmake .. -D SYSTEM_QXMPP=False
$ 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`)
## License
This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details

9
core/CMakeLists.txt

@ -20,16 +20,17 @@ set(squawkCORE_SRC
adapterFuctions.cpp
)
add_subdirectory(passwordStorageEngines)
# Tell CMake to create the helloworld executable
add_library(squawkCORE ${squawkCORE_SRC})
if(SYSTEM_QXMPP)
find_package(QXmpp CONFIG REQUIRED)
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
endif()
# Use the Widgets module from Qt 5.
target_link_libraries(squawkCORE Qt5::Core)
target_link_libraries(squawkCORE Qt5::Network)
@ -37,3 +38,7 @@ target_link_libraries(squawkCORE Qt5::Gui)
target_link_libraries(squawkCORE Qt5::Xml)
target_link_libraries(squawkCORE qxmpp)
target_link_libraries(squawkCORE lmdb)
target_link_libraries(squawkCORE simpleCrypt)
if (WITH_KWALLET)
target_link_libraries(squawkCORE kwalletPSE)
endif()

26
core/account.cpp

@ -53,7 +53,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
avatarType(),
ownVCardRequestInProgress(false),
network(p_net),
pendingStateMessages()
pendingStateMessages(),
passwordType(Shared::AccountPassword::plain)
{
config.setUser(p_login);
config.setDomain(p_server);
@ -266,6 +267,11 @@ QString Core::Account::getServer() const
return config.domain();
}
Shared::AccountPassword Core::Account::getPasswordType() const
{
return passwordType;
}
void Core::Account::onRosterReceived()
{
vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
@ -296,6 +302,11 @@ void Core::Account::onRosterItemAdded(const QString& bareJid)
}
}
void Core::Account::setPasswordType(Shared::AccountPassword pt)
{
passwordType = pt;
}
void Core::Account::onRosterItemChanged(const QString& bareJid)
{
std::map<QString, Contact*>::const_iterator itr = contacts.find(bareJid);
@ -1052,6 +1063,7 @@ void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& l
void Core::Account::onClientError(QXmppClient::Error err)
{
qDebug() << "Error";
QString errorText;
QString errorType;
switch (err) {
@ -1129,6 +1141,9 @@ void Core::Account::onClientError(QXmppClient::Error err)
case QXmppStanza::Error::UnexpectedRequest:
errorText = "Unexpected request";
break;
case QXmppStanza::Error::PolicyViolation:
errorText = "Policy violation";
break;
}
errorType = "Client stream error";
@ -1356,20 +1371,21 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
{"autoJoin", conf->getAutoJoin()},
{"joined", conf->getJoined()},
{"nick", conf->getNick()},
{"name", conf->getName()}
{"name", conf->getName()},
{"avatars", conf->getAllAvatars()}
};
Archive::AvatarInfo info;
bool hasAvatar = conf->readAvatarInfo(info);
if (hasAvatar) {
if (info.autogenerated) {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
cData.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
cData.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
}
cData.insert("avatarPath", conf->avatarPath() + "." + info.type);
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
cData.insert("avatarPath", "");
requestVCard(jid);
}

11
core/account.h

@ -55,7 +55,13 @@ class Account : public QObject
{
Q_OBJECT
public:
Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0);
Account(
const QString& p_login,
const QString& p_server,
const QString& p_password,
const QString& p_name,
NetworkAccess* p_net,
QObject* parent = 0);
~Account();
void connect();
@ -70,6 +76,7 @@ public:
QString getResource() const;
QString getAvatarPath() const;
Shared::Availability getAvailability() const;
Shared::AccountPassword getPasswordType() const;
void setName(const QString& p_name);
void setLogin(const QString& p_login);
@ -77,6 +84,7 @@ public:
void setPassword(const QString& p_password);
void setResource(const QString& p_resource);
void setAvailability(Shared::Availability avail);
void setPasswordType(Shared::AccountPassword pt);
QString getFullJid() const;
void sendMessage(Shared::Message data);
void sendMessage(const Shared::Message& data, const QString& path);
@ -158,6 +166,7 @@ private:
bool ownVCardRequestInProgress;
NetworkAccess* network;
std::map<QString, QString> pendingStateMessages;
Shared::AccountPassword passwordType;
private slots:
void onClientConnected();

33
core/archive.cpp

@ -675,7 +675,7 @@ bool Core::Archive::dropAvatar(const std::string& resource)
}
}
bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QString& resource)
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource)
{
if (!opened) {
throw Closed("setAvatar", jid.toStdString());
@ -726,7 +726,9 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QStr
MDB_val lmdbKey, lmdbData;
QByteArray value;
AvatarInfo newInfo(ext, newHash, generated);
newInfo.type = ext;
newInfo.hash = newHash;
newInfo.autogenerated = generated;
newInfo.serialize(&value);
lmdbKey.mv_size = res.size();
lmdbKey.mv_data = (char*)res.c_str();
@ -802,6 +804,33 @@ bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std:
}
}
void Core::Archive::readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const
{
if (!opened) {
throw Closed("readAllResourcesAvatars", jid.toStdString());
}
int rc;
MDB_val lmdbKey, lmdbData;
MDB_txn *txn;
MDB_cursor* cursor;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
mdb_cursor_open(txn, avatars, &cursor);
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
do {
std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size);
QString res(sId.c_str());
if (res != jid) {
data.emplace(res, AvatarInfo());
data[res].deserialize((char*)lmdbData.mv_data, lmdbData.mv_size);
}
} while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
}
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const
{
if (!opened) {

3
core/archive.h

@ -56,9 +56,10 @@ public:
std::list<Shared::Message> getBefore(int count, const QString& id);
bool isFromTheBeginning();
void setFromTheBeginning(bool is);
bool setAvatar(const QByteArray& data, bool generated = false, const QString& resource = "");
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
AvatarInfo getAvatarInfo(const QString& resource = "") const;
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
public:
const QString jid;

98
core/conference.cpp

@ -25,7 +25,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
nick(p_nick),
room(p_room),
joined(false),
autoJoin(p_autoJoin)
autoJoin(p_autoJoin),
exParticipants()
{
muc = true;
name = p_name;
@ -44,6 +45,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
if (autoJoin) {
room->join();
}
archive->readAllResourcesAvatars(exParticipants);
}
Core::Conference::~Conference()
@ -134,17 +137,20 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
{
QStringList comps = p_name.split("/");
QString resource = comps.back();
QXmppPresence pres = room->participantPresence(p_name);
QXmppMucItem mi = pres.mucItem();
if (resource == jid) {
qDebug() << "Room" << jid << "is reporting of adding itself to the list participants. Not sure what to do with that yet, skipping";
} else {
QXmppPresence pres = room->participantPresence(p_name);
resource = "";
}
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
bool hasAvatar = itr != exParticipants.end();
if (resource.size() > 0) {
QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc();
}
QXmppMucItem mi = pres.mucItem();
Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, resource);
QMap<QString, QVariant> cData = {
{"lastActivity", lastInteraction},
@ -155,12 +161,12 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
};
if (hasAvatar) {
if (info.autogenerated) {
if (itr->second.autogenerated) {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
}
cData.insert("avatarPath", avatarPath(resource) + "." + info.type);
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarPath", "");
@ -169,22 +175,43 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
emit addParticipant(resource, cData);
}
switch (pres.vCardUpdateType()) {
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
break;
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
break;
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
if (!hasAvatar || !itr->second.autogenerated) {
setAutoGeneratedAvatar(resource);
}
}
break;
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
if (hasAvatar) {
if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) {
emit requestVCard(p_name);
}
} else {
emit requestVCard(p_name);
}
break;
}
}
}
void Core::Conference::onRoomParticipantChanged(const QString& p_name)
{
QStringList comps = p_name.split("/");
QString resource = comps.back();
if (resource == jid) {
qDebug() << "Room" << jid << "is reporting of changing his own presence. Not sure what to do with that yet, skipping";
} else {
QXmppPresence pres = room->participantPresence(p_name);
QXmppPresence pres = room->participantPresence(p_name);
QXmppMucItem mi = pres.mucItem();
handlePresence(pres);
if (resource != jid) {
QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc();
}
QXmppMucItem mi = pres.mucItem();
handlePresence(pres);
emit changeParticipant(resource, {
{"lastActivity", lastInteraction},
@ -261,30 +288,46 @@ void Core::Conference::handlePresence(const QXmppPresence& pres)
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
{
bool result = RosterItem::setAutoGeneratedAvatar(resource);
Archive::AvatarInfo newInfo;
bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource);
if (result && resource.size() != 0) {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr == exParticipants.end()) {
exParticipants.insert(std::make_pair(resource, newInfo));
} else {
itr->second = newInfo;
}
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
{"availability", avatarPath(resource) + ".png"}
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
});
}
return result;
}
bool Core::Conference::setAvatar(const QByteArray& data, const QString& resource)
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
{
bool result = RosterItem::setAvatar(data, resource);
bool result = RosterItem::setAvatar(data, info, resource);
if (result && resource.size() != 0) {
if (data.size() > 0) {
QMimeDatabase db;
QMimeType type = db.mimeTypeForData(data);
QString ext = type.preferredSuffix();
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr == exParticipants.end()) {
exParticipants.insert(std::make_pair(resource, info));
} else {
itr->second = info;
}
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
{"avatarPath", avatarPath(resource) + "." + ext}
{"avatarPath", avatarPath(resource) + "." + info.type}
});
} else {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr != exParticipants.end()) {
exParticipants.erase(itr);
}
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
{"avatarPath", ""}
@ -309,3 +352,12 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co
return result;
}
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
{
QMap<QString, QVariant> result;
for (const std::pair<QString, Archive::AvatarInfo>& pair : exParticipants) {
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
}
return result;
}

15
core/conference.h

@ -19,9 +19,15 @@
#ifndef CORE_CONFERENCE_H
#define CORE_CONFERENCE_H
#include "rosteritem.h"
#include <QDir>
#include <QXmppMucManager.h>
#include <set>
#include "rosteritem.h"
#include "shared/global.h"
namespace Core
{
@ -46,8 +52,8 @@ public:
void setAutoJoin(bool p_autoJoin);
void handlePresence(const QXmppPresence & pres) override;
bool setAutoGeneratedAvatar(const QString& resource = "") override;
bool setAvatar(const QByteArray &data, const QString &resource = "") override;
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
QMap<QString, QVariant> getAllAvatars() const;
signals:
void nickChanged(const QString& nick);
@ -58,11 +64,16 @@ signals:
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
void removeParticipant(const QString& name);
protected:
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
private:
QString nick;
QXmppMucRoom* room;
bool joined;
bool autoJoin;
std::map<QString, Archive::AvatarInfo> exParticipants;
static const std::set<QString> supportedList;
private slots:
void onRoomJoined();

37
core/passwordStorageEngines/CMakeLists.txt

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.0)
project(pse)
if (WITH_KWALLET)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Gui CONFIG REQUIRED)
get_target_property(KWALLET_INTERFACE_INCLUDE_DIRECTORIES KF5::Wallet INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES Qt5::Gui INTERFACE_INCLUDE_DIRECTORIES)
set(kwalletPSE_SRC
kwallet.cpp
)
add_library(kwalletPSE ${kwalletPSE_SRC})
target_include_directories(kwalletPSE PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
target_include_directories(kwalletPSE PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
target_link_libraries(kwalletPSE Qt5::Core)
set(kwalletW_SRC
wrappers/kwallet.cpp
)
add_library(kwalletWrapper SHARED ${kwalletW_SRC})
target_include_directories(kwalletWrapper PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
target_include_directories(kwalletWrapper PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
target_link_libraries(kwalletWrapper KF5::Wallet)
target_link_libraries(kwalletWrapper Qt5::Core)
install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

230
core/passwordStorageEngines/kwallet.cpp

@ -0,0 +1,230 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "kwallet.h"
Core::PSE::KWallet::OpenWallet Core::PSE::KWallet::openWallet = 0;
Core::PSE::KWallet::NetworkWallet Core::PSE::KWallet::networkWallet = 0;
Core::PSE::KWallet::DeleteWallet Core::PSE::KWallet::deleteWallet = 0;
Core::PSE::KWallet::ReadPassword Core::PSE::KWallet::readPassword = 0;
Core::PSE::KWallet::WritePassword Core::PSE::KWallet::writePassword = 0;
Core::PSE::KWallet::HasFolder Core::PSE::KWallet::hasFolder = 0;
Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0;
Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0;
Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial;
QLibrary Core::PSE::KWallet::lib("kwalletWrapper");
Core::PSE::KWallet::KWallet():
QObject(),
cState(disconnected),
everError(false),
wallet(0),
readRequest(),
writeRequest()
{
if (sState == initial) {
lib.load();
if (lib.isLoaded()) {
openWallet = (OpenWallet) lib.resolve("openWallet");
networkWallet = (NetworkWallet) lib.resolve("networkWallet");
deleteWallet = (DeleteWallet) lib.resolve("deleteWallet");
readPassword = (ReadPassword) lib.resolve("readPassword");
writePassword = (WritePassword) lib.resolve("writePassword");
hasFolder = (HasFolder) lib.resolve("hasFolder");
createFolder = (CreateFolder) lib.resolve("createFolder");
setFolder = (SetFolder) lib.resolve("setFolder");
if (openWallet
&& networkWallet
&& deleteWallet
&& readPassword
&& writePassword
) {
sState = success;
} else {
sState = failure;
}
} else {
sState = failure;
}
}
}
Core::PSE::KWallet::~KWallet()
{
close();
}
void Core::PSE::KWallet::open()
{
if (sState == success) {
if (cState == disconnected) {
QString name;
networkWallet(name);
wallet = openWallet(name, 0, ::KWallet::Wallet::Asynchronous);
if (wallet) {
cState = connecting;
connect(wallet, SIGNAL(walletOpened(bool)), this, SLOT(onWalletOpened(bool)));
connect(wallet, SIGNAL(walletClosed()), this, SLOT(onWalletClosed()));
} else {
everError = true;
emit opened(false);
rejectPending();
}
}
}
}
Core::PSE::KWallet::ConnectionState Core::PSE::KWallet::connectionState()
{
return cState;
}
void Core::PSE::KWallet::close()
{
if (sState == success) {
if (cState != disconnected) {
deleteWallet(wallet);
wallet = 0;
}
rejectPending();
}
}
void Core::PSE::KWallet::onWalletClosed()
{
cState = disconnected;
deleteWallet(wallet);
wallet = 0;
emit closed();
rejectPending();
}
void Core::PSE::KWallet::onWalletOpened(bool success)
{
emit opened(success);
if (success) {
QString appName = QCoreApplication::applicationName();
if (!hasFolder(wallet, appName)) {
createFolder(wallet, appName);
}
setFolder(wallet, appName);
cState = connected;
readPending();
} else {
everError = true;
cState = disconnected;
deleteWallet(wallet);
wallet = 0;
rejectPending();
}
}
Core::PSE::KWallet::SupportState Core::PSE::KWallet::supportState()
{
return sState;
}
bool Core::PSE::KWallet::everHadError() const
{
return everError;
}
void Core::PSE::KWallet::resetEverHadError()
{
everError = false;
}
void Core::PSE::KWallet::requestReadPassword(const QString& login, bool askAgain)
{
if (sState == success) {
readRequest.insert(login);
readSwitch(askAgain);
}
}
void Core::PSE::KWallet::requestWritePassword(const QString& login, const QString& password, bool askAgain)
{
if (sState == success) {
std::map<QString, QString>::iterator itr = writeRequest.find(login);
if (itr == writeRequest.end()) {
writeRequest.insert(std::make_pair(login, password));
} else {
itr->second = password;
}
readSwitch(askAgain);
}
}
void Core::PSE::KWallet::readSwitch(bool askAgain)
{
switch (cState) {
case connected:
readPending();
break;
case connecting:
break;
case disconnected:
if (!everError || askAgain) {
open();
}
break;
}
}
void Core::PSE::KWallet::rejectPending()
{
writeRequest.clear();
std::set<QString>::const_iterator i = readRequest.begin();
while (i != readRequest.end()) {
emit rejectPassword(*i);
readRequest.erase(i);
i = readRequest.begin();
}
}
void Core::PSE::KWallet::readPending()
{
std::map<QString, QString>::const_iterator itr = writeRequest.begin();
while (itr != writeRequest.end()) {
int result = writePassword(wallet, itr->first, itr->second);
if (result == 0) {
qDebug() << "Successfully saved password for user" << itr->first;
} else {
qDebug() << "Error writing password for user" << itr->first << ":" << result;
}
writeRequest.erase(itr);
itr = writeRequest.begin();
}
std::set<QString>::const_iterator i = readRequest.begin();
while (i != readRequest.end()) {
QString password;
int result = readPassword(wallet, *i, password);
if (result == 0 && password.size() > 0) { //even though it's written that the error is supposed to be returned in case there were no password
emit responsePassword(*i, password); //it doesn't do so. I assume empty password as a lack of password in KWallet
} else {
emit rejectPassword(*i);
}
readRequest.erase(i);
i = readRequest.begin();
}
}

112
core/passwordStorageEngines/kwallet.h

@ -0,0 +1,112 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_PSE_KWALLET_H
#define CORE_PSE_KWALLET_H
#include <QObject>
#include <QLibrary>
#include <QDebug>
#include <QCoreApplication>
#include <map>
#include <set>
#include <KF5/KWallet/KWallet>
namespace Core {
namespace PSE {
/**
* @todo write docs
*/
class KWallet : public QObject
{
Q_OBJECT
public:
enum SupportState {
initial,
success,
failure
};
enum ConnectionState {
disconnected,
connecting,
connected
};
KWallet();
~KWallet();
static SupportState supportState();
void open();
void close();
ConnectionState connectionState();
bool everHadError() const;
void resetEverHadError();
void requestReadPassword(const QString& login, bool askAgain = false);
void requestWritePassword(const QString& login, const QString& password, bool askAgain = false);
signals:
void opened(bool success);
void closed();
void responsePassword(const QString& login, const QString& password);
void rejectPassword(const QString& login);
private slots:
void onWalletOpened(bool success);
void onWalletClosed();
private:
void readSwitch(bool askAgain);
void readPending();
void rejectPending();
private:
typedef ::KWallet::Wallet* (*OpenWallet)(const QString &, WId, ::KWallet::Wallet::OpenType);
typedef void (*NetworkWallet)(QString&);
typedef void (*DeleteWallet)(::KWallet::Wallet*);
typedef int (*ReadPassword)(::KWallet::Wallet*, const QString&, QString&);
typedef int (*WritePassword)(::KWallet::Wallet*, const QString&, const QString&);
typedef bool (*HasFolder)(::KWallet::Wallet* w, const QString &f);
typedef bool (*CreateFolder)(::KWallet::Wallet* w, const QString &f);
typedef bool (*SetFolder)(::KWallet::Wallet* w, const QString &f);
static OpenWallet openWallet;
static NetworkWallet networkWallet;
static DeleteWallet deleteWallet;
static ReadPassword readPassword;
static WritePassword writePassword;
static HasFolder hasFolder;
static CreateFolder createFolder;
static SetFolder setFolder;
static SupportState sState;
static QLibrary lib;
ConnectionState cState;
bool everError;
::KWallet::Wallet* wallet;
std::set<QString> readRequest;
std::map<QString, QString> writeRequest;
};
}}
#endif // CORE_PSE_KWALLET_H

33
core/passwordStorageEngines/wrappers/kwallet.cpp

@ -0,0 +1,33 @@
#include <KF5/KWallet/KWallet>
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
return KWallet::Wallet::openWallet(name, w, ot);
}
extern "C" void deleteWallet(KWallet::Wallet* w) {
w->deleteLater();
}
extern "C" void networkWallet(QString& str) {
str = KWallet::Wallet::NetworkWallet();
}
extern "C" int readPassword(KWallet::Wallet* w, const QString &key, QString &value) {
return w->readPassword(key, value);
}
extern "C" int writePassword(KWallet::Wallet* w, const QString &key, const QString &value) {
return w->writePassword(key, value);
}
extern "C" bool hasFolder(KWallet::Wallet* w, const QString &f) {
return w->hasFolder(f);
}
extern "C" bool createFolder(KWallet::Wallet* w, const QString &f) {
return w->createFolder(f);
}
extern "C" bool setFolder(KWallet::Wallet* w, const QString &f) {
return w->setFolder(f);
}

34
core/rosteritem.cpp

@ -409,29 +409,38 @@ bool Core::RosterItem::isMuc() const
}
QString Core::RosterItem::avatarPath(const QString& resource) const
{
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
return path;
}
QString Core::RosterItem::folderPath() const
{
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + account + "/" + jid + "/" + (resource.size() == 0 ? jid : resource);
path += "/" + account + "/" + jid;
return path;
}
bool Core::RosterItem::setAvatar(const QByteArray& data, const QString& resource)
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
{
bool result = archive->setAvatar(data, false, resource);
bool result = archive->setAvatar(data, info, false, resource);
if (resource.size() == 0 && result) {
if (data.size() == 0) {
emit avatarChanged(Shared::Avatar::empty, "");
} else {
QMimeDatabase db;
QMimeType type = db.mimeTypeForData(data);
QString ext = type.preferredSuffix();
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + ext);
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
}
}
return result;
}
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
{
Archive::AvatarInfo info;
return setAutoGeneratedAvatar(info, resource);
}
bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource)
{
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
QPainter painter(&image);
@ -453,7 +462,7 @@ bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
stream.open(QBuffer::WriteOnly);
image.save(&stream, "PNG");
stream.close();
bool result = archive->setAvatar(arr, true, resource);
bool result = archive->setAvatar(arr, info, true, resource);
if (resource.size() == 0 && result) {
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
}
@ -468,6 +477,7 @@ bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
{
Archive::AvatarInfo info;
Archive::AvatarInfo newInfo;
bool hasAvatar = readAvatarInfo(info, resource);
QByteArray ava = card.photo();
@ -477,13 +487,10 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
QString path = "";
if (ava.size() > 0) {
bool changed = setAvatar(ava, resource);
bool changed = setAvatar(ava, newInfo, resource);
if (changed) {
type = Shared::Avatar::valid;
QMimeDatabase db;
QMimeType type = db.mimeTypeForData(ava);
QString ext = type.preferredSuffix();
path = avatarPath(resource) + "." + ext;
path = avatarPath(resource) + "." + newInfo.type;
} else if (hasAvatar) {
if (info.autogenerated) {
type = Shared::Avatar::autocreated;
@ -501,7 +508,6 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
path = avatarPath(resource) + ".png";
}
vCard.setAvatarType(type);
vCard.setAvatarPath(path);

8
core/rosteritem.h

@ -69,8 +69,8 @@ public:
void requestHistory(int count, const QString& before);
void requestFromEmpty(int count, const QString& before);
QString avatarPath(const QString& resource = "") const;
QString folderPath() const;
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
virtual bool setAvatar(const QByteArray& data, const QString& resource = "");
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
virtual void handlePresence(const QXmppPresence& pres) = 0;
@ -89,6 +89,10 @@ public:
const QString jid;
const QString account;
protected:
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
protected:
QString name;
ArchiveState archiveState;
@ -103,7 +107,7 @@ protected:
std::list<std::pair<int, QString>> requestCache;
std::map<QString, Shared::Message> toCorrect;
bool muc;
private:
void nextRequest();
void performRequest(int count, const QString& before);

250
core/squawk.cpp

@ -26,13 +26,27 @@ Core::Squawk::Squawk(QObject* parent):
QObject(parent),
accounts(),
amap(),
network()
network(),
waitingForAccounts(0)
#ifdef WITH_KWALLET
,kwallet()
#endif
{
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
#ifdef WITH_KWALLET
if (kwallet.supportState() == PSE::KWallet::success) {
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword);
Shared::Global::setSupported("KWallet", true);
}
#endif
}
Core::Squawk::~Squawk()
@ -44,6 +58,11 @@ Core::Squawk::~Squawk()
}
}
void Core::Squawk::onWalletOpened(bool success)
{
qDebug() << "KWallet opened: " << success;
}
void Core::Squawk::stop()
{
qDebug("Stopping squawk core..");
@ -51,14 +70,31 @@ void Core::Squawk::stop()
QSettings settings;
settings.beginGroup("core");
settings.beginWriteArray("accounts");
SimpleCrypt crypto(passwordHash);
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
settings.setArrayIndex(i);
Account* acc = accounts[i];
Shared::AccountPassword ap = acc->getPasswordType();
QString password;
switch (ap) {
case Shared::AccountPassword::plain:
password = acc->getPassword();
break;
case Shared::AccountPassword::jammed:
password = crypto.encryptToString(acc->getPassword());
break;
default:
break;
}
settings.setValue("name", acc->getName());
settings.setValue("server", acc->getServer());
settings.setValue("login", acc->getLogin());
settings.setValue("password", acc->getPassword());
settings.setValue("password", password);
settings.setValue("resource", acc->getResource());
settings.setValue("passwordType", static_cast<int>(ap));
}
settings.endArray();
settings.endGroup();
@ -72,21 +108,7 @@ void Core::Squawk::start()
{
qDebug("Starting squawk core..");
QSettings settings;
settings.beginGroup("core");
int size = settings.beginReadArray("accounts");
for (int i = 0; i < size; ++i) {
settings.setArrayIndex(i);
addAccount(
settings.value("login").toString(),
settings.value("server").toString(),
settings.value("password").toString(),
settings.value("name").toString(),
settings.value("resource").toString()
);
}
settings.endArray();
settings.endGroup();
readSettings();
network.start();
}
@ -98,10 +120,17 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
QString password = map.value("password").toString();
QString resource = map.value("resource").toString();
addAccount(login, server, password, name, resource);
addAccount(login, server, password, name, resource, Shared::AccountPassword::plain);
}
void Core::Squawk::addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource)
void Core::Squawk::addAccount(
const QString& login,
const QString& server,
const QString& password,
const QString& name,
const QString& resource,
Shared::AccountPassword passwordType
)
{
QSettings settings;
unsigned int reconnects = settings.value("reconnects", 2).toUInt();
@ -109,6 +138,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
Account* acc = new Account(login, server, password, name, &network);
acc->setResource(resource);
acc->setReconnectTimes(reconnects);
acc->setPasswordType(passwordType);
accounts.push_back(acc);
amap.insert(std::make_pair(name, acc));
@ -119,8 +149,10 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup);
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact), this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
connect(acc, qOverload<const QString&>(&Account::removeContact), this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact),
this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
connect(acc, qOverload<const QString&>(&Account::removeContact),
this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact);
connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence);
connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence);
@ -149,7 +181,8 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
{"state", QVariant::fromValue(Shared::ConnectionState::disconnected)},
{"offline", QVariant::fromValue(Shared::Availability::offline)},
{"error", ""},
{"avatarPath", acc->getAvatarPath()}
{"avatarPath", acc->getAvatarPath()},
{"passwordType", QVariant::fromValue(passwordType)}
};
emit newAccount(map);
@ -192,17 +225,29 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
Account* acc = static_cast<Account*>(sender());
emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}});
if (p_state == Shared::ConnectionState::disconnected) {
bool equals = true;
for (Accounts::const_iterator itr = accounts.begin(), end = accounts.end(); itr != end; itr++) {
if ((*itr)->getState() != Shared::ConnectionState::disconnected) {
equals = false;
switch (p_state) {
case Shared::ConnectionState::disconnected: {
bool equals = true;
for (Accounts::const_iterator itr = accounts.begin(), end = accounts.end(); itr != end; itr++) {
if ((*itr)->getState() != Shared::ConnectionState::disconnected) {
equals = false;
}
}
if (equals && state != Shared::Availability::offline) {
state = Shared::Availability::offline;
emit stateChanged(state);
}
}
}
if (equals && state != Shared::Availability::offline) {
state = Shared::Availability::offline;
emit stateChanged(state);
}
break;
case Shared::ConnectionState::connected:
#ifdef WITH_KWALLET
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true);
}
#endif
break;
default:
break;
}
}
@ -320,12 +365,37 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
Core::Account* acc = itr->second;