diff --git a/.gitea/workflows/release.yml b/.forgejo/workflows/release.yml similarity index 100% rename from .gitea/workflows/release.yml rename to .forgejo/workflows/release.yml diff --git a/.gitmodules b/.gitmodules index 448dae5..3742a90 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "external/qxmpp"] path = external/qxmpp - url = https://github.com/qxmpp-project/qxmpp.git -[submodule "external/storage"] - path = external/storage - url = https://git.macaw.me/blue/storage + url = https://invent.kde.org/libraries/qxmpp/ [submodule "external/lmdbal"] path = external/lmdbal - url = gitea@git.macaw.me:blue/lmdbal.git + url = https://git.macaw.me/blue/lmdbal diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca1542..44c00ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Squawk 0.2.4 (UNRELEASED) +### Bug fixes +- messages to the mucs get sent once again + +### Improvements +- it's possible to build against Qt 6 now, Qt6 is the default +- got rid of deprecated SimpleCrypt library +- look up for proper stanzaID + ## Squawk 0.2.3 (February 04, 2024) ### Bug fixes - "Add contact" and "Join conference" menu are enabled once again (pavavno)! diff --git a/CMakeLists.txt b/CMakeLists.txt index e2465cf..5eb98a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.5) -project(squawk VERSION 0.2.3 LANGUAGES CXX) +cmake_minimum_required(VERSION 3.16) +project(squawk VERSION 0.2.4 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0079 NEW) +cmake_policy(SET CMP0167 NEW) set(CMAKE_CXX_STANDARD 17) -set(QT_VERSION_MAJOR 5) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) @@ -33,18 +33,21 @@ option(SYSTEM_LMDBAL "Use system lmdbal lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) -option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sourt the problems out +option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out # Dependencies -## Qt -if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt6 Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) -else () - find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) +## Qt, detect and prefer Qt6 if available +find_package(Qt6 QUIET CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) +if (Qt6_FOUND) + set(QT_VERSION_MAJOR 6) +else() + find_package(Qt5 REQUIRED CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) + set(QT_VERSION_MAJOR 5) endif() -find_package(Boost COMPONENTS) +message(STATUS "Building against Qt${QT_VERSION_MAJOR}") +find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) ## OMEMO @@ -67,9 +70,9 @@ endif () ## KIO if (WITH_KIO) - find_package(KF5KIO CONFIG) + find_package(KF${QT_VERSION_MAJOR}KIO CONFIG) - if (NOT KF5KIO_FOUND) + if (NOT KF${QT_VERSION_MAJOR}KIO_FOUND) set(WITH_KIO OFF) message("KIO package wasn't found, KIO support modules wouldn't be built") else () @@ -80,9 +83,9 @@ endif () ## KWallet if (WITH_KWALLET) - find_package(KF5Wallet CONFIG) + find_package(KF${QT_VERSION_MAJOR}Wallet CONFIG) - if (NOT KF5Wallet_FOUND) + if (NOT KF${QT_VERSION_MAJOR}Wallet_FOUND) set(WITH_KWALLET OFF) message("KWallet package wasn't found, KWallet support module wouldn't be built") else () @@ -93,13 +96,13 @@ endif () ## KConfig if (WITH_KCONFIG) - find_package(KF5Config CONFIG) - if (NOT KF5Config_FOUND) + find_package(KF${QT_VERSION_MAJOR}Config CONFIG) + if (NOT KF${QT_VERSION_MAJOR}Config_FOUND) set(WITH_KCONFIG OFF) message("KConfig package wasn't found, KConfig support modules wouldn't be built") else() - find_package(KF5ConfigWidgets CONFIG) - if (NOT KF5ConfigWidgets_FOUND) + find_package(KF${QT_VERSION_MAJOR}ConfigWidgets CONFIG) + if (NOT KF${QT_VERSION_MAJOR}ConfigWidgets_FOUND) set(WITH_KCONFIG OFF) message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built") else() @@ -113,12 +116,12 @@ endif() ## QXmpp if (SYSTEM_QXMPP) if (WITH_OMEMO) - find_package(QXmpp CONFIG COMPONENTS Omemo) + find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG COMPONENTS Omemo) else () - find_package(QXmpp CONFIG) + find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG) endif () - if (NOT QXmpp_FOUND) + if (NOT QXmppQt${QT_VERSION_MAJOR}_FOUND) set(SYSTEM_QXMPP OFF) message("QXmpp package wasn't found, trying to build with bundled QXmpp") else () @@ -150,8 +153,8 @@ endif () ## LMDBAL if (SYSTEM_LMDBAL) - find_package(lmdbal) - if (NOT lmdbal_FOUND) + find_package(lmdbalqt${QT_VERSION_MAJOR}) + if (NOT lmdbalqt${QT_VERSION_MAJOR}_FOUND) set(SYSTEM_LMDBAL OFF) message("LMDBAL package wasn't found, trying to build with bundled LMDBAL") else () @@ -161,9 +164,11 @@ else() message("Building with bundled LMDBAL") set(BUILD_STATIC ON) add_subdirectory(external/lmdbal) - add_library(LMDBAL::LMDBAL ALIAS LMDBAL) + add_library(LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} ALIAS LMDBAL) endif() +find_package(OpenSSL REQUIRED) + ## Linking target_link_libraries(squawk PRIVATE @@ -173,9 +178,9 @@ target_link_libraries(squawk Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Xml - LMDBAL::LMDBAL + LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} + OpenSSL::Crypto QXmpp::QXmpp - simpleCrypt ) if (WITH_OMEMO) @@ -202,7 +207,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) list(APPEND COMPILE_OPTIONS -O3) endif() if (CMAKE_BUILD_TYPE STREQUAL Debug) - list(APPEND COMPILE_OPTIONS -g) + list(APPEND COMPILE_OPTIONS -O0) + list(APPEND COMPILE_OPTIONS -g3) list(APPEND COMPILE_OPTIONS -Wall) list(APPEND COMPILE_OPTIONS -Wextra) endif() @@ -211,9 +217,13 @@ if(CMAKE_COMPILER_IS_GNUCXX) target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS}) endif(CMAKE_COMPILER_IS_GNUCXX) +# I am not really sure about this solution +# This should enable plugins to be found in path like /usr/lib/squawk instead of just /usr/lib +set(PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/squawk") +add_compile_definitions(PLUGIN_PATH="${PLUGIN_PATH}") + add_subdirectory(main) add_subdirectory(core) -add_subdirectory(external/simpleCrypt) add_subdirectory(packaging) add_subdirectory(plugins) add_subdirectory(resources) diff --git a/README.md b/README.md index 9c835b6..b65e5e9 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ [![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.2.2.png) +![Squawk screenshot](https://macaw.me/projects/squawk/0.2.2.png) ### Prerequisites -- QT 5.12 *(lower versions might work but it wasn't tested)* -- CMake 3.4 or higher +- QT 5 or 6 +- CMake 3.10 or higher - qxmpp 1.1.0 or higher - LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) for lmdb) - KDE Frameworks: kwallet (optional) @@ -108,6 +108,7 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`: - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) - `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) - `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`) +- `QT_VERSION_MAJOR` - `6` builds against Qt 6, `5` builds against Qt 6, corresponding version of lmdbal and qxmpp should be installed (default is `6`) ## License diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index a02bfe6..8baa5ad 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -23,10 +23,7 @@ set(HEADER_FILES squawk.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES}) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) @@ -34,3 +31,4 @@ add_subdirectory(handlers) add_subdirectory(passwordStorageEngines) add_subdirectory(components) add_subdirectory(delayManager) +add_subdirectory(utils) diff --git a/core/account.cpp b/core/account.cpp index 8082aeb..e3d14e1 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -81,7 +81,8 @@ Core::Account::Account( config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); - //config.setAutoReconnectionEnabled(false); + // config.setIgnoreSslErrors(true); + // config.setAutoReconnectionEnabled(false); delay = new DelayManager::Manager(getBareJid()); QObject::connect(delay, &DelayManager::Manager::gotInfo, this, &Account::infoReady); QObject::connect(delay, &DelayManager::Manager::gotOwnInfo, this, &Account::infoReady); diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index 77d290b..751a01f 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -12,7 +12,4 @@ set(HEADER_FILES archive.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/components/networkaccess.cpp b/core/components/networkaccess.cpp index 0771dfa..59e2448 100644 --- a/core/components/networkaccess.cpp +++ b/core/components/networkaccess.cpp @@ -550,10 +550,8 @@ void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { QDir dir(currentPath); bool success = true; qDebug() << "moving" << currentPath << "to" << newPath; - for (QFileInfo fileInfo : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) { - QString fileName = fileInfo.fileName(); + for (QString fileName : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; - } if (!success) qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 094f671..34f7fd1 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -164,19 +164,7 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg } void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { - const QDateTime& time(source.stamp()); - QString id; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - id = source.originId(); - if (id.size() == 0) - id = source.id(); - - target.setStanzaId(source.stanzaId()); - qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); -#else - id = source.id(); -#endif - target.setId(id); + initializeIDs(target, source); QString messageId = target.getId(); if (messageId.size() == 0) { target.generateRandomId(); //TODO out of desperation, I need at least a random ID @@ -197,6 +185,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp if (guessing) outgoing = target.getFromJid() == acc->getBareJid(); + const QDateTime& time(source.stamp()); target.setOutgoing(outgoing); if (time.isValid()) target.setTime(time); @@ -210,6 +199,37 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setOutOfBandUrl(oob); } +void Core::MessageHandler::initializeIDs(Shared::Message& target, const QXmppMessage& source) const { + QString id; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + id = source.originId(); + if (id.size() == 0) + id = source.id(); + + QString stanzaID; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + // here I'm looking preferably for id generated by myself, but if there isn't - any is better than nothing + QVector sIDs = source.stanzaIds(); + for (const QXmppStanzaId& sID : sIDs) { + bool match = sID.by == acc->getBareJid(); + if (stanzaID.isEmpty() || match) + stanzaID = sID.id; + + if (match) + break; + } +#else + stanzaID = source.stanzaId(); +#endif + target.setStanzaId(stanzaID); + qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stanzaId:" << stanzaID; +#else + id = source.id(); +#endif + target.setId(id); +} + + void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) { qDebug() << reason; qDebug() << "- from: " << msg.from(); @@ -219,9 +239,6 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re qDebug() << "- state: " << msg.state(); qDebug() << "- stamp: " << msg.stamp(); qDebug() << "- id: " << msg.id(); -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - qDebug() << "- stanzaId: " << msg.stanzaId(); -#endif qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); qDebug() << "=============================="; } @@ -431,7 +448,7 @@ QMap Core::MessageHandler::getChanges(Shared::Message& data, } QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const { - QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); + QXmppMessage msg(QString(), data.getTo(), data.getBody(), data.getThread()); QString id(data.getId()); if (originalId.size() > 0) @@ -492,7 +509,7 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe QFileInfo file(path); if (file.exists() && file.isReadable()) { pendingStateMessages.insert(std::make_pair(id, jid)); - uploadingSlotsQueue.emplace_back(path, id); + uploadingSlotsQueue.emplace_back(file, id); if (uploadingSlotsQueue.size() == 1) acc->um->requestUploadSlot(file); } else { @@ -505,10 +522,10 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); const QString& mId = pair.second; QString palJid = pendingStateMessages.at(mId); - acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); + acc->network->uploadFile({acc->name, palJid, mId}, pair.first.path(), slot.putUrl(), slot.getUrl(), slot.putHeaders()); uploadingSlotsQueue.pop_front(); if (uploadingSlotsQueue.size() > 0) @@ -522,7 +539,7 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; qDebug() << err; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err; handleUploadError(pendingStateMessages.at(pair.second), pair.second, err); diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 15f99bf..3555548 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include #include @@ -76,12 +77,13 @@ private: bool handlePendingMessageError(const QString& id, const QString& errorText); std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); bool adjustPendingMessage(const QString& messageId, const QMap& data, bool final); + void initializeIDs(Shared::Message& target, const QXmppMessage& source) const; private: Account* acc; std::map pendingStateMessages; //key is message id, value is JID std::map pendingCorrectionMessages; //key is new mesage, value is originalOne - std::deque> uploadingSlotsQueue; + std::deque> uploadingSlotsQueue; }; } diff --git a/core/passwordStorageEngines/CMakeLists.txt b/core/passwordStorageEngines/CMakeLists.txt index 2afda3f..2a38931 100644 --- a/core/passwordStorageEngines/CMakeLists.txt +++ b/core/passwordStorageEngines/CMakeLists.txt @@ -1,9 +1,6 @@ if (WITH_KWALLET) - target_sources(squawk PRIVATE - kwallet.cpp - kwallet.h - ) + target_sources(squawk PRIVATE kwallet.cpp) add_subdirectory(wrappers) - target_include_directories(squawk PRIVATE $) + target_include_directories(squawk PRIVATE $) endif () diff --git a/core/passwordStorageEngines/kwallet.cpp b/core/passwordStorageEngines/kwallet.cpp index 0dfe071..c92085b 100644 --- a/core/passwordStorageEngines/kwallet.cpp +++ b/core/passwordStorageEngines/kwallet.cpp @@ -28,7 +28,8 @@ 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"); + +QLibrary Core::PSE::KWallet::lib(QString("%1/kwalletWrapper").arg(PLUGIN_PATH)); Core::PSE::KWallet::KWallet(): QObject(), diff --git a/core/passwordStorageEngines/kwallet.h b/core/passwordStorageEngines/kwallet.h index 28475d2..1f047e6 100644 --- a/core/passwordStorageEngines/kwallet.h +++ b/core/passwordStorageEngines/kwallet.h @@ -27,7 +27,7 @@ #include #include -#include +#include namespace Core { namespace PSE { diff --git a/core/passwordStorageEngines/wrappers/CMakeLists.txt b/core/passwordStorageEngines/wrappers/CMakeLists.txt index e8420da..6280cc0 100644 --- a/core/passwordStorageEngines/wrappers/CMakeLists.txt +++ b/core/passwordStorageEngines/wrappers/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(kwalletWrapper SHARED kwallet.cpp) -target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet) -install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +target_link_libraries(kwalletWrapper PRIVATE KF${QT_VERSION_MAJOR}::Wallet) + +install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) diff --git a/core/passwordStorageEngines/wrappers/kwallet.cpp b/core/passwordStorageEngines/wrappers/kwallet.cpp index d899985..5c7bc99 100644 --- a/core/passwordStorageEngines/wrappers/kwallet.cpp +++ b/core/passwordStorageEngines/wrappers/kwallet.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include +#include extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) { return KWallet::Wallet::openWallet(name, w, ot); diff --git a/core/squawk.cpp b/core/squawk.cpp index 1888487..7f04d9a 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -22,6 +22,8 @@ #include #include +#include "utils/jammer.h" + Core::Squawk::Squawk(QObject* parent): QObject(parent), accounts(), @@ -29,10 +31,10 @@ Core::Squawk::Squawk(QObject* parent): state(Shared::Availability::offline), network(), isInitialized(false), - clientCache(), #ifdef WITH_KWALLET - kwallet() + kwallet(), #endif + clientCache() { connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError); @@ -71,7 +73,6 @@ void Core::Squawk::stop() { QSettings settings; settings.beginGroup("core"); settings.beginWriteArray("accounts"); - SimpleCrypt crypto(passwordHash); for (std::deque::size_type i = 0; i < accounts.size(); ++i) { settings.setArrayIndex(i); Account* acc = accounts[i]; @@ -84,7 +85,7 @@ void Core::Squawk::stop() { password = acc->getPassword(); break; case Shared::AccountPassword::jammed: - password = crypto.encryptToString(acc->getPassword()); + password = Jammer::encrypt(acc->getPassword(), passwordHash); break; default: break; @@ -697,17 +698,16 @@ void Core::Squawk::readSettings() { settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt() ); + QString name = settings.value("name").toString(); QString password = settings.value("password", "").toString(); - if (passwordType == Shared::AccountPassword::jammed) { - SimpleCrypt crypto(passwordHash); - password = crypto.decryptToString(password); - } + if (passwordType == Shared::AccountPassword::jammed) + password = Jammer::decrypt(password, passwordHash); addAccount( settings.value("login").toString(), settings.value("server").toString(), password, - settings.value("name").toString(), + name, settings.value("resource").toString(), settings.value("active").toBool(), passwordType diff --git a/core/squawk.h b/core/squawk.h index 2ee122e..983f5ab 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -33,7 +33,6 @@ #include "shared/global.h" #include "shared/info.h" #include "shared/clientinfo.h" -#include "external/simpleCrypt/simplecrypt.h" #include #include @@ -42,10 +41,8 @@ #include "passwordStorageEngines/kwallet.h" #endif -namespace Core -{ -class Squawk : public QObject -{ +namespace Core { +class Squawk : public QObject { Q_OBJECT public: @@ -140,11 +137,12 @@ private: Shared::Availability state; NetworkAccess network; bool isInitialized; - ClientCache clientCache; #ifdef WITH_KWALLET PSE::KWallet kwallet; #endif + + ClientCache clientCache; private slots: void addAccount( diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt new file mode 100644 index 0000000..e030130 --- /dev/null +++ b/core/utils/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCE_FILES + jammer.cpp +) + +set(HEADER_FILES + jammer.h +) + +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/utils/jammer.cpp b/core/utils/jammer.cpp new file mode 100644 index 0000000..1714c6b --- /dev/null +++ b/core/utils/jammer.cpp @@ -0,0 +1,94 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#include "jammer.h" + +#include + +#include +#include + +struct CipherCtxDeleter { + void operator()(EVP_CIPHER_CTX* ctx) const { + EVP_CIPHER_CTX_free(ctx); + } +}; +typedef std::unique_ptr CipherCtx; + +QString Core::Jammer::encrypt(const QString& plaintext, qint64 key) { + QByteArray encryptedData = process(plaintext.toUtf8(), intToKey(key), true); + + return QString::fromUtf8(encryptedData.toHex()); +} + +QString Core::Jammer::decrypt(const QString& ciphertext, qint64 key) { + QByteArray encryptedData = QByteArray::fromHex(ciphertext.toUtf8()); + QByteArray decryptedData = process(encryptedData, intToKey(key), false); + + return QString::fromUtf8(decryptedData); +} + +std::string Core::Jammer::getOpenSSLErrorString() { + unsigned long errCode = ERR_get_error(); + if (errCode == 0) { + return "No OpenSSL error"; + } + const char *errMsg = ERR_reason_error_string(errCode); + return errMsg ? std::string(errMsg) : "Unknown OpenSSL error"; +} + +QByteArray Core::Jammer::process(const QByteArray& input, const QByteArray& key, bool encrypt) { + CipherCtx ctx(EVP_CIPHER_CTX_new()); + if (!ctx) + throw std::runtime_error("Failed to create password jammer context"); + + QByteArray output(input.size() + 16, 0); + int outputLength = 0; + int finalLength = 0; + + if (!ctx) + throw std::runtime_error("Failed to create EVP_CIPHER_CTX: " + getOpenSSLErrorString()); + + if (EVP_CipherInit_ex(ctx.get(), EVP_chacha20(), nullptr, toUChar(key), nullptr, encrypt) != 1) + throw std::runtime_error("EVP_CipherInit_ex failed. " + getOpenSSLErrorString()); + + if (EVP_CipherUpdate(ctx.get(), toUChar(output), &outputLength, toUChar(input), input.size()) != 1) + throw std::runtime_error("EVP_CipherUpdate failed. " + getOpenSSLErrorString()); + + if (EVP_CipherFinal_ex(ctx.get(), toUChar(output) + outputLength, &finalLength) != 1) + throw std::runtime_error("EVP_CipherFinal_ex failed. " + getOpenSSLErrorString()); + + output.resize(outputLength + finalLength); + + return output; +} + +QByteArray Core::Jammer::intToKey(qint64 key, int keySize) { + QByteArray keyBytes(reinterpret_cast(&key), sizeof(key)); + while (keyBytes.size() < keySize) + keyBytes.append(keyBytes); + + keyBytes.truncate(keySize); + return keyBytes; +} + +unsigned char* Core::Jammer::toUChar(QByteArray& data) { + return reinterpret_cast(data.data());} + +const unsigned char* Core::Jammer::toUChar(const QByteArray& data) { + return reinterpret_cast(data.constData());} diff --git a/core/utils/jammer.h b/core/utils/jammer.h new file mode 100644 index 0000000..456c14b --- /dev/null +++ b/core/utils/jammer.h @@ -0,0 +1,44 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#pragma once + +#include + +#include +#include + +namespace Core { + +class Jammer { +public: + static QString encrypt(const QString& plaintext, qint64 key); + static QString decrypt(const QString& ciphertext, qint64 key); + +private: + Jammer() = delete; + + static QByteArray process(const QByteArray& input, const QByteArray& key, bool encrypt); + static QByteArray intToKey(qint64 key, int keySize = 32); + + static unsigned char* toUChar(QByteArray& data); + static const unsigned char* toUChar(const QByteArray& data); + static std::string getOpenSSLErrorString(); +}; + +} diff --git a/external/lmdbal b/external/lmdbal index d62eddc..3701fb9 160000 --- a/external/lmdbal +++ b/external/lmdbal @@ -1 +1 @@ -Subproject commit d62eddc47edbec9f8c071459e045578f61ab58df +Subproject commit 3701fb92a1498bd737828d8d1df63d4c4d8f02c7 diff --git a/external/qxmpp b/external/qxmpp index 0cd7379..ca1bdb3 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit 0cd7379bd78aa01af7e84f2fad6269ef0c0ba49c +Subproject commit ca1bdb3e46c71ceb334e6dc52291850f0c96cb50 diff --git a/external/simpleCrypt/CMakeLists.txt b/external/simpleCrypt/CMakeLists.txt deleted file mode 100644 index 5f274ba..0000000 --- a/external/simpleCrypt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(simplecrypt LANGUAGES CXX) - -set(CMAKE_AUTOMOC ON) - -find_package(Qt5 COMPONENTS Core REQUIRED) - -add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h) - -target_link_libraries(simpleCrypt Qt5::Core) diff --git a/external/simpleCrypt/simplecrypt.cpp b/external/simpleCrypt/simplecrypt.cpp deleted file mode 100644 index 093403e..0000000 --- a/external/simpleCrypt/simplecrypt.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - 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 PROFITS; 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 -#include -#include -#include -#include -#include - -SimpleCrypt::SimpleCrypt(): -m_key(0), -m_compressionMode(CompressionAuto), -m_protectionMode(ProtectionChecksum), -m_lastError(ErrorNoError) {} - -SimpleCrypt::SimpleCrypt(quint64 key): -m_key(key), -m_compressionMode(CompressionAuto), -m_protectionMode(ProtectionChecksum), -m_lastError(ErrorNoError) -{ - 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(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(QRandomGenerator::global()->generate() & 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; -} diff --git a/external/simpleCrypt/simplecrypt.h b/external/simpleCrypt/simplecrypt.h deleted file mode 100644 index 0052618..0000000 --- a/external/simpleCrypt/simplecrypt.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - 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 PROFITS; 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 -#include -#include -#include - -/** - @ 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 m_keyParts; - CompressionMode m_compressionMode; - IntegrityProtectionMode m_protectionMode; - Error m_lastError; -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) - -#endif // SimpleCrypt_H diff --git a/main/root.cpp b/main/root.cpp index d43ae2f..f71951c 100644 --- a/main/root.cpp +++ b/main/root.cpp @@ -58,34 +58,42 @@ Root::~Root() { delete gui; if (core != nullptr) delete core; + delete coreThread; } delete global; } - void Root::initializeTranslation() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool defaultLoaded = defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath)); + if (!defaultLoaded) + qDebug() << "Couldn't load default translation"; +#else defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); +#endif installTranslator(&defaultTranslator); QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); bool found = false; for (QString share : shares) { found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); - if (found) { + if (found) break; - } - } - if (!found) { - currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); } - installTranslator(¤tTranslator); + if (!found) + found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); + + if (found) + installTranslator(¤tTranslator); + else + qDebug() << "Couldn't load current translation"; } void Root::initializeAppIcon() { - for (std::vector::size_type i = 0; i < appIconSizes.size(); ++i) - appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i])); + for (unsigned int appIconSize : appIconSizes) + appIcon.addFile(":images/logo.svg", QSize(appIconSize, appIconSize)); Root::setWindowIcon(appIcon); } @@ -94,18 +102,16 @@ bool Root::initializeSettings() { QVariant vs = settings.value("style"); if (vs.isValid()) { QString style = vs.toString().toLower(); - if (style != "system") { + if (style != "system") Shared::Global::setStyle(style); - } } if (Shared::Global::supported("colorSchemeTools")) { QVariant vt = settings.value("theme"); if (vt.isValid()) { QString theme = vt.toString(); - if (theme.toLower() != "system") { + if (theme.toLower() != "system") Shared::Global::setTheme(theme); - } } } diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 29ed800..0defdc3 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,19 +1,19 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.3 +pkgver=0.2.4 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp-qt5') -makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') -optdepends=('kwallet5: secure password storage (requires rebuild)' - 'kconfig5: system themes support (requires rebuild)' - 'kconfigwidgets5: system themes support (requires rebuild)' - 'kio5: better show in folder action (requires rebuild)') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal-qt6' 'qxmpp') +makedepends=('cmake>=3.3' 'imagemagick' 'qt6-tools' 'boost') +optdepends=('kwallet6: secure password storage (requires rebuild)' + 'kconfig6: system themes support (requires rebuild)' + 'kconfigwidgets6: system themes support (requires rebuild)' + 'kio6: better show in folder action (requires rebuild)') -source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") +source=("$pkgname-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") sha256sums=('SKIP') build() { cd "$srcdir/squawk" diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 4965b37..4bb9c87 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -1,3 +1,7 @@ configure_file(squawk.desktop squawk.desktop COPYONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) \ No newline at end of file +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) + +configure_file(macaw.me.squawk.appdata.xml macaw.me.squawk.appdata.xml COPYONLY) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/macaw.me.squawk.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) diff --git a/packaging/macaw.me.squawk.appdata.xml b/packaging/macaw.me.squawk.appdata.xml new file mode 100644 index 0000000..c188496 --- /dev/null +++ b/packaging/macaw.me.squawk.appdata.xml @@ -0,0 +1,33 @@ + + + macaw.me.squawk + CC0-1.0 + GPL-3.0+ + Squawk + Desktop Qt based XMPP messenger + +

+ Squawk is a lightweight XMPP desktop messenger. + The primary objective of this project is to offer + you a fast and user-friendly messaging experience + that closely aligns with your system’s style, while + also minimizing resource consumption. +

+

+ Squawk is still at a very early stage and might not suit + everyone but you are welcome to try it out. +

+
+ macaw.me.squawk.desktop + + + https://macaw.me/projects/squawk/0.2.2.png + View XMPP contacts and conversations + + + https://macaw.me/projects/squawk/ + + squawk + + blue@macaw.me +
diff --git a/packaging/squawk.desktop b/packaging/squawk.desktop index ba0f13c..c64f9ab 100644 --- a/packaging/squawk.desktop +++ b/packaging/squawk.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Type=Application -Version=1.0 +Version=0.2.4 Name=Squawk GenericName=Instant Messenger GenericName[ru]=Мгновенные сообщения diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 388c258..9eb9070 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,14 +1,14 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) - target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) + target_link_libraries(openFileManagerWindowJob PRIVATE KF${QT_VERSION_MAJOR}::KIOWidgets) - install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif () if (WITH_KCONFIG) add_library(colorSchemeTools SHARED colorschemetools.cpp) - target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore) - target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets) + target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigCore) + target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigWidgets) - install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif() diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp index ea2c23e..76eba9b 100644 --- a/plugins/colorschemetools.cpp +++ b/plugins/colorschemetools.cpp @@ -20,9 +20,9 @@ #include #include -#include -#include -#include +#include +#include +#include QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection); diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 9288650..717abf2 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -3,11 +3,8 @@ target_sources(squawk PRIVATE resources.qrc) configure_file(images/logo.svg squawk.svg COPYONLY) configure_file(squawk.rc squawk.rc COPYONLY) -if(WIN32) - set(CONVERT_BIN magick convert) -else(WIN32) - set(CONVERT_BIN convert) -endif(WIN32) +set(CONVERT_BIN magick) + execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) @@ -18,20 +15,20 @@ if (WIN32) set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc") set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE) target_sources(squawk PRIVATE ${SQUAWK_WIN_RC}) -endif(WIN32) +endif (WIN32) if (APPLE) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset") - execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set(MACOSX_BUNDLE_ICON_FILE squawk.icns) set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE) @@ -47,8 +44,8 @@ if (APPLE) MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO MACOSX_BUNDLE_BUNDLE_NAME "Squawk" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in) - endif(APPLE) - endif() + endif (APPLE) + endif () endif (APPLE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 2ef3970..45fcfd0 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -39,7 +39,4 @@ set(HEADER_FILES defines.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/shared/clientid.h b/shared/clientid.h index 5188b1c..05bed45 100644 --- a/shared/clientid.h +++ b/shared/clientid.h @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef SHARED_CLIENTID_H -#define SHARED_CLIENTID_H +#pragma once #include #include +#include namespace Shared { @@ -54,5 +54,3 @@ Q_DECLARE_METATYPE(Shared::ClientId) QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info); QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info); - -#endif // SHARED_CLIENTID_H diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 288e9fa..ca66c6d 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef SHARED_CLIENTINFO_H -#define SHARED_CLIENTINFO_H +#pragma once #include +#include #include #include @@ -54,5 +54,3 @@ private: QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info); QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info); - -#endif // SHARED_CLIENTINFO_H diff --git a/shared/defines.h b/shared/defines.h index 227a714..8ead3f6 100644 --- a/shared/defines.h +++ b/shared/defines.h @@ -16,9 +16,6 @@ * along with this program. If not, see . */ -#ifndef SHARED_DEFINES_H -#define SHARED_DEFINES_H +#pragma once #define SHARED_UNUSED(x) (void)(x) - -#endif diff --git a/shared/enums.h b/shared/enums.h index 43d1583..6f5e9db 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -101,8 +101,8 @@ enum class Avatar { valid }; Q_ENUM_NS(Avatar) -static const Avatar AvatarHighest = Avatar::valid; -static const Avatar AvatarLowest = Avatar::empty; +static const Avatar AvatarHighest = Avatar::valid; +static const Avatar AvatarLowest = Avatar::empty; static const std::deque messageStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; diff --git a/shared/global.cpp b/shared/global.cpp index 6618426..f0e3230 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -19,8 +19,10 @@ #include "global.h" #include +#include "defines.h" #include "enums.h" #include "ui/models/roster.h" + #ifdef WITH_OMEMO constexpr bool OMEMO_SUPPORT = true; #else @@ -36,11 +38,10 @@ QFont getFont (QFontDatabase::SystemFont type, bool bold = false, bool italic = if (factor != 1.0) { float ps = font.pointSizeF(); - if (ps != -1) { + if (ps != -1) font.setPointSizeF(ps * factor); - } else { + else font.setPointSize(font.pointSize() * factor); - } } return font; @@ -50,12 +51,12 @@ Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; #ifdef WITH_KIO -QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob"); +QLibrary Shared::Global::openFileManagerWindowJob(QString("%1/openFileManagerWindowJob").arg(PLUGIN_PATH)); Shared::Global::HighlightInFileManager Shared::Global::hfm = 0; #endif #ifdef WITH_KCONFIG -QLibrary Shared::Global::colorSchemeTools("colorSchemeTools"); +QLibrary Shared::Global::colorSchemeTools(QString("%1/colorSchemeTools").arg(PLUGIN_PATH)); Shared::Global::CreatePreview Shared::Global::createPreview = 0; Shared::Global::DeletePreview Shared::Global::deletePreview = 0; Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0; @@ -148,7 +149,7 @@ Shared::Global::Global(): smallFontMetrics(smallFont), headerFontMetrics(headerFont), titleFontMetrics(titleFont), - pluginSupport({ + optionalFeatures({ {"KWallet", false}, {"openFileManagerWindowJob", false}, {"colorSchemeTools", false} @@ -197,8 +198,7 @@ Shared::Global::Global(): static const QSize defaultIconFileInfoHeight(50, 50); -Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) -{ +Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) { std::map::const_iterator itr = instance->fileCache.find(path); if (itr == instance->fileCache.end()) { QMimeDatabase db; @@ -275,17 +275,17 @@ QString Shared::Global::getName(EncryptionProtocol ep) { } void Shared::Global::setSupported(const QString& pluginName, bool support) { - std::map::iterator itr = instance->pluginSupport.find(pluginName); - if (itr != instance->pluginSupport.end()) { + std::map::iterator itr = instance->optionalFeatures.find(pluginName); + if (itr != instance->optionalFeatures.end()) { itr->second = support; } } bool Shared::Global::supported(const QString& pluginName) { - std::map::iterator itr = instance->pluginSupport.find(pluginName); - if (itr != instance->pluginSupport.end()) { + std::map::iterator itr = instance->optionalFeatures.find(pluginName); + if (itr != instance->optionalFeatures.end()) return itr->second; - } + return false; } @@ -317,7 +317,7 @@ void Shared::Global::highlightInFileManager(const QString& path) qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: squawk wasn't compiled to support it, trying fallback"; #endif - QFileInfo info = path; + QFileInfo info(path); if (info.exists()) { QProcess proc; proc.start("xdg-mime", query); @@ -325,11 +325,10 @@ void Shared::Global::highlightInFileManager(const QString& path) QString output = proc.readLine().simplified(); QString folder; - if (info.isDir()) { + if (info.isDir()) folder = info.canonicalFilePath(); - } else { + else folder = info.canonicalPath(); - } if (output.contains(dolphinReg)) { //there is a bug on current (21.04.0) dolphin, it works correct only if you already have dolphin launched @@ -356,27 +355,32 @@ void Shared::Global::highlightInFileManager(const QString& path) } QIcon Shared::Global::createThemePreview(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { QIcon* icon = createPreview(path); QIcon localIcon = *icon; deletePreview(icon); return localIcon; - } else { - return QIcon(); } +#endif + + return QIcon(); } QString Shared::Global::getColorSchemeName(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { QString res; colorSchemeName(path, res); return res; - } else { - return ""; } +#endif + + return ""; } void Shared::Global::setTheme(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { if (path.toLower() == "system") { QApplication::setPalette(getInstance()->defaultSystemPalette); @@ -386,23 +390,26 @@ void Shared::Global::setTheme(const QString& path) { QApplication::setPalette(pallete); } } +#else + SHARED_UNUSED(path); + qDebug("setTheme() was called, but this version of squawk was compiled without KConfig support, ignoring"); +#endif } void Shared::Global::setStyle(const QString& style) { - if (style.toLower() == "system") { + if (style.toLower() == "system") QApplication::setStyle(getInstance()->defaultSystemStyle); - } else { + else QApplication::setStyle(style); - } } #define FROM_INT_INPL(Enum) \ template<> \ Enum Shared::Global::fromInt(int src) \ { \ - if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) { \ + if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) \ throw EnumOutOfRange(#Enum); \ - } \ + \ return static_cast(src); \ } \ template<> \ diff --git a/shared/global.h b/shared/global.h index 6d23c2f..627903f 100644 --- a/shared/global.h +++ b/shared/global.h @@ -135,7 +135,7 @@ namespace Shared { private: static Global* instance; - std::map pluginSupport; + std::map optionalFeatures; std::map fileCache; #ifdef WITH_KIO diff --git a/shared/messageinfo.cpp b/shared/messageinfo.cpp index 7502a6e..a26f23f 100644 --- a/shared/messageinfo.cpp +++ b/shared/messageinfo.cpp @@ -43,3 +43,19 @@ Shared::MessageInfo & Shared::MessageInfo::operator=(const Shared::MessageInfo& return *this; } + +QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info) { + in >> info.account; + in >> info.jid; + in >> info.messageId; + + return in; +} + +QDataStream& operator <<( QDataStream& out, const Shared::MessageInfo& info) { + out << info.account; + out << info.jid; + out << info.messageId; + + return out; +} diff --git a/shared/messageinfo.h b/shared/messageinfo.h index 3cf75bc..f06371b 100644 --- a/shared/messageinfo.h +++ b/shared/messageinfo.h @@ -19,6 +19,7 @@ #pragma once #include +#include namespace Shared { struct MessageInfo { @@ -34,3 +35,6 @@ struct MessageInfo { }; } + +QDataStream& operator << (QDataStream& out, const Shared::MessageInfo& info); +QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info); diff --git a/ui/utils/flowlayout.cpp b/ui/utils/flowlayout.cpp index ad7715e..34f978a 100644 --- a/ui/utils/flowlayout.cpp +++ b/ui/utils/flowlayout.cpp @@ -33,96 +33,78 @@ FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing): setContentsMargins(margin, margin, margin, margin); } -FlowLayout::~FlowLayout() -{ +FlowLayout::~FlowLayout() { QLayoutItem *item; - while ((item = takeAt(0))) { + while ((item = takeAt(0))) delete item; - } } -void FlowLayout::addItem(QLayoutItem *item) -{ +void FlowLayout::addItem(QLayoutItem *item) { itemList.append(item); } -int FlowLayout::horizontalSpacing() const -{ - if (m_hSpace >= 0) { +int FlowLayout::horizontalSpacing() const { + if (m_hSpace >= 0) return m_hSpace; - } else { + else return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } } -int FlowLayout::verticalSpacing() const -{ - if (m_vSpace >= 0) { +int FlowLayout::verticalSpacing() const { + if (m_vSpace >= 0) return m_vSpace; - } else { + else return smartSpacing(QStyle::PM_LayoutVerticalSpacing); - } } -int FlowLayout::count() const -{ +int FlowLayout::count() const { return itemList.size(); } -QLayoutItem *FlowLayout::itemAt(int index) const -{ +QLayoutItem *FlowLayout::itemAt(int index) const { return itemList.value(index); } -QLayoutItem *FlowLayout::takeAt(int index) -{ - if (index >= 0 && index < itemList.size()) { +QLayoutItem *FlowLayout::takeAt(int index) { + if (index >= 0 && index < itemList.size()) return itemList.takeAt(index); - } + return nullptr; } -Qt::Orientations FlowLayout::expandingDirections() const -{ +Qt::Orientations FlowLayout::expandingDirections() const { return Qt::Orientations(); } -bool FlowLayout::hasHeightForWidth() const -{ +bool FlowLayout::hasHeightForWidth() const { return true; } -int FlowLayout::heightForWidth(int width) const -{ +int FlowLayout::heightForWidth(int width) const { int height = doLayout(QRect(0, 0, width, 0), true); return height; } -void FlowLayout::setGeometry(const QRect &rect) -{ +void FlowLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); doLayout(rect, false); } -QSize FlowLayout::sizeHint() const -{ +QSize FlowLayout::sizeHint() const { return minimumSize(); } -QSize FlowLayout::minimumSize() const -{ +QSize FlowLayout::minimumSize() const { QSize size; - for (const QLayoutItem *item : qAsConst(itemList)) { + for (const QLayoutItem *item : std::as_const(itemList)) size = size.expandedTo(item->minimumSize()); - } const QMargins margins = contentsMargins(); size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return size; } -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const -{ +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); @@ -130,16 +112,16 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const int y = effectiveRect.y(); int lineHeight = 0; - for (QLayoutItem *item : qAsConst(itemList)) { + for (QLayoutItem *item : std::as_const(itemList)) { const QWidget *wid = item->widget(); int spaceX = horizontalSpacing(); - if (spaceX == -1) { + if (spaceX == -1) spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - } + int spaceY = verticalSpacing(); - if (spaceY == -1) { + if (spaceY == -1) spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - } + int nextX = x + item->sizeHint().width() + spaceX; if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { x = effectiveRect.x(); @@ -148,9 +130,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const lineHeight = 0; } - if (!testOnly) { + if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - } x = nextX; lineHeight = qMax(lineHeight, item->sizeHint().height()); @@ -158,8 +139,7 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const return y + lineHeight - rect.y() + bottom; } -int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const -{ +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const { QObject *parent = this->parent(); if (!parent) { return -1; diff --git a/ui/utils/progress.cpp b/ui/utils/progress.cpp index 73e4d02..676376b 100644 --- a/ui/utils/progress.cpp +++ b/ui/utils/progress.cpp @@ -49,7 +49,7 @@ Progress::Progress(quint16 p_size, QWidget* parent): QGridLayout* layout = new QGridLayout(); setLayout(layout); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->setVerticalSpacing(0); layout->setHorizontalSpacing(0); diff --git a/ui/widgets/accounts/account.cpp b/ui/widgets/accounts/account.cpp index 164af6c..1e2c4c7 100644 --- a/ui/widgets/accounts/account.cpp +++ b/ui/widgets/accounts/account.cpp @@ -26,6 +26,7 @@ Account::Account(): m_ui->setupUi(this); connect(m_ui->passwordType, qOverload(&QComboBox::currentIndexChanged), this, &Account::onComboboxChange); + QStandardItemModel *model = static_cast(m_ui->passwordType->model()); for (int i = static_cast(Shared::AccountPasswordLowest); i < static_cast(Shared::AccountPasswordHighest) + 1; ++i) { Shared::AccountPassword ap = static_cast(i); @@ -34,18 +35,14 @@ Account::Account(): m_ui->passwordType->setCurrentIndex(static_cast(Shared::AccountPassword::plain)); if (!Shared::Global::supported("KWallet")) { - QStandardItemModel *model = static_cast(m_ui->passwordType->model()); QStandardItem *item = model->item(static_cast(Shared::AccountPassword::kwallet)); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } } -Account::~Account() -{ -} +Account::~Account() {} -QMap Account::value() const -{ +QMap Account::value() const { QMap map; map["login"] = m_ui->login->text(); map["password"] = m_ui->password->text(); @@ -58,13 +55,11 @@ QMap Account::value() const return map; } -void Account::lockId() -{ +void Account::lockId() { m_ui->name->setReadOnly(true);; } -void Account::setData(const QMap& data) -{ +void Account::setData(const QMap& data) { m_ui->login->setText(data.value("login").toString()); m_ui->password->setText(data.value("password").toString()); m_ui->server->setText(data.value("server").toString()); @@ -73,8 +68,7 @@ void Account::setData(const QMap& data) m_ui->passwordType->setCurrentIndex(data.value("passwordType").toInt()); } -void Account::onComboboxChange(int index) -{ +void Account::onComboboxChange(int index) { QString description = Shared::Global::getDescription(Shared::Global::fromInt(index)); m_ui->comment->setText(description); } diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 7214a40..cedcf21 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -500,7 +500,7 @@ void Conversation::onFeedContext(const QPoint& pos) { } QString path = Shared::resolvePath(item->getAttachPath()); - if (path.size() > 0) { + if (!path.isEmpty()) { showMenu = true; QAction* open = contextMenu->addAction(Shared::icon("document-preview"), tr("Open")); connect(open, &QAction::triggered, [path]() { @@ -513,7 +513,7 @@ void Conversation::onFeedContext(const QPoint& pos) { }); } - bool hasAttach = item->getAttachPath() > 0 || item->getOutOfBandUrl() > 0; + bool hasAttach = !item->getAttachPath().isEmpty() || !item->getOutOfBandUrl().isEmpty(); //the only mandatory condition - is for the message to be outgoing, the rest is just a good intention on the server if (item->getOutgoing() && !hasAttach && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { showMenu = true; diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 41e4484..29ec9c6 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -141,7 +141,15 @@ void FeedView::updateGeometries() { const QStyle* st = style(); QSize layoutBounds = maximumViewportSize(); - QStyleOptionViewItem option = viewOptions(); + + + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + option.rect.setHeight(maxMessageHeight); option.rect.setWidth(layoutBounds.width()); int frameAroundContents = 0; @@ -182,7 +190,11 @@ void FeedView::updateGeometries() { previousOffset += elementMargin; } lastDate = currentDate; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); +#else QSize messageSize = itemDelegate(index)->sizeHint(option, index); +#endif uint32_t offsetX(0); if (specialDelegate) { if (index.data(Models::MessageFeed::SentByMe).toBool()) @@ -232,7 +244,11 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt previousOffset += elementMargin; } lastDate = currentDate; - QSize messageSize = itemDelegate(index)->sizeHint(option, index); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); +#else + QSize messageSize = itemDelegate(index)->sizeHint(option, index); +#endif if (previousOffset + messageSize.height() + elementMargin > totalHeight) return false; @@ -288,8 +304,14 @@ void FeedView::paintEvent(QPaintEvent* event) { } } + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + QPainter painter(vp); - QStyleOptionViewItem option = viewOptions(); option.features = QStyleOptionViewItem::WrapText; QPoint cursor = vp->mapFromGlobal(QCursor::pos()); @@ -319,7 +341,12 @@ void FeedView::paintEvent(QPaintEvent* event) { stripe.setWidth(viewportRect.width()); bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor); option.state.setFlag(QStyle::State_MouseOver, mouseOver); - itemDelegate(index)->paint(&painter, option, index); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + itemDelegateForIndex(index)->paint(&painter, option, index); +#else + itemDelegate(index)->paint(&painter, option, index); +#endif if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) drawDateDevider(option.rect.bottom(), lastDate, painter); @@ -379,8 +406,12 @@ void FeedView::setAnchorHovered(Shared::Hover type) { void FeedView::mouseMoveEvent(QMouseEvent* event) { if (!isVisible()) return; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragEndPoint = event->position().toPoint(); +#else + dragEndPoint = event->localPos().toPoint(); +#endif - dragEndPoint = event->localPos().toPoint(); if (mousePressed) { QPoint distance = dragStartPoint - dragEndPoint; if (distance.manhattanLength() > 5) @@ -423,7 +454,11 @@ void FeedView::mousePressEvent(QMouseEvent* event) { mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->localPos().toPoint(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragStartPoint = event->position().toPoint(); +#else + dragStartPoint = event->localPos().toPoint(); +#endif if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -441,7 +476,11 @@ void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { QAbstractItemView::mouseDoubleClickEvent(event); mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->localPos().toPoint(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragStartPoint = event->position().toPoint(); +#else + dragStartPoint = event->localPos().toPoint(); +#endif if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -469,7 +508,11 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) { if (mousePressed) { if (!dragging && specialDelegate) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QPoint point = event->position().toPoint(); +#else QPoint point = event->localPos().toPoint(); +#endif QModelIndex index = indexAt(point); if (index.isValid()) { QRect rect = visualRect(index); diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index a39afc8..19645c0 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef FEEDVIEW_H -#define FEEDVIEW_H +#pragma once #include #include @@ -30,11 +29,7 @@ #include #include -/** - * @todo write docs - */ -class FeedView : public QAbstractItemView -{ +class FeedView : public QAbstractItemView { Q_OBJECT public: FeedView(QWidget* parent = nullptr); @@ -111,5 +106,3 @@ private: static const std::set geometryChangingRoles; }; - -#endif //FEEDVIEW_H diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index b89b438..b82a992 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -659,7 +659,7 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(data.state)])); QString tt = Shared::Global::getName(data.state); if (data.state == Shared::Message::State::error) { - if (data.error > 0) + if (data.error.size() > 0) tt += ": " + data.error; } if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp index ee2e3ed..ae071ff 100644 --- a/ui/widgets/settings/settingslist.cpp +++ b/ui/widgets/settings/settingslist.cpp @@ -21,36 +21,39 @@ SettingsList::SettingsList(QWidget* parent): QListWidget(parent), lastWidth(0) -{ +{} +SettingsList::~SettingsList() {} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void SettingsList::initViewItemOption(QStyleOptionViewItem* option) const { + QListWidget::initViewItemOption(option); + if (!iconSize().isValid()) + option->decorationSize.setWidth(lastWidth); + + option->rect.setWidth(lastWidth); } - -SettingsList::~SettingsList() -{ -} - -QStyleOptionViewItem SettingsList::viewOptions() const -{ +#else +QStyleOptionViewItem SettingsList::viewOptions() const { QStyleOptionViewItem option = QListWidget::viewOptions(); - if (!iconSize().isValid()) { + if (!iconSize().isValid()) option.decorationSize.setWidth(lastWidth); - } + option.rect.setWidth(lastWidth); return option; } +#endif -void SettingsList::resizeEvent(QResizeEvent* event) -{ +void SettingsList::resizeEvent(QResizeEvent* event) { lastWidth = event->size().width(); QListWidget::resizeEvent(event); } -QRect SettingsList::visualRect(const QModelIndex& index) const -{ +QRect SettingsList::visualRect(const QModelIndex& index) const { QRect res = QListWidget::visualRect(index); - if (index.isValid()) { + if (index.isValid()) res.setWidth(lastWidth); - } + return res; } diff --git a/ui/widgets/settings/settingslist.h b/ui/widgets/settings/settingslist.h index 64c9d57..14ebf55 100644 --- a/ui/widgets/settings/settingslist.h +++ b/ui/widgets/settings/settingslist.h @@ -16,29 +16,28 @@ * along with this program. If not, see . */ -#ifndef UI_SETTINGSLIST_H -#define UI_SETTINGSLIST_H +#pragma once #include #include +#include -/** - * @todo write docs - */ -class SettingsList : public QListWidget -{ +class SettingsList : public QListWidget { Q_OBJECT public: SettingsList(QWidget* parent = nullptr); ~SettingsList(); protected: +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void initViewItemOption(QStyleOptionViewItem* option) const override; +#else QStyleOptionViewItem viewOptions() const override; +#endif + void resizeEvent(QResizeEvent * event) override; QRect visualRect(const QModelIndex & index) const override; private: int lastWidth; }; - -#endif // UI_SETTINGSLIST_H