pasword storing options: jammed an alwaysAsk, external lib for password jamming, changelog

This commit is contained in:
Blue 2020-04-07 23:33:03 +03:00
parent 95f0d4008a
commit 7ce27d1c11
14 changed files with 728 additions and 18 deletions

74
CHANGELOG.md Normal file
View File

@ -0,0 +1,74 @@
# Changelog
## Squawk 0.1.4 (UNRELEASED)
------------------------------
### New features
- several 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
### Bug fixes
- never updating MUC avatars now update
- going offline related segfault fix
## 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

View File

@ -74,6 +74,8 @@ endif()
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)

View File

@ -37,3 +37,4 @@ 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)

View File

@ -52,14 +52,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();
@ -318,12 +335,37 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
Core::Account* acc = itr->second;
Shared::ConnectionState st = acc->getState();
QMap<QString, QVariant>::const_iterator mItr;
bool needToReconnect = false;
if (st != Shared::ConnectionState::disconnected) {
mItr = map.find("login");
if (mItr != map.end()) {
needToReconnect = acc->getLogin() != mItr->toString();
}
if (!needToReconnect) {
mItr = map.find("password");
if (mItr != map.end()) {
needToReconnect = acc->getPassword() != mItr->toString();
}
}
if (!needToReconnect) {
mItr = map.find("server");
if (mItr != map.end()) {
needToReconnect = acc->getServer() != mItr->toString();
}
}
if (!needToReconnect) {
mItr = map.find("resource");
if (mItr != map.end()) {
needToReconnect = acc->getResource() != mItr->toString();
}
}
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
acc->reconnect();
}
QMap<QString, QVariant>::const_iterator mItr;
mItr = map.find("login");
if (mItr != map.end()) {
acc->setLogin(mItr->toString());
@ -344,6 +386,11 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
acc->setServer(mItr->toString());
}
mItr = map.find("passwordType");
if (mItr != map.end()) {
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
}
emit changeAccount(name, map);
}
@ -570,6 +617,17 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card
itr->second->uploadVCard(card);
}
void Core::Squawk::responsePassword(const QString& account, const QString& password)
{
AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) {
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
return;
}
itr->second->setPassword(password);
accountReady();
}
void Core::Squawk::readSettings()
{
QSettings settings;
@ -614,5 +672,16 @@ void Core::Squawk::parseAccount(
addAccount(login, server, password, name, resource, passwordType);
accountReady();
break;
case Shared::AccountPassword::jammed: {
SimpleCrypt crypto(passwordHash);
QString decrypted = crypto.decryptToString(password);
addAccount(login, server, decrypted, name, resource, passwordType);
accountReady();
}
break;
case Shared::AccountPassword::alwaysAsk:
addAccount(login, server, QString(), name, resource, passwordType);
emit requestPassword(name);
break;
}
}

View File

@ -32,6 +32,7 @@
#include "shared/message.h"
#include "shared/global.h"
#include "networkaccess.h"
#include "external/simpleCrypt/simplecrypt.h"
namespace Core
{
@ -73,6 +74,7 @@ signals:
void uploadFileProgress(const QString& messageId, qreal value);
void responseVCard(const QString& jid, const Shared::VCard& card);
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
void requestPassword(const QString& account);
public slots:
void start();
@ -101,6 +103,7 @@ public slots:
void downloadFileRequest(const QString& messageId, const QString& url);
void requestVCard(const QString& account, const QString& jid);
void uploadVCard(const QString& account, const Shared::VCard& card);
void responsePassword(const QString& account, const QString& password);
private:
typedef std::deque<Account*> Accounts;
@ -155,6 +158,8 @@ private:
const QString& resource,
Shared::AccountPassword passwordType
);
static const quint64 passwordHash = 0x08d054225ac4871d;
};
}

16
external/simpleCrypt/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.0)
project(simplecrypt)
set(CMAKE_AUTOMOC ON)
find_package(Qt5Core CONFIG REQUIRED)
set(simplecrypt_SRC
simplecrypt.cpp
)
# Tell CMake to create the helloworld executable
add_library(simpleCrypt ${simplecrypt_SRC})
# Use the Widgets module from Qt 5.
target_link_libraries(simpleCrypt Qt5::Core)

252
external/simpleCrypt/simplecrypt.cpp vendored Normal file
View File

@ -0,0 +1,252 @@
/*
Copyright (c) 2011, Andre Somers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Rathenau Instituut, Andre Somers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "simplecrypt.h"
#include <QByteArray>
#include <QtDebug>
#include <QtGlobal>
#include <QDateTime>
#include <QCryptographicHash>
#include <QDataStream>
SimpleCrypt::SimpleCrypt():
m_key(0),
m_compressionMode(CompressionAuto),
m_protectionMode(ProtectionChecksum),
m_lastError(ErrorNoError)
{
qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF));
}
SimpleCrypt::SimpleCrypt(quint64 key):
m_key(key),
m_compressionMode(CompressionAuto),
m_protectionMode(ProtectionChecksum),
m_lastError(ErrorNoError)
{
qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF));
splitKey();
}
void SimpleCrypt::setKey(quint64 key)
{
m_key = key;
splitKey();
}
void SimpleCrypt::splitKey()
{
m_keyParts.clear();
m_keyParts.resize(8);
for (int i=0;i<8;i++) {
quint64 part = m_key;
for (int j=i; j>0; j--)
part = part >> 8;
part = part & 0xff;
m_keyParts[i] = static_cast<char>(part);
}
}
QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext)
{
QByteArray plaintextArray = plaintext.toUtf8();
return encryptToByteArray(plaintextArray);
}
QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext)
{
if (m_keyParts.isEmpty()) {
qWarning() << "No key set.";
m_lastError = ErrorNoKeySet;
return QByteArray();
}
QByteArray ba = plaintext;
CryptoFlags flags = CryptoFlagNone;
if (m_compressionMode == CompressionAlways) {
ba = qCompress(ba, 9); //maximum compression
flags |= CryptoFlagCompression;
} else if (m_compressionMode == CompressionAuto) {
QByteArray compressed = qCompress(ba, 9);
if (compressed.count() < ba.count()) {
ba = compressed;
flags |= CryptoFlagCompression;
}
}
QByteArray integrityProtection;
if (m_protectionMode == ProtectionChecksum) {
flags |= CryptoFlagChecksum;
QDataStream s(&integrityProtection, QIODevice::WriteOnly);
s << qChecksum(ba.constData(), ba.length());
} else if (m_protectionMode == ProtectionHash) {
flags |= CryptoFlagHash;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(ba);
integrityProtection += hash.result();
}
//prepend a random char to the string
char randomChar = char(qrand() & 0xFF);
ba = randomChar + integrityProtection + ba;
int pos(0);
char lastChar(0);
int cnt = ba.count();
while (pos < cnt) {
ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar;
lastChar = ba.at(pos);
++pos;
}
QByteArray resultArray;
resultArray.append(char(0x03)); //version for future updates to algorithm
resultArray.append(char(flags)); //encryption flags
resultArray.append(ba);
m_lastError = ErrorNoError;
return resultArray;
}
QString SimpleCrypt::encryptToString(const QString& plaintext)
{
QByteArray plaintextArray = plaintext.toUtf8();
QByteArray cypher = encryptToByteArray(plaintextArray);
QString cypherString = QString::fromLatin1(cypher.toBase64());
return cypherString;
}
QString SimpleCrypt::encryptToString(QByteArray plaintext)
{
QByteArray cypher = encryptToByteArray(plaintext);
QString cypherString = QString::fromLatin1(cypher.toBase64());
return cypherString;
}
QString SimpleCrypt::decryptToString(const QString &cyphertext)
{
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
QByteArray plaintextArray = decryptToByteArray(cyphertextArray);
QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size());
return plaintext;
}
QString SimpleCrypt::decryptToString(QByteArray cypher)
{
QByteArray ba = decryptToByteArray(cypher);
QString plaintext = QString::fromUtf8(ba, ba.size());
return plaintext;
}
QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext)
{
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
QByteArray ba = decryptToByteArray(cyphertextArray);
return ba;
}
QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher)
{
if (m_keyParts.isEmpty()) {
qWarning() << "No key set.";
m_lastError = ErrorNoKeySet;
return QByteArray();
}
QByteArray ba = cypher;
if( cypher.count() < 3 )
return QByteArray();
char version = ba.at(0);
if (version !=3) { //we only work with version 3
m_lastError = ErrorUnknownVersion;
qWarning() << "Invalid version or not a cyphertext.";
return QByteArray();
}
CryptoFlags flags = CryptoFlags(ba.at(1));
ba = ba.mid(2);
int pos(0);
int cnt(ba.count());
char lastChar = 0;
while (pos < cnt) {
char currentChar = ba[pos];
ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8);
lastChar = currentChar;
++pos;
}
ba = ba.mid(1); //chop off the random number at the start
bool integrityOk(true);
if (flags.testFlag(CryptoFlagChecksum)) {
if (ba.length() < 2) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
quint16 storedChecksum;
{
QDataStream s(&ba, QIODevice::ReadOnly);
s >> storedChecksum;
}
ba = ba.mid(2);
quint16 checksum = qChecksum(ba.constData(), ba.length());
integrityOk = (checksum == storedChecksum);
} else if (flags.testFlag(CryptoFlagHash)) {
if (ba.length() < 20) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
QByteArray storedHash = ba.left(20);
ba = ba.mid(20);
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(ba);
integrityOk = (hash.result() == storedHash);
}
if (!integrityOk) {
m_lastError = ErrorIntegrityFailed;
return QByteArray();
}
if (flags.testFlag(CryptoFlagCompression))
ba = qUncompress(ba);
m_lastError = ErrorNoError;
return ba;
}

225
external/simpleCrypt/simplecrypt.h vendored Normal file
View File

@ -0,0 +1,225 @@
/*
Copyright (c) 2011, Andre Somers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Rathenau Instituut, Andre Somers nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SIMPLECRYPT_H
#define SIMPLECRYPT_H
#include <QString>
#include <QVector>
#include <QFlags>
/**
@ short Simple encrypt*ion and decryption of strings and byte arrays
This class provides a simple implementation of encryption and decryption
of strings and byte arrays.
@warning The encryption provided by this class is NOT strong encryption. It may
help to shield things from curious eyes, but it will NOT stand up to someone
determined to break the encryption. Don't say you were not warned.
The class uses a 64 bit key. Simply create an instance of the class, set the key,
and use the encryptToString() method to calculate an encrypted version of the input string.
To decrypt that string again, use an instance of SimpleCrypt initialized with
the same key, and call the decryptToString() method with the encrypted string. If the key
matches, the decrypted version of the string will be returned again.
If you do not provide a key, or if something else is wrong, the encryption and
decryption function will return an empty string or will return a string containing nonsense.
lastError() will return a value indicating if the method was succesful, and if not, why not.
SimpleCrypt is prepared for the case that the encryption and decryption
algorithm is changed in a later version, by prepending a version identifier to the cypertext.
*/
class SimpleCrypt
{
public:
/**
CompressionMode describes if compression will be applied to the data to be
encrypted.
*/
enum CompressionMode {
CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */
CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */
CompressionNever /*!< Never apply compression. */
};
/**
IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data
or wrong decryption keys.
Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This
increases the length of the resulting cypertext, but makes it possible to check if the plaintext
appears to be valid after decryption.
*/
enum IntegrityProtectionMode {
ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */
ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */
ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */
};
/**
Error describes t*he type of error that occured.
*/
enum Error {
ErrorNoError, /*!< No error occurred. */
ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */
ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */
ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */
};
/**
Constructor. *
Constructs a SimpleCrypt instance without a valid key set on it.
*/
SimpleCrypt();
/**
Constructor. *
Constructs a SimpleCrypt instance and initializes it with the given @arg key.
*/
explicit SimpleCrypt(quint64 key);
/**
( Re-) initializes* the key with the given @arg key.
*/
void setKey(quint64 key);
/**
Returns true if SimpleCrypt has been initialized with a key.
*/
bool hasKey() const {return !m_keyParts.isEmpty();}
/**
Sets the compress*ion mode to use when encrypting data. The default mode is Auto.
Note that decryption is not influenced by this mode, as the decryption recognizes
what mode was used when encrypting.
*/
void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;}
/**
Returns the CompressionMode that is currently in use.
*/
CompressionMode compressionMode() const {return m_compressionMode;}
/**
Sets the integrity mode to use when encrypting data. The default mode is Checksum.
Note that decryption is not influenced by this mode, as the decryption recognizes
what mode was used when encrypting.
*/
void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;}
/**
Returns the IntegrityProtectionMode that is currently in use.
*/
IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;}
/**
Returns the last *error that occurred.
*/
Error lastError() const {return m_lastError;}
/**
Encrypts the @arg* plaintext string with the key the class was initialized with, and returns
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
actual result of the string, so it can be stored easily in a text format.
*/
QString encryptToString(const QString& plaintext) ;
/**
Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
actual result of the encryption, so it can be stored easily in a text format.
*/
QString encryptToString(QByteArray plaintext) ;
/**
Encrypts the @arg* plaintext string with the key the class was initialized with, and returns
a binary cyphertext in a QByteArray the result.
This method returns a byte array, that is useable for storing a binary format. If you need
a string you can store in a text file, use encryptToString() instead.
*/
QByteArray encryptToByteArray(const QString& plaintext) ;
/**
Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns
a binary cyphertext in a QByteArray the result.
This method returns a byte array, that is useable for storing a binary format. If you need
a string you can store in a text file, use encryptToString() instead.
*/
QByteArray encryptToByteArray(QByteArray plaintext) ;
/**
Decrypts a cypher*text string encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QString decryptToString(const QString& cyphertext) ;
/**
Decrypts a cypher*text string encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QByteArray decryptToByteArray(const QString& cyphertext) ;
/**
Decrypts a cypher*text binary encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QString decryptToString(QByteArray cypher) ;
/**
Decrypts a cypher*text binary encrypted with this class with the set key back to the
plain text version.
If an error occured, such as non-matching keys between encryption and decryption,
an empty string or a string containing nonsense may be returned.
*/
QByteArray decryptToByteArray(QByteArray cypher) ;
//enum to describe options that have been used for the encryption. Currently only one, but
//that only leaves room for future extensions like adding a cryptographic hash...
enum CryptoFlag{CryptoFlagNone = 0,
CryptoFlagCompression = 0x01,
CryptoFlagChecksum = 0x02,
CryptoFlagHash = 0x04
};
Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag);
private:
void splitKey();
quint64 m_key;
QVector<char> m_keyParts;
CompressionMode m_compressionMode;
IntegrityProtectionMode m_protectionMode;
Error m_lastError;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags)
#endif // SimpleCrypt_H

View File

@ -116,6 +116,7 @@ int main(int argc, char *argv[])
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
@ -146,6 +147,7 @@ int main(int argc, char *argv[])
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
coreThread->start();

View File

@ -110,11 +110,11 @@ static const std::deque<QString> messageStateThemeIcons = {"state-offline", "sta
enum class AccountPassword {
plain,
jammed,
kwallet,
alwaysAsk
alwaysAsk,
kwallet
};
Q_ENUM_NS(AccountPassword)
static const AccountPassword AccountPasswordHighest = AccountPassword::alwaysAsk;
static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet;
static const AccountPassword AccountPasswordLowest = AccountPassword::plain;
}

View File

@ -69,8 +69,8 @@ Shared::Global::Global():
accountPassword({
tr("Plain"),
tr("Jammed"),
tr("KWallet"),
tr("Always Ask")
tr("Always Ask"),
tr("KWallet")
})
{
if (instance != 0) {

View File

@ -39,6 +39,10 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
if (aItr != data.end()) {
setAvailability(aItr.value().toUInt());
}
QMap<QString, QVariant>::const_iterator pItr = data.find("passwordType");
if (pItr != data.end()) {
setPasswordType(pItr.value().toUInt());
}
}
Models::Account::~Account()

View File

@ -20,7 +20,6 @@
#include "ui_squawk.h"
#include <QDebug>
#include <QIcon>
#include <QInputDialog>
Squawk::Squawk(QWidget *parent) :
QMainWindow(parent),
@ -31,7 +30,9 @@ Squawk::Squawk(QWidget *parent) :
contextMenu(new QMenu()),
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
requestedFiles(),
vCards()
vCards(),
requestedAccountsForPasswords(),
prompt(0)
{
m_ui->setupUi(this);
m_ui->roster->setModel(&rosterModel);
@ -62,6 +63,18 @@ Squawk::Squawk(QWidget *parent) :
if (testAttribute(Qt::WA_TranslucentBackground)) {
m_ui->roster->viewport()->setAutoFillBackground(false);
}
QSettings settings;
settings.beginGroup("ui");
settings.beginGroup("window");
if (settings.contains("geometry")) {
restoreGeometry(settings.value("geometry").toByteArray());
}
if (settings.contains("state")) {
restoreState(settings.value("state").toByteArray());
}
settings.endGroup();
settings.endGroup();
}
Squawk::~Squawk() {
@ -871,14 +884,6 @@ void Squawk::readSettings()
{
QSettings settings;
settings.beginGroup("ui");
settings.beginGroup("window");
if (settings.contains("geometry")) {
restoreGeometry(settings.value("geometry").toByteArray());
}
if (settings.contains("state")) {
restoreState(settings.value("state").toByteArray());
}
settings.endGroup();
if (settings.contains("availability")) {
int avail = settings.value("availability").toInt();
@ -958,3 +963,48 @@ void Squawk::onItemCollepsed(const QModelIndex& index)
break;
}
}
void Squawk::requestPassword(const QString& account)
{
requestedAccountsForPasswords.push_back(account);
checkNextAccountForPassword();
}
void Squawk::checkNextAccountForPassword()
{
if (prompt == 0 && requestedAccountsForPasswords.size() > 0) {
prompt = new QInputDialog(this);
QString accName = requestedAccountsForPasswords.front();
connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted);
connect(prompt, &QDialog::rejected, this, &Squawk::onPasswordPromptRejected);
prompt->setInputMode(QInputDialog::TextInput);
prompt->setTextEchoMode(QLineEdit::Password);
prompt->setLabelText(tr("Input the password for account %1").arg(accName));
prompt->setWindowTitle(tr("Password for account %1").arg(accName));
prompt->setTextValue("");
prompt->exec();
}
}
void Squawk::onPasswordPromptAccepted()
{
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
onPasswordPromptDone();
}
void Squawk::onPasswordPromptDone()
{
prompt->deleteLater();
prompt = 0;
requestedAccountsForPasswords.pop_front();
checkNextAccountForPassword();
}
void Squawk::onPasswordPromptRejected()
{
//for now it's the same on reject and on accept, but one day I'm gonna make
//"Asking for the password again on the authentication failure" feature
//and here I'll be able to break the circle of password requests
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
onPasswordPromptDone();
}

View File

@ -24,6 +24,7 @@
#include <QCloseEvent>
#include <QtDBus/QDBusInterface>
#include <QSettings>
#include <QInputDialog>
#include <deque>
#include <map>
@ -79,6 +80,7 @@ signals:
void downloadFileRequest(const QString& messageId, const QString& url);
void requestVCard(const QString& account, const QString& jid);
void uploadVCard(const QString& account, const Shared::VCard& card);
void responsePassword(const QString& account, const QString& password);
public slots:
void readSettings();
@ -107,6 +109,7 @@ public slots:
void fileProgress(const QString& messageId, qreal value);
void responseVCard(const QString& jid, const Shared::VCard& card);
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
void requestPassword(const QString& account);
private:
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
@ -119,6 +122,8 @@ private:
QDBusInterface dbus;
std::map<QString, std::set<Models::Roster::ElId>> requestedFiles;
std::map<QString, VCard*> vCards;
std::deque<QString> requestedAccountsForPasswords;
QInputDialog* prompt;
protected:
void closeEvent(QCloseEvent * event) override;
@ -146,7 +151,12 @@ private slots:
void onConversationRequestLocalFile(const QString& messageId, const QString& url);
void onConversationDownloadFile(const QString& messageId, const QString& url);
void onItemCollepsed(const QModelIndex& index);
void onPasswordPromptAccepted();
void onPasswordPromptRejected();
private:
void checkNextAccountForPassword();
void onPasswordPromptDone();
};
#endif // SQUAWK_H