Compare commits

..

No commits in common. "encryption" and "master" have entirely different histories.

188 changed files with 7259 additions and 15357 deletions

6
.gitmodules vendored
View File

@ -1,9 +1,3 @@
[submodule "external/qxmpp"] [submodule "external/qxmpp"]
path = external/qxmpp path = external/qxmpp
url = https://github.com/qxmpp-project/qxmpp.git url = https://github.com/qxmpp-project/qxmpp.git
[submodule "external/storage"]
path = external/storage
url = https://git.macaw.me/blue/storage
[submodule "external/lmdbal"]
path = external/lmdbal
url = gitea@git.macaw.me:blue/lmdbal.git

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,9 @@
- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues - deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues
- all of the expandable roster items now get saved between launches - all of the expandable roster items now get saved between launches
- settings file on the disk is not rewritten every roster element expansion or collapse - settings file on the disk is not rewritten every roster element expansion or collapse
- removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar)
- vcard window now is Info system and it can display more information
### New features ### New features
- now you can enable tray icon from settings! - Now you can enable tray icon from settings!
- there is a job queue now, this allowes to spread a bit the spam on the server at connection time
- squawk now querries clients of it's peers, you can see what programs other people use
## Squawk 0.2.2 (May 05, 2022) ## Squawk 0.2.2 (May 05, 2022)
### Bug fixes ### Bug fixes

View File

@ -1,12 +1,10 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.4)
project(squawk VERSION 0.2.3 LANGUAGES CXX) project(squawk VERSION 0.2.3 LANGUAGES CXX)
cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0076 NEW)
cmake_policy(SET CMP0077 NEW)
cmake_policy(SET CMP0079 NEW) cmake_policy(SET CMP0079 NEW)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(QT_VERSION_MAJOR 5)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
@ -29,42 +27,44 @@ add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG})
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
option(SYSTEM_QXMPP "Use system qxmpp lib" ON) option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
option(SYSTEM_LMDBAL "Use system lmdbal lib" ON)
option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KWALLET "Build KWallet support module" ON)
option(WITH_KIO "Build KIO support module" ON) option(WITH_KIO "Build KIO support module" ON)
option(WITH_KCONFIG "Build KConfig support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON)
option(WITH_OMEMO "Build OMEMO support module" ON)
# Dependencies # Dependencies
## Qt ## Qt
if (NOT DEFINED QT_VERSION_MAJOR) set(QT_VERSION_MAJOR 5)
find_package(QT NAMES Qt6 Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
else ()
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core)
endif()
find_package(Boost COMPONENTS) find_package(Boost COMPONENTS)
target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5Widgets_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5DBus_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5Gui_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5Xml_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5Network_INCLUDE_DIRS})
target_include_directories(squawk PRIVATE ${Qt5Core_INCLUDE_DIRS})
## OMEMO ## QXmpp
if (WITH_OMEMO) if (SYSTEM_QXMPP)
find_package(PkgConfig) find_package(QXmpp CONFIG)
if (PKG_CONFIG_FOUND)
pkg_check_modules(OMEMO libomemo-c) if (NOT QXmpp_FOUND)
if (OMEMO_FOUND) set(SYSTEM_QXMPP OFF)
target_compile_definitions(squawk PRIVATE WITH_OMEMO) message("QXmpp package wasn't found, trying to build with bundled QXmpp")
message("Building with support of OMEMO")
else ()
message("libomemo-c package wasn't found, trying to build without OMEMO support")
set(WITH_OMEMO OFF)
endif ()
else () else ()
message("PKG_CONFIG module wasn't found, can not check libomemo-c support, trying to build without OMEMO support") message("Building with system QXmpp")
set(WITH_OMEMO OFF)
endif () endif ()
endif () endif ()
if (NOT SYSTEM_QXMPP)
target_link_libraries(squawk PRIVATE qxmpp)
add_subdirectory(external/qxmpp)
else ()
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
endif ()
## KIO ## KIO
if (WITH_KIO) if (WITH_KIO)
find_package(KF5KIO CONFIG) find_package(KF5KIO CONFIG)
@ -91,7 +91,6 @@ if (WITH_KWALLET)
endif () endif ()
endif () endif ()
## KConfig
if (WITH_KCONFIG) if (WITH_KCONFIG)
find_package(KF5Config CONFIG) find_package(KF5Config CONFIG)
if (NOT KF5Config_FOUND) if (NOT KF5Config_FOUND)
@ -110,86 +109,24 @@ if (WITH_KCONFIG)
endif() endif()
endif() endif()
## QXmpp ## Signal (TODO)
if (SYSTEM_QXMPP) # find_package(Signal REQUIRED)
if (WITH_OMEMO)
find_package(QXmpp CONFIG COMPONENTS Omemo)
else ()
find_package(QXmpp CONFIG)
endif ()
if (NOT QXmpp_FOUND) ## LMDB
set(SYSTEM_QXMPP OFF) find_package(LMDB REQUIRED)
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
else ()
message("Building with system QXmpp")
endif ()
endif () #it's endif() + if() and not else() because I want it to have a fallback behaviour
if (NOT SYSTEM_QXMPP) #we can fail finding system QXmpp and this way we'll check bundled before failing completely
message("Building with bundled QXmpp")
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base) # Linking
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/client) target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) target_link_libraries(squawk PRIVATE lmdb)
target_link_libraries(squawk PRIVATE simpleCrypt)
if (WITH_OMEMO) # Link thread libraries on Linux
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo)
target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo)
set(BUILD_OMEMO ON)
set(BUILD_TESTS OFF)
else ()
set(BUILD_OMEMO OFF)
endif ()
add_subdirectory(external/qxmpp)
add_library(QXmpp::QXmpp ALIAS QXmppQt${QT_VERSION_MAJOR})
if (WITH_OMEMO)
target_include_directories(QXmppOmemoQt${QT_VERSION_MAJOR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src)
add_library(QXmpp::Omemo ALIAS QXmppOmemoQt${QT_VERSION_MAJOR})
endif ()
endif ()
## LMDBAL
if (SYSTEM_LMDBAL)
find_package(lmdbal)
if (NOT lmdbal_FOUND)
set(SYSTEM_LMDBAL OFF)
message("LMDBAL package wasn't found, trying to build with bundled LMDBAL")
else ()
message("Building with system LMDBAL")
endif ()
else()
message("Building with bundled LMDBAL")
set(BUILD_STATIC ON)
add_subdirectory(external/lmdbal)
add_library(LMDBAL::LMDBAL ALIAS LMDBAL)
endif()
## Linking
target_link_libraries(squawk
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::DBus
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Xml
LMDBAL::LMDBAL
QXmpp::QXmpp
simpleCrypt
)
if (WITH_OMEMO)
target_link_libraries(squawk PRIVATE QXmpp::Omemo)
endif ()
## Link thread libraries on Linux
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
target_link_libraries(squawk PRIVATE Threads::Threads) target_link_libraries(squawk PRIVATE Threads::Threads)
endif() endif()
## Build type # Build type
if (NOT CMAKE_BUILD_TYPE) if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif () endif ()
@ -221,7 +158,7 @@ add_subdirectory(shared)
add_subdirectory(translations) add_subdirectory(translations)
add_subdirectory(ui) add_subdirectory(ui)
## Install the executable # Install the executable
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
@ -229,7 +166,7 @@ install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
if (CMAKE_BUILD_TYPE STREQUAL "Release") if (CMAKE_BUILD_TYPE STREQUAL "Release")
if (APPLE) if (APPLE)
add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..." add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..."
COMMAND "${Qt${QT_VERSION_MAJOR}Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app" COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app"
) )
endif(APPLE) endif(APPLE)
endif() endif()

View File

@ -9,9 +9,9 @@
### Prerequisites ### Prerequisites
- QT 5.12 *(lower versions might work but it wasn't tested)* - QT 5.12 *(lower versions might work but it wasn't tested)*
- lmdb
- CMake 3.4 or higher - CMake 3.4 or higher
- qxmpp 1.1.0 or higher - qxmpp 1.1.0 or higher
- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) around lmdb)
- KDE Frameworks: kwallet (optional) - KDE Frameworks: kwallet (optional)
- KDE Frameworks: KIO (optional) - KDE Frameworks: KIO (optional)
- KDE Frameworks: KConfig (optional) - KDE Frameworks: KConfig (optional)
@ -92,11 +92,9 @@ You can always refer to `appveyor.yml` to see how AppVeyor build squawk.
Here is the list of keys you can pass to configuration phase of `cmake ..`. Here is the list of keys you can pass to configuration phase of `cmake ..`.
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`) - `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`) - `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
- `SYSTEM_LMDBAL` - `True` tries to link against `LMDABL` installed in the system, `False` builds bundled `LMDBAL` library (default is `True`)
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`) - `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
- `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_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_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 `True`)
## License ## License

52
cmake/FindLMDB.cmake Normal file
View File

@ -0,0 +1,52 @@
#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license
#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code
# Try to find LMDB headers and library.
#
# Usage of this module as follows:
#
# find_package(LMDB)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# LMDB_ROOT_DIR Set this variable to the root installation of
# LMDB if the module has problems finding the
# proper installation path.
#
# Variables defined by this module:
#
# LMDB_FOUND System has LMDB library/headers.
# LMDB_LIBRARIES The LMDB library.
# LMDB_INCLUDE_DIRS The location of LMDB headers.
find_path(LMDB_ROOT_DIR
NAMES include/lmdb.h
)
find_library(LMDB_LIBRARIES
NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible
HINTS ${LMDB_ROOT_DIR}/lib
)
add_library(lmdb UNKNOWN IMPORTED)
set_target_properties(lmdb PROPERTIES
IMPORTED_LOCATION ${LMDB_LIBRARIES}
)
find_path(LMDB_INCLUDE_DIRS
NAMES lmdb.h
HINTS ${LMDB_ROOT_DIR}/include
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LMDB DEFAULT_MSG
LMDB_LIBRARIES
LMDB_INCLUDE_DIRS
)
mark_as_advanced(
LMDB_ROOT_DIR
LMDB_LIBRARIES
LMDB_INCLUDE_DIRS
)

15
cmake/FindSignal.cmake Normal file
View File

@ -0,0 +1,15 @@
find_path(Signal_INCLUDE_DIR NAMES signal/signal_protocol.h)
find_library(Signal_LIBRARY signal-protocol-c)
mark_as_advanced(Signal_INCLUDE_DIR Signal_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Signal REQUIRED_VARS Signal_LIBRARY Signal_INCLUDE_DIR)
if (Signal_FOUND AND NOT TARGET Signal::Signal)
add_library(Signal::Signal UNKNOWN IMPORTED)
set_target_properties(Signal::Signal PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${Signal_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${Signal_INCLUDE_DIR}"
)
endif ()

View File

@ -3,34 +3,27 @@ if(WIN32)
set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp) set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp)
endif(WIN32) endif(WIN32)
set(SOURCE_FILES
account.cpp
adapterfunctions.cpp
conference.cpp
contact.cpp
rosteritem.cpp
${SIGNALCATCHER_SOURCE}
squawk.cpp
)
set(HEADER_FILES
account.h
adapterfunctions.h
conference.h
contact.h
rosteritem.h
signalcatcher.h
squawk.h
)
target_sources(squawk PRIVATE target_sources(squawk PRIVATE
${SOURCE_FILES} account.cpp
${HEADER_FILES} account.h
) adapterfunctions.cpp
adapterfunctions.h
conference.cpp
conference.h
contact.cpp
contact.h
networkaccess.cpp
networkaccess.h
rosteritem.cpp
rosteritem.h
${SIGNALCATCHER_SOURCE}
signalcatcher.h
squawk.cpp
squawk.h
)
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
add_subdirectory(handlers) add_subdirectory(handlers)
add_subdirectory(storage)
add_subdirectory(passwordStorageEngines) add_subdirectory(passwordStorageEngines)
add_subdirectory(components)
add_subdirectory(delayManager)

View File

@ -18,20 +18,11 @@
#include "account.h" #include "account.h"
#include <QXmppMessage.h> #include <QXmppMessage.h>
#include <QDateTime> #include <QDateTime>
#include "shared/defines.h" using namespace Core;
Core::Account::Account( Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent):
const QString& p_login,
const QString& p_server,
const QString& p_password,
const QString& p_name,
bool p_active,
NetworkAccess* p_net,
QObject* parent
):
QObject(parent), QObject(parent),
name(p_name), name(p_name),
archiveQueries(), archiveQueries(),
@ -39,25 +30,7 @@ Core::Account::Account(
config(), config(),
presence(), presence(),
state(Shared::ConnectionState::disconnected), state(Shared::ConnectionState::disconnected),
mh(new MessageHandler(this)),
rh(new RosterHandler(this)),
vh(new VCardHandler(this)),
dh(new DiscoveryHandler(this)),
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
th(new TrustHandler(this)),
#endif
#ifdef WITH_OMEMO
oh(new OmemoHandler(this)),
om(new QXmppOmemoManager(oh)),
#endif
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
tm(new QXmppTrustManager(th)),
cm(new QXmppCarbonManagerV2()),
psm(new QXmppPubSubManager()),
#else
cm(new QXmppCarbonManager()), cm(new QXmppCarbonManager()),
#endif
am(new QXmppMamManager()), am(new QXmppMamManager()),
mm(new QXmppMucManager()), mm(new QXmppMucManager()),
bm(new QXmppBookmarkManager()), bm(new QXmppBookmarkManager()),
@ -69,29 +42,20 @@ Core::Account::Account(
reconnectScheduled(false), reconnectScheduled(false),
reconnectTimer(new QTimer), reconnectTimer(new QTimer),
network(p_net), network(p_net),
delay(nullptr),
passwordType(Shared::AccountPassword::plain), passwordType(Shared::AccountPassword::plain),
lastError(Error::none), lastError(Error::none),
pepSupport(Shared::Support::unknown), pepSupport(false),
active(p_active), active(p_active),
notReadyPassword(false), notReadyPassword(false),
loadingOmemo(false) mh(new MessageHandler(this)),
rh(new RosterHandler(this)),
vh(new VCardHandler(this))
{ {
config.setUser(p_login); config.setUser(p_login);
config.setDomain(p_server); config.setDomain(p_server);
config.setPassword(p_password); config.setPassword(p_password);
config.setAutoAcceptSubscriptions(true); config.setAutoAcceptSubscriptions(true);
//config.setAutoReconnectionEnabled(false); //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);
QObject::connect(delay, &DelayManager::Manager::requestOwnVCard, vm, &QXmppVCardManager::requestClientVCard);
QObject::connect(delay, &DelayManager::Manager::requestVCard, vm, &QXmppVCardManager::requestVCard);
rh->initialize();
vh->initialize();
dh->initialize();
QObject::connect(&client, &QXmppClient::stateChanged, this, &Account::onClientStateChange); QObject::connect(&client, &QXmppClient::stateChanged, this, &Account::onClientStateChange);
QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived); QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived);
@ -100,10 +64,8 @@ Core::Account::Account(
client.addExtension(cm); client.addExtension(cm);
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
QObject::connect(cm, &QXmppCarbonManager::messageReceived, mh, &MessageHandler::onCarbonMessageReceived); QObject::connect(cm, &QXmppCarbonManager::messageReceived, mh, &MessageHandler::onCarbonMessageReceived);
QObject::connect(cm, &QXmppCarbonManager::messageSent, mh, &MessageHandler::onCarbonMessageSent); QObject::connect(cm, &QXmppCarbonManager::messageSent, mh, &MessageHandler::onCarbonMessageSent);
#endif
client.addExtension(am); client.addExtension(am);
@ -117,6 +79,9 @@ Core::Account::Account(
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete); QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete);
QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
@ -124,34 +89,6 @@ Core::Account::Account(
client.addExtension(rcpm); client.addExtension(rcpm);
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
client.addExtension(psm);
#ifdef WITH_OMEMO
QObject::connect(delay, &DelayManager::Manager::requestBundles, oh, &OmemoHandler::requestBundles);
QObject::connect(delay, &DelayManager::Manager::requestOwnBundles, oh, &OmemoHandler::requestOwnBundles);
QObject::connect(om, &QXmppOmemoManager::deviceAdded, oh, &OmemoHandler::onOmemoDeviceAdded);
client.addExtension(tm);
client.addExtension(om);
om->setSecurityPolicy(QXmpp::Toakafa);
if (oh->hasOwnDevice()) {
QXmppTask<bool> future = om->load();
loadingOmemo = true;
future.then(this, [this] (bool result) {
loadingOmemo = false;
if (state == Shared::ConnectionState::scheduled)
client.connectToServer(config, presence);
if (result)
qDebug() << "successfully loaded OMEMO data for account" << getName();
else
qDebug() << "couldn't load OMEMO data for account" << getName();
});
}
#endif
reconnectTimer->setSingleShot(true); reconnectTimer->setSingleShot(true);
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
@ -160,14 +97,14 @@ Core::Account::Account(
logger->setLoggingType(QXmppLogger::SignalLogging); logger->setLoggingType(QXmppLogger::SignalLogging);
client.setLogger(logger); client.setLogger(logger);
QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text) { QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
SHARED_UNUSED(type);
qDebug() << text; qDebug() << text;
}); });
} }
} }
Core::Account::~Account() { Account::~Account()
{
if (reconnectScheduled) { if (reconnectScheduled) {
reconnectScheduled = false; reconnectScheduled = false;
reconnectTimer->stop(); reconnectTimer->stop();
@ -177,40 +114,27 @@ Core::Account::~Account() {
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
rh->clear(); //conferenses inside of roster handler hold QXmppMuc objects. delete vh;
//If we destroy QXmppMucManager, then when we will be destroying RosterHandler delete mh;
//it will try to destory Core::Conference objects delete rh;
//and inside of those QXmppMuc objects will already be destroyed.
//So, clear will start the destruction from Core::Conference and this way it's not gonna crash
delete delay;
delete reconnectTimer; delete reconnectTimer;
delete rcpm; delete rcpm;
delete dm;
delete um; delete um;
delete bm; delete bm;
delete mm; delete mm;
delete am; delete am;
delete cm; delete cm;
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
delete psm;
#endif
#ifdef WITH_OMEMO
delete om;
delete tm;
delete oh;
delete th;
#endif
delete dh;
delete vh;
delete rh;
delete mh;
} }
Shared::ConnectionState Core::Account::getState() const { Shared::ConnectionState Core::Account::getState() const
return state;} {
return state;
}
void Core::Account::connect() { void Core::Account::connect()
{
if (reconnectScheduled) { if (reconnectScheduled) {
reconnectScheduled = false; reconnectScheduled = false;
reconnectTimer->stop(); reconnectTimer->stop();
@ -219,12 +143,7 @@ void Core::Account::connect() {
if (notReadyPassword) { if (notReadyPassword) {
emit needPassword(); emit needPassword();
} else { } else {
if (loadingOmemo) { client.connectToServer(config, presence);
state = Shared::ConnectionState::scheduled;
emit connectionStateChanged(state);
} else {
client.connectToServer(config, presence);
}
} }
} else { } else {
@ -232,30 +151,28 @@ void Core::Account::connect() {
} }
} }
void Core::Account::onReconnectTimer() { void Core::Account::onReconnectTimer()
{
if (reconnectScheduled) { if (reconnectScheduled) {
reconnectScheduled = false; reconnectScheduled = false;
connect(); connect();
} }
} }
void Core::Account::disconnect() { void Core::Account::disconnect()
{
if (reconnectScheduled) { if (reconnectScheduled) {
reconnectScheduled = false; reconnectScheduled = false;
reconnectTimer->stop(); reconnectTimer->stop();
} }
if (state != Shared::ConnectionState::disconnected) { if (state != Shared::ConnectionState::disconnected) {
//rh->clearConferences(); //rh->clearConferences();
if (state != Shared::ConnectionState::scheduled) { client.disconnectFromServer();
client.disconnectFromServer();
} else {
state = Shared::ConnectionState::disconnected;
emit connectionStateChanged(state);
}
} }
} }
void Core::Account::onClientStateChange(QXmppClient::State st) { void Core::Account::onClientStateChange(QXmppClient::State st)
{
switch (st) { switch (st) {
case QXmppClient::ConnectedState: { case QXmppClient::ConnectedState: {
if (state != Shared::ConnectionState::connected) { if (state != Shared::ConnectionState::connected) {
@ -263,27 +180,9 @@ void Core::Account::onClientStateChange(QXmppClient::State st) {
Shared::ConnectionState os = state; Shared::ConnectionState os = state;
state = Shared::ConnectionState::connected; state = Shared::ConnectionState::connected;
if (os == Shared::ConnectionState::connecting) { if (os == Shared::ConnectionState::connecting) {
#ifdef WITH_OMEMO qDebug() << "running service discovery for account" << name;
if (!oh->hasOwnDevice()) { dm->requestItems(getServer());
qDebug() << "setting up OMEMO data for account" << getName(); dm->requestInfo(getServer());
om->changeDeviceLabel(QGuiApplication::applicationDisplayName() + " - " + QSysInfo::productType());
QXmppTask<bool> future = om->setUp();
future.then(this, [this] (bool result) {
if (result)
qDebug() << "successfully set up OMEMO data for account" << getName();
else
qDebug() << "couldn't set up OMEMO data for account" << getName();
if (state == Shared::ConnectionState::connected)
runDiscoveryService();
});
} else {
runDiscoveryService();
}
#else
runDiscoveryService();
#endif
} }
lastError = Error::none; lastError = Error::none;
emit connectionStateChanged(state); emit connectionStateChanged(state);
@ -315,7 +214,8 @@ void Core::Account::onClientStateChange(QXmppClient::State st) {
} }
} }
void Core::Account::reconnect() { void Core::Account::reconnect()
{
if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting
if (state == Shared::ConnectionState::connected) { if (state == Shared::ConnectionState::connected) {
reconnectScheduled = true; reconnectScheduled = true;
@ -327,7 +227,8 @@ void Core::Account::reconnect() {
} }
} }
Shared::Availability Core::Account::getAvailability() const { Shared::Availability Core::Account::getAvailability() const
{
if (state == Shared::ConnectionState::connected) { if (state == Shared::ConnectionState::connected) {
QXmppPresence::AvailableStatusType pres = presence.availableStatusType(); QXmppPresence::AvailableStatusType pres = presence.availableStatusType();
return static_cast<Shared::Availability>(pres); //they are compatible; return static_cast<Shared::Availability>(pres); //they are compatible;
@ -336,42 +237,37 @@ Shared::Availability Core::Account::getAvailability() const {
} }
} }
void Core::Account::setAvailability(Shared::Availability avail) { void Core::Account::setAvailability(Shared::Availability avail)
{
if (avail == Shared::Availability::offline) { if (avail == Shared::Availability::offline) {
disconnect(); //TODO not sure how to do here - changing state may cause connection or disconnection disconnect(); //TODO not sure how to do here - changing state may cause connection or disconnection
} else { } else {
QXmppPresence::AvailableStatusType pres = static_cast<QXmppPresence::AvailableStatusType>(avail); QXmppPresence::AvailableStatusType pres = static_cast<QXmppPresence::AvailableStatusType>(avail);
presence.setAvailableStatusType(pres); presence.setAvailableStatusType(pres);
if (state != Shared::ConnectionState::disconnected) if (state != Shared::ConnectionState::disconnected) {
client.setClientPresence(presence); client.setClientPresence(presence);
}
} }
} }
void Core::Account::runDiscoveryService() { void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
qDebug() << "running service discovery for account" << name; {
dm->requestItems(getServer());
dm->requestInfo(getServer());
}
void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) {
QString id = p_presence.from(); QString id = p_presence.from();
QStringList comps = id.split("/"); QStringList comps = id.split("/");
QString jid = comps.front().toLower(); QString jid = comps.front().toLower();
QString resource = comps.back(); QString resource = comps.back();
if (jid == getBareJid()) { if (jid == getBareJid()) {
if (resource == getResource()) if (resource == getResource()) {
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType())); emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
} else {
vh->handlePresenceOfMyAccountChange(p_presence); vh->handleOtherPresenceOfMyAccountChange(p_presence);
}
} else { } else {
RosterItem* item = rh->getRosterItem(jid); RosterItem* item = rh->getRosterItem(jid);
if (item != nullptr) { if (item != 0) {
if (item->isMuc()) //MUC presence is handled by inner muc events item->handlePresence(p_presence);
return;
else
item->handlePresence(p_presence);
} }
} }
@ -379,46 +275,37 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) {
case QXmppPresence::Error: case QXmppPresence::Error:
qDebug() << "An error reported by presence from" << id << p_presence.error().text(); qDebug() << "An error reported by presence from" << id << p_presence.error().text();
break; break;
case QXmppPresence::Available: { case QXmppPresence::Available:{
QDateTime lastInteraction = p_presence.lastUserInteraction(); QDateTime lastInteraction = p_presence.lastUserInteraction();
if (!lastInteraction.isValid()) if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc(); lastInteraction = QDateTime::currentDateTimeUtc();
}
emit addPresence(jid, resource, { emit addPresence(jid, resource, {
{"lastActivity", lastInteraction}, {"lastActivity", lastInteraction},
{"availability", p_presence.availableStatusType()}, //TODO check and handle invisible {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible
{"status", p_presence.statusText()}, {"status", p_presence.statusText()}
{"client", QVariant::fromValue(
Shared::ClientId(
p_presence.capabilityNode(),
p_presence.capabilityVer().toBase64(),
p_presence.capabilityHash())
)
}
}); });
} break; }
break;
case QXmppPresence::Unavailable: case QXmppPresence::Unavailable:
emit removePresence(jid, resource); emit removePresence(jid, resource);
break; break;
case QXmppPresence::Subscribe: case QXmppPresence::Subscribe:
qDebug("xmpp presence \"subscribe\" received, do not yet know what to do, skipping"); qDebug("xmpp presence \"subscribe\" received, do not yet know what to do, skipping");
break;
case QXmppPresence::Subscribed: case QXmppPresence::Subscribed:
qDebug("xmpp presence \"subscribed\" received, do not yet know what to do, skipping"); qDebug("xmpp presence \"subscribed\" received, do not yet know what to do, skipping");
break;
case QXmppPresence::Unsubscribe: case QXmppPresence::Unsubscribe:
qDebug("xmpp presence \"unsubscribe\" received, do not yet know what to do, skipping"); qDebug("xmpp presence \"unsubscribe\" received, do not yet know what to do, skipping");
break;
case QXmppPresence::Unsubscribed: case QXmppPresence::Unsubscribed:
qDebug("xmpp presence \"unsubscribed\" received, do not yet know what to do, skipping"); qDebug("xmpp presence \"unsubscribed\" received, do not yet know what to do, skipping");
break;
case QXmppPresence::Probe: case QXmppPresence::Probe:
qDebug("xmpp presence \"probe\" received, do not yet know what to do, skipping"); qDebug("xmpp presence \"probe\" received, do not yet know what to do, skipping");
break; break;
} }
} }
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
{
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
std::map<QString, QString>::const_iterator itr = archiveQueries.find(queryId); std::map<QString, QString>::const_iterator itr = archiveQueries.find(queryId);
if (itr != archiveQueries.end()) { if (itr != archiveQueries.end()) {
@ -430,19 +317,21 @@ void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMess
sMsg.setState(Shared::Message::State::sent); sMsg.setState(Shared::Message::State::sent);
QString oId = msg.replaceId(); QString oId = msg.replaceId();
if (oId.size() > 0) if (oId.size() > 0) {
item->correctMessageInArchive(oId, sMsg); item->correctMessageInArchive(oId, sMsg);
else } else {
item->addMessageToArchive(sMsg); item->addMessageToArchive(sMsg);
}
} }
} }
} }
void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { void Core::Account::requestArchive(const QString& jid, int count, const QString& before)
{
qDebug() << "An archive request for " << jid << ", before " << before; qDebug() << "An archive request for " << jid << ", before " << before;
RosterItem* item = rh->getRosterItem(jid); RosterItem* contact = rh->getRosterItem(jid);
if (item == nullptr) { if (contact == 0) {
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping";
emit responseArchive(jid, std::list<Shared::Message>(), true); emit responseArchive(jid, std::list<Shared::Message>(), true);
return; return;
@ -450,21 +339,14 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString&
if (state != Shared::ConnectionState::connected) { if (state != Shared::ConnectionState::connected) {
qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping"; qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping";
emit responseArchive(jid, std::list<Shared::Message>(), false); emit responseArchive(contact->jid, std::list<Shared::Message>(), false);
return;
} }
#ifdef WITH_OMEMO contact->requestHistory(count, before);
if (!item->isMuc()) {
Contact* contact = static_cast<Contact*>(item);
if (contact->omemoBundles == Shared::Possible::unknown)
oh->requestBundles(jid);
}
#endif
item->requestHistory(count, before);
} }
void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) { void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at)
{
RosterItem* contact = static_cast<RosterItem*>(sender()); RosterItem* contact = static_cast<RosterItem*>(sender());
QString to; QString to;
@ -508,7 +390,8 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a
archiveQueries.insert(std::make_pair(q, contact->jid)); archiveQueries.insert(std::make_pair(q, contact->jid));
} }
void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) { void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete)
{
std::map<QString, QString>::const_iterator itr = archiveQueries.find(queryId); std::map<QString, QString>::const_iterator itr = archiveQueries.find(queryId);
if (itr != archiveQueries.end()) { if (itr != archiveQueries.end()) {
QString jid = itr->second; QString jid = itr->second;
@ -516,20 +399,21 @@ void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResu
RosterItem* ri = rh->getRosterItem(jid); RosterItem* ri = rh->getRosterItem(jid);
if (ri != nullptr) { if (ri != 0) {
qDebug() << "Flushing messages for" << jid << ", complete:" << complete; qDebug() << "Flushing messages for" << jid << ", complete:" << complete;
ri->flushMessagesToArchive(complete, resultSetReply.first(), resultSetReply.last()); ri->flushMessagesToArchive(complete, resultSetReply.first(), resultSetReply.last());
} }
} }
} }
void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) { void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg)
SHARED_UNUSED(type); {
qDebug() << "MAM MESSAGE LOG::"; qDebug() << "MAM MESSAGE LOG::";
qDebug() << msg; qDebug() << msg;
} }
void Core::Account::onClientError(QXmppClient::Error err) { void Core::Account::onClientError(QXmppClient::Error err)
{
qDebug() << "Error"; qDebug() << "Error";
QString errorText; QString errorText;
QString errorType; QString errorType;
@ -639,18 +523,22 @@ void Core::Account::onClientError(QXmppClient::Error err) {
emit error(errorText); emit error(errorText);
} }
void Core::Account::subscribeToContact(const QString& jid, const QString& reason) { void Core::Account::subscribeToContact(const QString& jid, const QString& reason)
if (state == Shared::ConnectionState::connected) {
if (state == Shared::ConnectionState::connected) {
rm->subscribe(jid, reason); rm->subscribe(jid, reason);
else } else {
qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping"; qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping";
}
} }
void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) { void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason)
if (state == Shared::ConnectionState::connected) {
if (state == Shared::ConnectionState::connected) {
rm->unsubscribe(jid, reason); rm->unsubscribe(jid, reason);
else } else {
qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping"; qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping";
}
} }
void Core::Account::removeContactRequest(const QString& jid) { void Core::Account::removeContactRequest(const QString& jid) {
@ -659,7 +547,8 @@ void Core::Account::removeContactRequest(const QString& jid) {
void Core::Account::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups) { void Core::Account::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups) {
rh->addContactRequest(jid, name, groups);} rh->addContactRequest(jid, name, groups);}
void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) { void Core::Account::setRoomAutoJoin(const QString& jid, bool joined)
{
Conference* conf = rh->getConference(jid); Conference* conf = rh->getConference(jid);
if (conf == 0) { if (conf == 0) {
qDebug() << "An attempt to set auto join to the non existing room" << jid << "of the account" << getName() << ", skipping"; qDebug() << "An attempt to set auto join to the non existing room" << jid << "of the account" << getName() << ", skipping";
@ -669,7 +558,8 @@ void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) {
conf->setAutoJoin(joined); conf->setAutoJoin(joined);
} }
void Core::Account::setRoomJoined(const QString& jid, bool joined) { void Core::Account::setRoomJoined(const QString& jid, bool joined)
{
Conference* conf = rh->getConference(jid); Conference* conf = rh->getConference(jid);
if (conf == 0) { if (conf == 0) {
qDebug() << "An attempt to set joined to the non existing room" << jid << "of the account" << getName() << ", skipping"; qDebug() << "An attempt to set joined to the non existing room" << jid << "of the account" << getName() << ", skipping";
@ -679,51 +569,85 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) {
conf->setJoined(joined); conf->setJoined(joined);
} }
void Core::Account::setContactEncryption(const QString& jid, Shared::EncryptionProtocol value) { void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
Contact* cnt = rh->getContact(jid); {
if (cnt == nullptr) { if (items.from() == getServer()) {
qDebug() << "An attempt to set encryption to the non-existing contact" << jid << "of the account" << getName() << ", skipping"; std::set<QString> needToRequest;
return; qDebug() << "Server items list received for account " << name << ":";
} for (QXmppDiscoveryIq::Item item : items.items()) {
QString jid = item.jid();
if (jid != getServer()) {
qDebug() << " Node" << jid;
needToRequest.insert(jid);
} else {
qDebug() << " " << item.node().toStdString().c_str();
}
}
cnt->setEncryption(value); for (const QString& jid : needToRequest) {
} dm->requestInfo(jid);
}
void Core::Account::discoverInfo(const QString& address, const QString& node) {
if (state == Shared::ConnectionState::connected) {
dm->requestInfo(address, node);
} else {
qDebug() << "An attempt to send a discover info by account" << name <<
"sending request to" << address << "about node" << node <<
"but the account is not in the connected state, skipping";
} }
} }
void Core::Account::setPepSupport(Shared::Support support) { void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
if (support != pepSupport) { {
pepSupport = support; if (info.from() == getServer()) {
emit pepSupportChanged(pepSupport); bool enableCC = false;
qDebug() << "Server info received for account" << name;
QStringList features = info.features();
qDebug() << "List of supported features of the server " << getServer() << ":";
for (const QString& feature : features) {
qDebug() << " " << feature.toStdString().c_str();
if (feature == "urn:xmpp:carbons:2") {
enableCC = true;
}
}
if (enableCC) {
qDebug() << "Enabling carbon copies for account" << name;
cm->setCarbonsEnabled(true);
}
qDebug() << "Requesting account" << name << "capabilities";
dm->requestInfo(getBareJid());
} else if (info.from() == getBareJid()) {
qDebug() << "Received capabilities for account" << name << ":";
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
bool pepSupported = false;
for (const QXmppDiscoveryIq::Identity& identity : identities) {
QString type = identity.type();
qDebug() << " " << identity.category() << type;
if (type == "pep") {
pepSupported = true;
}
}
rh->setPepSupport(pepSupported);
} else {
qDebug() << "Received info for account" << name << "about" << info.from();
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
for (const QXmppDiscoveryIq::Identity& identity : identities) {
qDebug() << " " << identity.name() << identity.category() << identity.type();
}
} }
} }
void Core::Account::handleDisconnection() { void Core::Account::handleDisconnection()
setPepSupport(Shared::Support::unknown); {
delay->disconnected();
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
cm->setCarbonsEnabled(false); cm->setCarbonsEnabled(false);
#endif
rh->handleOffline(); rh->handleOffline();
vh->handleOffline();
archiveQueries.clear(); archiveQueries.clear();
} }
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last) { void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
{
RosterItem* contact = static_cast<RosterItem*>(sender()); RosterItem* contact = static_cast<RosterItem*>(sender());
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
if (last) if (last) {
qDebug() << "The response contains the first accounted message"; qDebug() << "The response contains the first accounted message";
}
emit responseArchive(contact->jid, list, last); emit responseArchive(contact->jid, list, last);
} }
@ -734,7 +658,9 @@ void Core::Account::setActive(bool p_active) {
if (active != p_active) { if (active != p_active) {
active = p_active; active = p_active;
emit changed({{"active", active}}); emit changed({
{"active", active}
});
} }
} }
@ -769,9 +695,7 @@ void Core::Account::setPasswordType(Shared::AccountPassword pt) {
passwordType = pt; } passwordType = pt; }
void Core::Account::setLogin(const QString& p_login) { void Core::Account::setLogin(const QString& p_login) {
config.setUser(p_login); config.setUser(p_login);}
delay->setOwnJid(getBareJid());
}
void Core::Account::setName(const QString& p_name) { void Core::Account::setName(const QString& p_name) {
name = p_name;} name = p_name;}
@ -782,9 +706,7 @@ void Core::Account::setPassword(const QString& p_password) {
} }
void Core::Account::setServer(const QString& p_server) { void Core::Account::setServer(const QString& p_server) {
config.setDomain(p_server); config.setDomain(p_server);}
delay->setOwnJid(getBareJid());
}
void Core::Account::sendMessage(const Shared::Message& data) { void Core::Account::sendMessage(const Shared::Message& data) {
mh->sendMessage(data);} mh->sendMessage(data);}
@ -798,26 +720,11 @@ void Core::Account::resendMessage(const QString& jid, const QString& id) {
void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) {
mh->sendMessage(data, false, originalId);} mh->sendMessage(data, false, originalId);}
void Core::Account::requestInfo(const QString& jid) { void Core::Account::requestVCard(const QString& jid) {
//TODO switch case of what kind of entity this info request is about vh->requestVCard(jid);}
//right now it could be only about myself or some contact
delay->getInfo(jid);
//vh->requestVCard(jid);
}
void Core::Account::updateInfo(const Shared::Info& info) { void Core::Account::uploadVCard(const Shared::VCard& card) {
//TODO switch case of what kind of entity this info update is about vh->uploadVCard(card);}
//right now it could be only about myself
vh->uploadVCard(info.getVCardRef());
const std::list<Shared::KeyInfo>& keys = info.getActiveKeysRef();
for (const Shared::KeyInfo& info : keys) {
qDebug() << "An attempt to save key: ";
qDebug() << "id:" << info.id;
qDebug() << "label:" << info.label;
qDebug() << "current device:" << info.currentDevice;
qDebug() << "... but it's not implemented yet, ignoring";
}
}
QString Core::Account::getAvatarPath() const { QString Core::Account::getAvatarPath() const {
return vh->getAvatarPath();} return vh->getAvatarPath();}
@ -834,12 +741,14 @@ void Core::Account::addContactToGroupRequest(const QString& jid, const QString&
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) {
rh->removeContactFromGroupRequest(jid, groupName);} rh->removeContactFromGroupRequest(jid, groupName);}
void Core::Account::renameContactRequest(const QString& jid, const QString& newName) { void Core::Account::renameContactRequest(const QString& jid, const QString& newName)
{
Contact* cnt = rh->getContact(jid); Contact* cnt = rh->getContact(jid);
if (cnt == 0) if (cnt == 0) {
qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping"; qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping";
else } else {
rm->renameItem(jid, newName); rm->renameItem(jid, newName);
}
} }
void Core::Account::invalidatePassword() { void Core::Account::invalidatePassword() {

View File

@ -15,7 +15,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#ifndef CORE_ACCOUNT_H
#define CORE_ACCOUNT_H
#include <QObject> #include <QObject>
#include <QCryptographicHash> #include <QCryptographicHash>
@ -27,14 +29,9 @@
#include <map> #include <map>
#include <set> #include <set>
#include <list>
#include <QXmppRosterManager.h> #include <QXmppRosterManager.h>
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
#include <QXmppCarbonManagerV2.h>
#else
#include <QXmppCarbonManager.h> #include <QXmppCarbonManager.h>
#endif
#include <QXmppDiscoveryManager.h> #include <QXmppDiscoveryManager.h>
#include <QXmppMamManager.h> #include <QXmppMamManager.h>
#include <QXmppMucManager.h> #include <QXmppMucManager.h>
@ -44,41 +41,25 @@
#include <QXmppUploadRequestManager.h> #include <QXmppUploadRequestManager.h>
#include <QXmppVCardManager.h> #include <QXmppVCardManager.h>
#include <QXmppMessageReceiptManager.h> #include <QXmppMessageReceiptManager.h>
#include <QXmppPubSubManager.h>
#include <shared/shared.h> #include "shared/shared.h"
#include <shared/identity.h>
#include <shared/info.h>
#include <shared/clientid.h>
#include "contact.h" #include "contact.h"
#include "conference.h" #include "conference.h"
#include <core/components/networkaccess.h> #include "networkaccess.h"
#include <core/delayManager/manager.h>
#include "handlers/messagehandler.h" #include "handlers/messagehandler.h"
#include "handlers/rosterhandler.h" #include "handlers/rosterhandler.h"
#include "handlers/vcardhandler.h" #include "handlers/vcardhandler.h"
#include "handlers/discoveryhandler.h"
#ifdef WITH_OMEMO namespace Core
#include <QXmppOmemoManager.h> {
#include <QXmppTrustManager.h>
#include "handlers/trusthandler.h"
#include "handlers/omemohandler.h"
#endif
namespace Core { class Account : public QObject
{
class Account : public QObject {
Q_OBJECT Q_OBJECT
friend class MessageHandler; friend class MessageHandler;
friend class RosterHandler; friend class RosterHandler;
friend class VCardHandler; friend class VCardHandler;
friend class DiscoveryHandler;
#ifdef WITH_OMEMO
friend class OmemoHandler;
friend class TrustHandler;
#endif
public: public:
enum class Error { enum class Error {
authentication, authentication,
@ -128,24 +109,21 @@ public:
void removeContactFromGroupRequest(const QString& jid, const QString& groupName); void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
void renameContactRequest(const QString& jid, const QString& newName); void renameContactRequest(const QString& jid, const QString& newName);
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data); void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
void setContactEncryption(const QString& jid, Shared::EncryptionProtocol value);
void setRoomJoined(const QString& jid, bool joined); void setRoomJoined(const QString& jid, bool joined);
void setRoomAutoJoin(const QString& jid, bool joined); void setRoomAutoJoin(const QString& jid, bool joined);
void removeRoomRequest(const QString& jid); void removeRoomRequest(const QString& jid);
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
void updateInfo(const Shared::Info& info); void uploadVCard(const Shared::VCard& card);
void resendMessage(const QString& jid, const QString& id); void resendMessage(const QString& jid, const QString& id);
void replaceMessage(const QString& originalId, const Shared::Message& data); void replaceMessage(const QString& originalId, const Shared::Message& data);
void invalidatePassword(); void invalidatePassword();
void discoverInfo(const QString& address, const QString& node);
public slots: public slots:
void connect(); void connect();
void disconnect(); void disconnect();
void reconnect(); void reconnect();
void requestInfo(const QString& jid); void requestVCard(const QString& jid);
signals: signals:
void changed(const QMap<QString, QVariant>& data); void changed(const QMap<QString, QVariant>& data);
@ -169,12 +147,10 @@ signals:
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data); void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data); void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
void removeRoomParticipant(const QString& jid, const QString& nickName); void removeRoomParticipant(const QString& jid, const QString& nickName);
void infoReady(const Shared::Info& info); void receivedVCard(const QString& jid, const Shared::VCard& card);
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers); void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
void needPassword(); void needPassword();
void infoDiscovered(const QString& address, const QString& node, const std::set<Shared::Identity>& identities, const std::set<QString>& features);
void pepSupportChanged(Shared::Support support);
private: private:
QString name; QString name;
@ -183,26 +159,7 @@ private:
QXmppConfiguration config; QXmppConfiguration config;
QXmppPresence presence; QXmppPresence presence;
Shared::ConnectionState state; Shared::ConnectionState state;
MessageHandler* mh;
RosterHandler* rh;
VCardHandler* vh;
DiscoveryHandler* dh;
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
TrustHandler* th;
#endif
#ifdef WITH_OMEMO
OmemoHandler* oh;
QXmppOmemoManager* om;
#endif
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
QXmppTrustManager* tm;
QXmppCarbonManagerV2* cm;
QXmppPubSubManager* psm;
#else
QXmppCarbonManager* cm; QXmppCarbonManager* cm;
#endif
QXmppMamManager* am; QXmppMamManager* am;
QXmppMucManager* mm; QXmppMucManager* mm;
QXmppBookmarkManager* bm; QXmppBookmarkManager* bm;
@ -215,13 +172,15 @@ private:
QTimer* reconnectTimer; QTimer* reconnectTimer;
NetworkAccess* network; NetworkAccess* network;
DelayManager::Manager* delay;
Shared::AccountPassword passwordType; Shared::AccountPassword passwordType;
Error lastError; Error lastError;
Shared::Support pepSupport; bool pepSupport;
bool active; bool active;
bool notReadyPassword; bool notReadyPassword;
bool loadingOmemo;
MessageHandler* mh;
RosterHandler* rh;
VCardHandler* vh;
private slots: private slots:
void onClientStateChange(QXmppClient::State state); void onClientStateChange(QXmppClient::State state);
@ -235,12 +194,15 @@ private slots:
void onMamLog(QXmppLogger::MessageType type, const QString &msg); void onMamLog(QXmppLogger::MessageType type, const QString &msg);
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last); void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
private: private:
void handleDisconnection(); void handleDisconnection();
void onReconnectTimer(); void onReconnectTimer();
void setPepSupport(Shared::Support support);
void runDiscoveryService();
}; };
} }
#endif // CORE_ACCOUNT_H

View File

@ -19,8 +19,6 @@
#define CORE_ADAPTER_FUNCTIONS_H #define CORE_ADAPTER_FUNCTIONS_H
#include <QXmppVCardIq.h> #include <QXmppVCardIq.h>
#include <QXmppTask.h>
#include <QXmppPromise.h>
#include <shared/vcard.h> #include <shared/vcard.h>
namespace Core { namespace Core {
@ -28,19 +26,6 @@ namespace Core {
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
template<typename T>
QXmppTask<T> makeReadyTask(T &&value) {
QXmppPromise<T> promise;
promise.finish(std::move(value));
return promise.task();
}
inline QXmppTask<void> makeReadyTask() {
QXmppPromise<void> promise;
promise.finish();
return promise.task();
}
} }

View File

@ -1,18 +0,0 @@
set(SOURCE_FILES
networkaccess.cpp
clientcache.cpp
urlstorage.cpp
archive.cpp
)
set(HEADER_FILES
networkaccess.h
clientcache.h
urlstorage.h
archive.h
)
target_sources(squawk PRIVATE
${SOURCE_FILES}
${HEADER_FILES}
)

View File

@ -1,419 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "archive.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <QDebug>
Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* parent):
QObject(parent),
jid(p_jid),
account(account),
opened(false),
db(account + "/" + jid),
messages(db.addStorage<QString, Shared::Message>("messages")),
order(db.addStorage<uint64_t, QString>("order", true)),
stats(db.addStorage<QString, QVariant>("stats")),
avatars(db.addStorage<QString, AvatarInfo>("avatars")),
stanzaIdToId(db.addStorage<QString, QString>("stanzaIdToId")),
cursor(order->createCursor())
{}
Core::Archive::~Archive() {
close();
}
void Core::Archive::open() {
db.open();
LMDBAL::WriteTransaction txn = db.beginTransaction();
AvatarInfo info;
bool hasAvatar = false;
try {
avatars->getRecord(jid, info, txn);
hasAvatar = true;
} catch (const LMDBAL::NotFound& e) {}
if (!hasAvatar)
return;
QFile ava(db.getPath() + "/" + jid + "." + info.type);
if (ava.exists())
return;
try {
avatars->removeRecord(jid, txn);
txn.commit();
} catch (const std::exception& e) {
qDebug() << e.what();
qDebug() << "error opening archive" << jid << "for account" << account
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
}
}
void Core::Archive::close() {
db.close();
}
bool Core::Archive::addElement(const Shared::Message& message) {
QString id = message.getId();
qDebug() << "Adding message with id " << id;
try {
LMDBAL::WriteTransaction txn = db.beginTransaction();
messages->addRecord(id, message, txn);
order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn);
QString stanzaId = message.getStanzaId();
if (!stanzaId.isEmpty())
stanzaIdToId->addRecord(stanzaId, id, txn);
txn.commit();
return true;
} catch (const std::exception& e) {
qDebug() << "Could not add message with id " + id;
qDebug() << e.what();
}
return false;
}
void Core::Archive::clear() {
db.drop();
}
Shared::Message Core::Archive::getElement(const QString& id) const {
return messages->getRecord(id);
}
bool Core::Archive::hasElement(const QString& id) const {
return messages->checkRecord(id);
}
void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVariant>& data) {
LMDBAL::WriteTransaction txn = db.beginTransaction();
Shared::Message msg = messages->getRecord(id, txn);
bool hadStanzaId = !msg.getStanzaId().isEmpty();
QDateTime oTime = msg.getTime();
bool idChange = msg.change(data);
QString newId = msg.getId();
QDateTime nTime = msg.getTime();
bool orderChange = oTime != nTime;
if (idChange || orderChange) {
if (idChange)
messages->removeRecord(id, txn);
if (orderChange)
order->removeRecord(oTime.toMSecsSinceEpoch(), txn);
order->forceRecord(nTime.toMSecsSinceEpoch(), newId, txn);
}
QString sid = msg.getStanzaId();
if (!sid.isEmpty() && (idChange || !hadStanzaId))
stanzaIdToId->forceRecord(sid, newId, txn);
messages->forceRecord(newId, msg, txn);
txn.commit();
}
Shared::Message Core::Archive::newest() const {
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
try {
cursor.open(txn);
while (true) {
std::pair<uint64_t, QString> pair = cursor.prev();
Shared::Message msg = messages->getRecord(pair.second, txn);
if (msg.serverStored()) {
cursor.close();
return msg;
}
}
} catch (...) {
cursor.close();
throw;
}
}
QString Core::Archive::newestId() const {
Shared::Message msg = newest();
return msg.getId();
}
QString Core::Archive::oldestId() const {
Shared::Message msg = oldest();
return msg.getId();
}
Shared::Message Core::Archive::oldest() const {
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
try {
cursor.open(txn);
while (true) {
std::pair<uint64_t, QString> pair = cursor.next();
Shared::Message msg = messages->getRecord(pair.second, txn);
if (msg.serverStored()) {
cursor.close();
return msg;
}
}
} catch (...) {
cursor.close();
throw;
}
}
unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messages) {
unsigned int success = 0;
LMDBAL::WriteTransaction txn = db.beginTransaction();
for (const Shared::Message& message : messages) {
QString id = message.getId();
bool added = false;
try {
Core::Archive::messages->addRecord(id, message, txn);
added = true;
} catch (const LMDBAL::Exist& e) {}
if (!added)
continue;
order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn);
QString sid = message.getStanzaId();
if (!sid.isEmpty())
stanzaIdToId->addRecord(sid, id, txn);
++success;
}
txn.commit();
return success;
}
long unsigned int Core::Archive::size() const {
return order->count();
}
std::list<Shared::Message> Core::Archive::getBefore(unsigned int count, const QString& id) {
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
std::list<Shared::Message> res;
try {
cursor.open(txn);
if (!id.isEmpty()) {
Shared::Message reference = messages->getRecord(id, txn);
uint64_t stamp = reference.getTime().toMSecsSinceEpoch();
cursor.set(stamp);
}
for (unsigned int i = 0; i < count; ++i) {
std::pair<uint64_t, QString> pair;
cursor.prev(pair.first, pair.second);
res.emplace_back();
Shared::Message& msg = res.back();
messages->getRecord(pair.second, msg, txn);
}
cursor.close();
return res;
} catch (const LMDBAL::NotFound& e) {
cursor.close();
if (res.empty())
throw e;
else
return res;
} catch (...) {
cursor.close();
throw;
}
}
bool Core::Archive::isFromTheBeginning() const {
try {
return stats->getRecord("fromTheBeginning").toBool();
} catch (const LMDBAL::NotFound& e) {
return false;
}
}
void Core::Archive::setFromTheBeginning(bool is) {
stats->forceRecord("fromTheBeginning", is);
}
Shared::EncryptionProtocol Core::Archive::encryption() const {
try {
return stats->getRecord("encryption").value<Shared::EncryptionProtocol>();
} catch (const LMDBAL::NotFound& e) {
return Shared::EncryptionProtocol::none;
}
}
bool Core::Archive::setEncryption(Shared::EncryptionProtocol is) {
LMDBAL::WriteTransaction txn = db.beginTransaction();
Shared::EncryptionProtocol current = Shared::EncryptionProtocol::none;
try {
current = stats->getRecord("encryption", txn).value<Shared::EncryptionProtocol>();
} catch (const LMDBAL::NotFound& e) {}
if (is != current) {
stats->forceRecord("encryption", static_cast<uint8_t>(is), txn);
txn.commit();
return true;
}
return false;
}
QString Core::Archive::idByStanzaId(const QString& stanzaId) const {
return stanzaIdToId->getRecord(stanzaId);
}
QString Core::Archive::stanzaIdById(const QString& id) const {
try {
Shared::Message msg = getElement(id);
return msg.getStanzaId();
} catch (const LMDBAL::NotFound& e) {
return QString();
}
}
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) {
LMDBAL::WriteTransaction txn = db.beginTransaction();
AvatarInfo oldInfo;
bool haveAvatar = false;
QString res = resource.isEmpty() ? jid : resource;
try {
avatars->getRecord(res, oldInfo, txn);
haveAvatar = true;
} catch (const LMDBAL::NotFound& e) {}
if (data.size() == 0) {
if (!haveAvatar)
return false;
avatars->removeRecord(res, txn);
txn.commit();
return true;
}
QString currentPath = db.getPath();
bool needToRemoveOld = false;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(data);
QByteArray newHash(hash.result());
if (haveAvatar) {
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash)
return false;
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type);
if (oldAvatar.exists()) {
if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) {
needToRemoveOld = true;
} else {
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
return false;
}
}
}
QMimeDatabase mimedb;
QMimeType type = mimedb.mimeTypeForData(data);
QString ext = type.preferredSuffix();
QFile newAvatar(currentPath + "/" + res + "." + ext);
if (!newAvatar.open(QFile::WriteOnly)) {
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type);
}
return false;
}
newAvatar.write(data);
newAvatar.close();
newInfo.type = ext;
newInfo.hash = newHash;
newInfo.autogenerated = generated;
try {
avatars->forceRecord(res, newInfo, txn);
txn.commit();
} catch (...) {
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type);
}
return false;
}
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
oldAvatar.remove();
}
return true;
}
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const {
try {
avatars->getRecord(resource.isEmpty() ? jid : resource, target);
return true;
} catch (const LMDBAL::NotFound& e) {
return false;
}
}
void Core::Archive::readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const {
avatars->readAll(data);
}
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const {
return avatars->getRecord(resource);
}
Core::Archive::AvatarInfo::AvatarInfo():
type(),
hash(),
autogenerated(false)
{}
Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated):
type(p_type),
hash(p_hash),
autogenerated(p_autogenerated)
{}
QDataStream & operator<<(QDataStream& out, const Core::Archive::AvatarInfo& info) {
out << info.type;
out << info.hash;
out << info.autogenerated;
return out;
}
QDataStream & operator>>(QDataStream& in, Core::Archive::AvatarInfo& info) {
in >> info.type;
in >> info.hash;
in >> info.autogenerated;
return in;
}

View File

@ -1,101 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QObject>
#include <QCryptographicHash>
#include <QMimeDatabase>
#include <QMimeType>
#include <QDataStream>
#include "shared/enums.h"
#include "shared/message.h"
#include "shared/exception.h"
#include <list>
#include <base.h>
#include <storage.h>
#include <cursor.h>
namespace Core {
class Archive : public QObject {
Q_OBJECT
public:
class AvatarInfo;
Archive(const QString& account, const QString& jid, QObject* parent = 0);
~Archive();
void open();
void close();
bool addElement(const Shared::Message& message);
unsigned int addElements(const std::list<Shared::Message>& messages);
Shared::Message getElement(const QString& id) const;
bool hasElement(const QString& id) const;
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
Shared::Message oldest() const;
QString oldestId() const;
Shared::Message newest() const;
QString newestId() const;
void clear();
long unsigned int size() const;
std::list<Shared::Message> getBefore(unsigned int count, const QString& id);
bool isFromTheBeginning() const;
void setFromTheBeginning(bool is);
Shared::EncryptionProtocol encryption() const;
bool setEncryption(Shared::EncryptionProtocol value); //returns true if changed, false otherwise
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
AvatarInfo getAvatarInfo(const QString& resource = "") const;
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
QString idByStanzaId(const QString& stanzaId) const;
QString stanzaIdById(const QString& id) const;
public:
const QString jid;
const QString account;
public:
class AvatarInfo {
public:
AvatarInfo();
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
QString type;
QByteArray hash;
bool autogenerated;
};
private:
bool opened;
LMDBAL::Base db;
LMDBAL::Storage<QString, Shared::Message>* messages;
LMDBAL::Storage<uint64_t, QString>* order;
LMDBAL::Storage<QString, QVariant>* stats;
LMDBAL::Storage<QString, AvatarInfo>* avatars;
LMDBAL::Storage<QString, QString>* stanzaIdToId;
mutable LMDBAL::Cursor<uint64_t, QString> cursor;
};
}
QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info);
QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info);

View File

@ -1,77 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "clientcache.h"
#include <QDebug>
Core::ClientCache::ClientCache():
db("clients"),
cache(db.addCache<QString, Shared::ClientInfo>("info")),
requested(),
specific()
{
db.open();
}
Core::ClientCache::~ClientCache() {
db.close();
}
void Core::ClientCache::open() {
db.open();}
void Core::ClientCache::close() {
db.close();}
bool Core::ClientCache::checkClient(const Shared::ClientId& p_id) {
QString id = p_id.getId();
if (requested.count(id) == 0 && !cache->checkRecord(id)) {
requested.emplace(id, p_id);
emit requestClientInfo(id);
return false;
}
return true;
}
bool Core::ClientCache::registerClientInfo (
const QString& sourceFullJid,
const QString& id,
const std::set<Shared::Identity>& identities,
const std::set<QString>& features)
{
std::map<QString, Shared::ClientInfo>::iterator itr = requested.find(id);
if (itr != requested.end()) {
Shared::ClientInfo& info = itr->second;
info.identities = identities;
info.extensions = features;
bool valid = info.valid();
if (valid) {
cache->addRecord(id, info);
} else {
info.specificPresence = sourceFullJid;
specific.insert(std::make_pair(sourceFullJid, info));
}
requested.erase(id);
return valid;
} else {
return false;
}
}

View File

@ -1,63 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <map>
#include <set>
#include <QObject>
#include <QString>
#include <cache.h>
#include <shared/clientid.h>
#include <shared/clientinfo.h>
#include <shared/identity.h>
namespace Core {
class ClientCache : public QObject {
Q_OBJECT
public:
ClientCache();
~ClientCache();
void open();
void close();
signals:
void requestClientInfo(const QString& id);
public slots:
bool checkClient(const Shared::ClientId& id);
bool registerClientInfo(
const QString& sourceFullJid,
const QString& id,
const std::set<Shared::Identity>& identities,
const std::set<QString>& features
);
private:
LMDBAL::Base db;
LMDBAL::Cache<QString, Shared::ClientInfo>* cache;
std::map<QString, Shared::ClientInfo> requested;
std::map<QString, Shared::ClientInfo> specific;
};
}

View File

@ -1,252 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include "urlstorage.h"
Core::UrlStorage::UrlStorage(const QString& p_name):
base(p_name),
urlToInfo(base.addStorage<QString, UrlInfo>("urlToInfo")),
pathToUrl(base.addStorage<QString, QString>("pathToUrl"))
{}
Core::UrlStorage::~UrlStorage() {
close();
}
void Core::UrlStorage::open() {
base.open();
}
void Core::UrlStorage::close() {
base.close();
}
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) {
LMDBAL::WriteTransaction txn = base.beginTransaction();
writeInfo(key, info, txn, overwrite);
txn.commit();
}
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite) {
if (overwrite)
urlToInfo->forceRecord(key, info, txn);
else
urlToInfo->addRecord(key, info, txn);
if (info.hasPath())
pathToUrl->forceRecord(info.getPath(), key, txn);
}
void Core::UrlStorage::addFile(const QString& url) {
addToInfo(url, "", "", "");
}
void Core::UrlStorage::addFile(const QString& url, const QString& path) {
addToInfo(url, "", "", "", path);
}
void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) {
addToInfo(url, account, jid, id);
}
void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) {
addToInfo(url, account, jid, id, path);
}
void Core::UrlStorage::addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path) {
UrlInfo info (path, msgs);
writeInfo(url, info, true);
}
QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id){
return addToInfo(url, account, jid, id).getPath();
}
Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(
const QString& url,
const QString& account,
const QString& jid,
const QString& id,
const QString& path
) {
UrlInfo info;
LMDBAL::WriteTransaction txn = base.beginTransaction();
try {
urlToInfo->getRecord(url, info, txn);
} catch (const LMDBAL::NotFound& e) {}
bool pathChange = false;
bool listChange = false;
if (path != "-s") {
if (info.getPath() != path) {
info.setPath(path);
pathChange = true;
}
}
if (account.size() > 0 && jid.size() > 0 && id.size() > 0)
listChange = info.addMessage(account, jid, id);
if (pathChange || listChange) {
writeInfo(url, info, txn, true);
txn.commit();
}
return info;
}
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path) {
std::list<Shared::MessageInfo> list;
LMDBAL::WriteTransaction txn = base.beginTransaction();
UrlInfo info;
try {
urlToInfo->getRecord(url, info, txn);
info.getMessages(list);
} catch (const LMDBAL::NotFound& e) {}
info.setPath(path);
writeInfo(url, info, txn, true);
txn.commit();
return list;
}
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url) {
std::list<Shared::MessageInfo> list;
LMDBAL::WriteTransaction txn = base.beginTransaction();
UrlInfo info;
urlToInfo->getRecord(url, info, txn);
urlToInfo->removeRecord(url, txn);
info.getMessages(list);
if (info.hasPath())
pathToUrl->removeRecord(info.getPath(), txn);
txn.commit();
return list;
}
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path) {
std::list<Shared::MessageInfo> list;
LMDBAL::WriteTransaction txn = base.beginTransaction();
QString url = pathToUrl->getRecord(path, txn);
pathToUrl->removeRecord(path, txn);
UrlInfo info = urlToInfo->getRecord(url, txn);
info.getMessages(list);
info.setPath(QString());
urlToInfo->changeRecord(url, info, txn);
txn.commit();
return list;
}
QString Core::UrlStorage::getUrl(const QString& path) {
return pathToUrl->getRecord(path);
}
std::pair<QString, std::list<Shared::MessageInfo>> Core::UrlStorage::getPath(const QString& url) {
UrlInfo info = urlToInfo->getRecord(url);
std::list<Shared::MessageInfo> container;
info.getMessages(container);
return std::make_pair(info.getPath(), container);
}
Core::UrlStorage::UrlInfo::UrlInfo():
localPath(),
messages() {}
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path):
localPath(path),
messages() {}
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs):
localPath(path),
messages(msgs) {}
Core::UrlStorage::UrlInfo::~UrlInfo() {}
bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) {
for (const Shared::MessageInfo& info : messages) {
if (info.account == acc && info.jid == jid && info.messageId == id)
return false;
}
messages.emplace_back(acc, jid, id);
return true;
}
void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const {
data << localPath;
std::list<Shared::MessageInfo>::size_type size = messages.size();
data << quint32(size);
for (const Shared::MessageInfo& info : messages) {
data << info.account;
data << info.jid;
data << info.messageId;
}
}
QDataStream & operator << (QDataStream& in, const Core::UrlStorage::UrlInfo& info) {
info.serialize(in);
return in;
}
QDataStream & operator >> (QDataStream& out, Core::UrlStorage::UrlInfo& info) {
info.deserialize(out);
return out;
}
void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) {
data >> localPath;
quint32 size;
data >> size;
for (quint32 i = 0; i < size; ++i) {
messages.emplace_back();
Shared::MessageInfo& info = messages.back();
data >> info.account;
data >> info.jid;
data >> info.messageId;
}
}
void Core::UrlStorage::UrlInfo::getMessages(std::list<Shared::MessageInfo>& container) const {
for (const Shared::MessageInfo& info : messages)
container.emplace_back(info);
}
QString Core::UrlStorage::UrlInfo::getPath() const {
return localPath;
}
bool Core::UrlStorage::UrlInfo::hasPath() const {
return localPath.size() > 0;
}
void Core::UrlStorage::UrlInfo::setPath(const QString& path) {
localPath = path;
}

View File

@ -42,48 +42,57 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError); connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError);
room->setNickName(nick); room->setNickName(nick);
if (autoJoin) if (autoJoin) {
room->join(); room->join();
}
archive->readAllResourcesAvatars(exParticipants); archive->readAllResourcesAvatars(exParticipants);
} }
Core::Conference::~Conference(){ Core::Conference::~Conference()
if (joined) {
if (joined) {
room->leave(); room->leave();
}
room->deleteLater(); room->deleteLater();
} }
QString Core::Conference::getNick() const { QString Core::Conference::getNick() const
{
return nick; return nick;
} }
bool Core::Conference::getAutoJoin() const { bool Core::Conference::getAutoJoin()
{
return autoJoin; return autoJoin;
} }
bool Core::Conference::getJoined() const { bool Core::Conference::getJoined() const
{
return joined; return joined;
} }
void Core::Conference::setJoined(bool p_joined) { void Core::Conference::setJoined(bool p_joined)
{
if (joined != p_joined) { if (joined != p_joined) {
if (p_joined) if (p_joined) {
room->join(); room->join();
else } else {
room->leave(); room->leave();
}
} }
} }
void Core::Conference::setAutoJoin(bool p_autoJoin) { void Core::Conference::setAutoJoin(bool p_autoJoin)
{
if (autoJoin != p_autoJoin) { if (autoJoin != p_autoJoin) {
autoJoin = p_autoJoin; autoJoin = p_autoJoin;
emit autoJoinChanged(autoJoin); emit autoJoinChanged(autoJoin);
} }
} }
void Core::Conference::setNick(const QString& p_nick) { void Core::Conference::setNick(const QString& p_nick)
{
if (nick != p_nick) { if (nick != p_nick) {
if (joined) { if (joined) {
room->setNickName(p_nick); room->setNickName(p_nick);
@ -94,99 +103,105 @@ void Core::Conference::setNick(const QString& p_nick) {
} }
} }
void Core::Conference::onRoomJoined() { void Core::Conference::onRoomJoined()
{
joined = true; joined = true;
emit joinedChanged(joined); emit joinedChanged(joined);
} }
void Core::Conference::onRoomLeft() { void Core::Conference::onRoomLeft()
{
joined = false; joined = false;
emit joinedChanged(joined); emit joinedChanged(joined);
} }
void Core::Conference::onRoomNameChanged(const QString& p_name) { void Core::Conference::onRoomNameChanged(const QString& p_name)
{
setName(p_name); setName(p_name);
} }
void Core::Conference::onRoomNickNameChanged(const QString& p_nick) { void Core::Conference::onRoomNickNameChanged(const QString& p_nick)
{
if (p_nick != nick) { if (p_nick != nick) {
nick = p_nick; nick = p_nick;
emit nickChanged(nick); emit nickChanged(nick);
} }
} }
void Core::Conference::onRoomError(const QXmppStanza::Error& err) { void Core::Conference::onRoomError(const QXmppStanza::Error& err)
{
qDebug() << "MUC" << jid << "error:" << err.text(); qDebug() << "MUC" << jid << "error:" << err.text();
} }
void Core::Conference::onRoomParticipantAdded(const QString& p_name) { void Core::Conference::onRoomParticipantAdded(const QString& p_name)
{
QStringList comps = p_name.split("/"); QStringList comps = p_name.split("/");
QString resource = comps.back(); QString resource = comps.back();
QXmppPresence pres = room->participantPresence(p_name); QXmppPresence pres = room->participantPresence(p_name);
QXmppMucItem mi = pres.mucItem(); QXmppMucItem mi = pres.mucItem();
if (resource == jid) if (resource == jid) {
resource = ""; resource = "";
}
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource); std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
bool hasAvatar = itr != exParticipants.end(); bool hasAvatar = itr != exParticipants.end();
if (resource.size() > 0) { if (resource.size() > 0) {
QDateTime lastInteraction = pres.lastUserInteraction(); QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc(); lastInteraction = QDateTime::currentDateTimeUtc();
}
QMap<QString, QVariant> cData = { QMap<QString, QVariant> cData = {
{"lastActivity", lastInteraction}, {"lastActivity", lastInteraction},
{"availability", pres.availableStatusType()}, {"availability", pres.availableStatusType()},
{"status", pres.statusText()}, {"status", pres.statusText()},
{"affiliation", mi.affiliation()}, {"affiliation", mi.affiliation()},
{"role", mi.role()}, {"role", mi.role()}
{"client", QVariant::fromValue(
Shared::ClientId(
pres.capabilityNode(),
pres.capabilityVer().toBase64(),
pres.capabilityHash())
)
}
}; };
careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name);
if (hasAvatar) {
if (itr->second.autogenerated) {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
}
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarPath", "");
requestVCard(p_name);
}
emit addParticipant(resource, cData); emit addParticipant(resource, cData);
if (!hasAvatar) // because this way vCard is already requested, no need to handle possible avatar update
return;
} }
handlePossibleAvatarUpdate(pres, resource, hasAvatar, itr->second);
}
void Core::Conference::handlePossibleAvatarUpdate (
const QXmppPresence& pres,
const QString& resource,
bool hasAvatar,
const Archive::AvatarInfo& info
) {
switch (pres.vCardUpdateType()) { switch (pres.vCardUpdateType()) {
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
break; break;
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
break; break;
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
if (!hasAvatar || !info.autogenerated) if (!hasAvatar || !itr->second.autogenerated) {
setAutoGeneratedAvatar(resource); setAutoGeneratedAvatar(resource);
}
break; }
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load break;
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
if (hasAvatar) { if (hasAvatar) {
if (info.autogenerated || info.hash != pres.photoHash()) if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) {
emit requestVCard(pres.from()); emit requestVCard(p_name);
}
} else { } else {
emit requestVCard(pres.from()); emit requestVCard(p_name);
} }
break; break;
}
} }
} }
void Core::Conference::onRoomParticipantChanged(const QString& p_name) { void Core::Conference::onRoomParticipantChanged(const QString& p_name)
{
QStringList comps = p_name.split("/"); QStringList comps = p_name.split("/");
QString resource = comps.back(); QString resource = comps.back();
QXmppPresence pres = room->participantPresence(p_name); QXmppPresence pres = room->participantPresence(p_name);
@ -194,27 +209,22 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) {
handlePresence(pres); handlePresence(pres);
if (resource != jid) { if (resource != jid) {
QDateTime lastInteraction = pres.lastUserInteraction(); QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc(); lastInteraction = QDateTime::currentDateTimeUtc();
}
emit changeParticipant(resource, { emit changeParticipant(resource, {
{"lastActivity", lastInteraction}, {"lastActivity", lastInteraction},
{"availability", pres.availableStatusType()}, {"availability", pres.availableStatusType()},
{"status", pres.statusText()}, {"status", pres.statusText()},
{"affiliation", mi.affiliation()}, {"affiliation", mi.affiliation()},
{"role", mi.role()}, {"role", mi.role()}
{"client", QVariant::fromValue(
Shared::ClientId(
pres.capabilityNode(),
pres.capabilityVer().toBase64(),
pres.capabilityHash())
)
}
}); });
} }
} }
void Core::Conference::onRoomParticipantRemoved(const QString& p_name) { void Core::Conference::onRoomParticipantRemoved(const QString& p_name)
{
QStringList comps = p_name.split("/"); QStringList comps = p_name.split("/");
QString resource = comps.back(); QString resource = comps.back();
if (resource == jid) { if (resource == jid) {
@ -224,40 +234,69 @@ void Core::Conference::onRoomParticipantRemoved(const QString& p_name) {
} }
} }
QString Core::Conference::getSubject() const { QString Core::Conference::getSubject() const
if (joined) {
if (joined) {
return room->subject(); return room->subject();
else } else {
return ""; return "";
}
} }
void Core::Conference::onRoomSubjectChanged(const QString& p_name) { void Core::Conference::onRoomSubjectChanged(const QString& p_name)
{
emit subjectChanged(p_name); emit subjectChanged(p_name);
} }
void Core::Conference::handlePresence(const QXmppPresence& pres) { void Core::Conference::handlePresence(const QXmppPresence& pres)
{
QString id = pres.from(); QString id = pres.from();
QStringList comps = id.split("/"); QStringList comps = id.split("/");
QString jid = comps.front(); QString jid = comps.front();
QString resource(""); QString resource("");
if (comps.size() > 1) if (comps.size() > 1) {
resource = comps.back(); resource = comps.back();
}
Archive::AvatarInfo info; switch (pres.vCardUpdateType()) {
bool hasAvatar = readAvatarInfo(info, resource); case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
handlePossibleAvatarUpdate(pres, resource, hasAvatar, info); break;
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
break;
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, resource);
if (!hasAvatar || !info.autogenerated) {
setAutoGeneratedAvatar(resource);
}
}
break;
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, resource);
if (hasAvatar) {
if (info.autogenerated || info.hash != pres.photoHash()) {
emit requestVCard(id);
}
} else {
emit requestVCard(id);
}
break;
}
}
} }
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
{
Archive::AvatarInfo newInfo; Archive::AvatarInfo newInfo;
bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource); bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource);
if (result && resource.size() != 0) { if (result && resource.size() != 0) {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource); std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr == exParticipants.end()) if (itr == exParticipants.end()) {
exParticipants.insert(std::make_pair(resource, newInfo)); exParticipants.insert(std::make_pair(resource, newInfo));
else } else {
itr->second = newInfo; itr->second = newInfo;
}
emit changeParticipant(resource, { emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)}, {"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
{"avatarPath", avatarPath(resource) + "." + newInfo.type} {"avatarPath", avatarPath(resource) + "." + newInfo.type}
@ -267,15 +306,17 @@ bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) {
return result; return result;
} }
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
{
bool result = RosterItem::setAvatar(data, info, resource); bool result = RosterItem::setAvatar(data, info, resource);
if (result && resource.size() != 0) { if (result && resource.size() != 0) {
if (data.size() > 0) { if (data.size() > 0) {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource); std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr == exParticipants.end()) if (itr == exParticipants.end()) {
exParticipants.insert(std::make_pair(resource, info)); exParticipants.insert(std::make_pair(resource, info));
else } else {
itr->second = info; itr->second = info;
}
emit changeParticipant(resource, { emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)}, {"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
@ -283,8 +324,9 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in
}); });
} else { } else {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource); std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr != exParticipants.end()) if (itr != exParticipants.end()) {
exParticipants.erase(itr); exParticipants.erase(itr);
}
emit changeParticipant(resource, { emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::empty)}, {"avatarState", static_cast<uint>(Shared::Avatar::empty)},
@ -297,31 +339,25 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in
return result; return result;
} }
void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource)
RosterItem::handleResponseVCard(card, resource, out); {
if (resource.size() > 0) Shared::VCard result = RosterItem::handleResponseVCard(card, resource);
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(out.getAvatarType())},
{"avatarPath", out.getAvatarPath()}
});
}
QMap<QString, QVariant> Core::Conference::getAllAvatars() const { if (resource.size() > 0) {
QMap<QString, QVariant> result; emit changeParticipant(resource, {
for (const std::pair<const QString, Archive::AvatarInfo>& pair : exParticipants) {"avatarState", static_cast<uint>(result.getAvatarType())},
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); {"avatarPath", result.getAvatarPath()}
});
}
return result; return result;
} }
QMap<QString, QVariant> Core::Conference::getInfo() const { QMap<QString, QVariant> Core::Conference::getAllAvatars() const
QMap<QString, QVariant> data = RosterItem::getInfo(); {
QMap<QString, QVariant> result;
data.insert("autoJoin", getAutoJoin()); for (const std::pair<const QString, Archive::AvatarInfo>& pair : exParticipants) {
data.insert("joined", getJoined()); result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
data.insert("nick", getNick()); }
data.insert("avatars", getAllAvatars()); return result;
return data;
} }

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_CONFERENCE_H
#define CORE_CONFERENCE_H
#include <QDir> #include <QDir>
@ -25,11 +26,16 @@
#include <set> #include <set>
#include "rosteritem.h" #include "rosteritem.h"
#include <shared/global.h> #include "shared/global.h"
#include <shared/clientid.h>
namespace Core { namespace Core
class Conference : public RosterItem { {
/**
* @todo write docs
*/
class Conference : public RosterItem
{
Q_OBJECT Q_OBJECT
public: public:
Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room); Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room);
@ -42,13 +48,12 @@ public:
bool getJoined() const; bool getJoined() const;
void setJoined(bool p_joined); void setJoined(bool p_joined);
bool getAutoJoin() const; bool getAutoJoin();
void setAutoJoin(bool p_autoJoin); void setAutoJoin(bool p_autoJoin);
void handlePresence(const QXmppPresence & pres) override; void handlePresence(const QXmppPresence & pres) override;
bool setAutoGeneratedAvatar(const QString& resource = "") override; bool setAutoGeneratedAvatar(const QString& resource = "") override;
void handleResponseVCard(const QXmppVCardIq & card, const QString &resource, Shared::VCard& out) override; Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
QMap<QString, QVariant> getAllAvatars() const; QMap<QString, QVariant> getAllAvatars() const;
QMap<QString, QVariant> getInfo() const override;
signals: signals:
void nickChanged(const QString& nick); void nickChanged(const QString& nick);
@ -62,14 +67,6 @@ signals:
protected: protected:
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override; bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
private:
void handlePossibleAvatarUpdate(
const QXmppPresence& pres,
const QString& resource,
bool hasAvatar,
const Archive::AvatarInfo& info
);
private: private:
QString nick; QString nick;
QXmppMucRoom* room; QXmppMucRoom* room;
@ -92,3 +89,5 @@ private slots:
}; };
} }
#endif // CORE_CONFERENCE_H

View File

@ -22,49 +22,55 @@
Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent): Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent):
RosterItem(pJid, account, parent), RosterItem(pJid, account, parent),
groups(), groups(),
subscriptionState(Shared::SubscriptionState::unknown), subscriptionState(Shared::SubscriptionState::unknown)
pep(Shared::Support::unknown) {
}
#ifdef WITH_OMEMO Core::Contact::~Contact()
,omemoBundles(Shared::Possible::unknown) {
#endif }
{}
Core::Contact::~Contact() {} QSet<QString> Core::Contact::getGroups() const
{
QSet<QString> Core::Contact::getGroups() const {
return groups; return groups;
} }
unsigned int Core::Contact::groupsCount() const { unsigned int Core::Contact::groupsCount() const
{
return groups.size(); return groups.size();
} }
void Core::Contact::setGroups(const QSet<QString>& set) { void Core::Contact::setGroups(const QSet<QString>& set)
{
QSet<QString> toRemove = groups - set; QSet<QString> toRemove = groups - set;
QSet<QString> toAdd = set - groups; QSet<QString> toAdd = set - groups;
groups = set; groups = set;
for (const QString& group : toRemove) for (QSet<QString>::iterator itr = toRemove.begin(), end = toRemove.end(); itr != end; ++itr) {
emit groupRemoved(group); emit groupRemoved(*itr);
}
for (const QString& group : toAdd) for (QSet<QString>::iterator itr = toAdd.begin(), end = toAdd.end(); itr != end; ++itr) {
emit groupAdded(group); emit groupAdded(*itr);
}
} }
Shared::SubscriptionState Core::Contact::getSubscriptionState() const { Shared::SubscriptionState Core::Contact::getSubscriptionState() const
{
return subscriptionState; return subscriptionState;
} }
void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) { void Core::Contact::setSubscriptionState(Shared::SubscriptionState state)
{
if (subscriptionState != state) { if (subscriptionState != state) {
subscriptionState = state; subscriptionState = state;
emit subscriptionStateChanged(subscriptionState); emit subscriptionStateChanged(subscriptionState);
} }
} }
void Core::Contact::handlePresence(const QXmppPresence& pres) { void Core::Contact::handlePresence(const QXmppPresence& pres)
{
switch (pres.vCardUpdateType()) { switch (pres.vCardUpdateType()) {
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
break; break;
@ -73,17 +79,18 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) {
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
Archive::AvatarInfo info; Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info); bool hasAvatar = readAvatarInfo(info);
if (!hasAvatar || !info.autogenerated) if (!hasAvatar || !info.autogenerated) {
setAutoGeneratedAvatar(); setAutoGeneratedAvatar();
}
} }
break; break;
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
Archive::AvatarInfo info; Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info); bool hasAvatar = readAvatarInfo(info);
if (hasAvatar) { if (hasAvatar) {
if (info.autogenerated || info.hash != pres.photoHash()) if (info.autogenerated || info.hash != pres.photoHash()) {
emit requestVCard(jid); emit requestVCard(jid);
}
} else { } else {
emit requestVCard(jid); emit requestVCard(jid);
} }
@ -91,23 +98,3 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) {
} }
} }
} }
void Core::Contact::setPepSupport(Shared::Support support) {
if (pep != support)
pep = support;
}
Shared::Support Core::Contact::getPepSupport() const {
return pep;
}
QMap<QString, QVariant> Core::Contact::getInfo() const {
QMap<QString, QVariant> data = RosterItem::getInfo();
data.insert("state", QVariant::fromValue(subscriptionState));
return data;
}

View File

@ -16,18 +16,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_CONTACT_H
#define CORE_CONTACT_H
#include <QObject> #include <QObject>
#include <QSet> #include <QSet>
#include "rosteritem.h" #include "rosteritem.h"
#include <shared/enums.h>
namespace Core { namespace Core {
class Contact : public RosterItem { class Contact : public RosterItem
{
Q_OBJECT Q_OBJECT
public: public:
Contact(const QString& pJid, const QString& account, QObject* parent = 0); Contact(const QString& pJid, const QString& account, QObject* parent = 0);
@ -39,11 +38,7 @@ public:
void setSubscriptionState(Shared::SubscriptionState state); void setSubscriptionState(Shared::SubscriptionState state);
Shared::SubscriptionState getSubscriptionState() const; Shared::SubscriptionState getSubscriptionState() const;
void setPepSupport(Shared::Support support);
Shared::Support getPepSupport() const;
void handlePresence(const QXmppPresence & pres) override; void handlePresence(const QXmppPresence & pres) override;
QMap<QString, QVariant> getInfo() const override;
signals: signals:
void groupAdded(const QString& name); void groupAdded(const QString& name);
@ -53,11 +48,7 @@ signals:
private: private:
QSet<QString> groups; QSet<QString> groups;
Shared::SubscriptionState subscriptionState; Shared::SubscriptionState subscriptionState;
Shared::Support pep;
#ifdef WITH_OMEMO
public:
Shared::Possible omemoBundles;
#endif
}; };
} }
#endif // CORE_CONTACT_H

View File

@ -1,26 +0,0 @@
set(SOURCE_FILES
manager.cpp
job.cpp
cardinternal.cpp
infoforuser.cpp
owncardinternal.cpp
owninfoforuser.cpp
contact.cpp
info.cpp
)
set(HEADER_FILES
manager.h
job.h
cardinternal.h
infoforuser.h
owncardinternal.h
owninfoforuser.h
contact.h
info.h
)
target_sources(squawk PRIVATE
${SOURCE_FILES}
${HEADER_FILES}
)

View File

@ -1,30 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cardinternal.h"
Core::DelayManager::CardInternal::CardInternal(Id p_id, const QString& p_jid) :
Job(p_id, Type::cardInternal),
Contact(p_id, p_jid, Type::cardInternal)
{}
Core::DelayManager::CardInternal::CardInternal(const CardInternal& other) :
Job(other),
Contact(other)
{}

View File

@ -1,35 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QString>
#include "contact.h"
namespace Core {
namespace DelayManager {
class CardInternal : public Contact {
public:
CardInternal(Id id, const QString& jid);
CardInternal(const CardInternal& other);
};
}
}

View File

@ -1,28 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "contact.h"
Core::DelayManager::Contact::Contact(const Contact& other):
Job(other),
jid(other.jid) {}
Core::DelayManager::Contact::Contact(Id p_id, const QString& p_jid, Type p_type):
Job(p_id, p_type),
jid(p_jid) {}

View File

@ -1,58 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "info.h"
Core::DelayManager::Info::Info(Id p_id, Type p_type) :
Job(p_id, p_type),
stage(Stage::waitingForVCard),
info(nullptr)
{}
Core::DelayManager::Info::Info(const Info& other) :
Job(other),
stage(other.stage),
info(nullptr)
{}
Core::DelayManager::Info::~Info() {
if (stage == Stage::waitingForBundles) {
delete info;
}
}
Core::DelayManager::Info::Stage Core::DelayManager::Info::getStage() const {
return stage;
}
void Core::DelayManager::Info::receivedVCard(const Shared::VCard& card) {
if (stage != Stage::waitingForVCard)
throw 245;
info = new Shared::VCard(card);
stage = Stage::waitingForBundles;
}
Shared::VCard * Core::DelayManager::Info::claim() {
if (stage != Stage::waitingForBundles)
throw 246;
Shared::VCard* res = info;
info = nullptr;
return res;
}

View File

@ -1,55 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "job.h"
#include <shared/vcard.h>
#include <shared/info.h>
namespace Core {
namespace DelayManager {
class Info : public virtual Job {
public:
enum class Stage {
waitingForVCard,
waitingForBundles,
finished
};
protected:
Info(Id id, Type type);
Info(const Info& other);
public:
~Info();
void receivedVCard(const Shared::VCard& card);
Shared::VCard* claim();
Stage getStage() const;
private:
Stage stage;
Shared::VCard* info;
};
}
}

View File

@ -1,31 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "infoforuser.h"
Core::DelayManager::InfoForUser::InfoForUser(Id p_id, const QString& p_jid) :
Job(p_id, Type::infoForUser),
Contact(p_id, p_jid, Type::infoForUser),
Info(p_id, Type::infoForUser)
{}
Core::DelayManager::InfoForUser::InfoForUser(const InfoForUser& other) :
Job(other),
Contact(other),
Info(other)
{}

View File

@ -1,34 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "contact.h"
#include "info.h"
namespace Core {
namespace DelayManager {
class InfoForUser : public Contact, public Info {
public:
InfoForUser(Id id, const QString& jid);
InfoForUser(const InfoForUser& other);
};
}
}

View File

@ -1,30 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "job.h"
Core::DelayManager::Job::Job(Id p_id, Type p_type) :
id (p_id),
type (p_type) {}
Core::DelayManager::Job::Job(const Job& other) :
id(other.id),
type(other.type) {}
Core::DelayManager::Job::~Job() {}

View File

@ -1,54 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
namespace Core {
namespace DelayManager {
class Job {
public:
typedef uint16_t Id;
enum class Type {
cardInternal,
ownCardInternal,
infoForUser,
ownInfoForUser
};
inline static constexpr const char * const TypeString[] = {
"cardInternal",
"ownCardInternal",
"infoForUser",
"ownInfoForUser"
};
protected:
Job(Id id, Type type);
Job(const Job& other);
public:
virtual ~Job();
const Id id;
const Type type;
};
}
}

View File

@ -1,435 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "manager.h"
#include <QDebug>
#include "cardinternal.h"
#include "infoforuser.h"
#include "owncardinternal.h"
#include "owninfoforuser.h"
Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* parent) :
QObject(parent),
maxParallelJobs(mpj),
nextJobId(1),
scheduledJobs(),
scheduledJobsById(scheduledJobs.get<id>()),
jobSequence(scheduledJobs.get<sequence>()),
runningJobs(),
ownVCardJobId(0),
ownInfoJobId(0),
scheduledVCards(),
requestedVCards(),
#ifdef WITH_OMEMO
requestedBundles(),
#endif
ownJid(poj)
{}
Core::DelayManager::Manager::~Manager() {
for (const std::pair<const Job::Id, Job*>& pair : runningJobs)
delete pair.second;
for (Job* job : jobSequence)
delete job;
}
Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() {
Job::Id id = nextJobId++;
if (id == 0)
id = nextJobId++;
return id;
}
void Core::DelayManager::Manager::getInfo(const QString& jid) {
if (jid == ownJid)
return getOwnInfo();
Job* job = nullptr;
#ifdef WITH_OMEMO
std::map<QString, Job::Id>::const_iterator bitr = requestedBundles.find(jid);
if (bitr != requestedBundles.end()) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(bitr->second);
if (itr == runningJobs.end())
throw JobNotFound(bitr->second);
job = itr->second;
}
else
#endif
job = getVCardJob(jid);
if (job != nullptr) {
if (job->type == Job::Type::cardInternal)
replaceJob(new InfoForUser(job->id, jid));
} else
scheduleJob(new InfoForUser(getNextJobId(), jid));
}
void Core::DelayManager::Manager::getOwnInfo() {
if (ownInfoJobId == 0) {
if (ownVCardJobId != 0)
replaceJob(new OwnInfoForUser(ownVCardJobId));
else
scheduleJob(new OwnInfoForUser(getNextJobId()));
}
}
void Core::DelayManager::Manager::getVCard(const QString& jid) {
Job* job = getVCardJob(jid);
if (job == nullptr)
scheduleJob(new CardInternal(getNextJobId(), jid));
}
void Core::DelayManager::Manager::getOwnVCard() {
if (ownVCardJobId == 0)
scheduleJob(new OwnCardInternal(getNextJobId()));
}
bool Core::DelayManager::Manager::isOwnVCardPending() const {
return ownVCardJobId != 0;
}
Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) {
Job* job = nullptr;
std::map<QString, Job::Id>::const_iterator sitr = scheduledVCards.find(jid);
if (sitr == scheduledVCards.end()) {
std::map<QString, Job::Id>::const_iterator ritr = requestedVCards.find(jid);
if (ritr != requestedVCards.end()) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(ritr->second);
if (itr == runningJobs.end())
throw JobNotFound(ritr->second, "getVCardJob:1");
job = itr->second;
}
} else {
StorageById::const_iterator itr = scheduledJobsById.find(sitr->second);
if (itr == scheduledJobsById.end())
throw JobNotFound(sitr->second, "getVCardJob:2");
job = *itr;
}
return job;
}
void Core::DelayManager::Manager::preScheduleJob(Job* job) {
switch (job->type) {
case Job::Type::cardInternal:
scheduledVCards.emplace(dynamic_cast<CardInternal*>(job)->jid, job->id);
break;
case Job::Type::ownCardInternal:
ownVCardJobId = job->id;
break;
case Job::Type::infoForUser:
scheduledVCards.emplace(dynamic_cast<InfoForUser*>(job)->jid, job->id);
break;
case Job::Type::ownInfoForUser:
ownVCardJobId = job->id;
ownInfoJobId = job->id;
break;
}
}
void Core::DelayManager::Manager::scheduleJob(Job* job) {
preScheduleJob(job);
if (runningJobs.size() < maxParallelJobs)
executeJob(job);
else
scheduledJobs.push_back(job);
}
void Core::DelayManager::Manager::preExecuteJob(Job* job) {
switch (job->type) {
case Job::Type::cardInternal:
case Job::Type::infoForUser: {
Contact* cij = dynamic_cast<Contact*>(job);
requestedVCards.emplace(cij->jid, job->id);
scheduledVCards.erase(cij->jid);
}
break;
case Job::Type::ownInfoForUser:
case Job::Type::ownCardInternal:
break;
}
}
void Core::DelayManager::Manager::executeJob(Job* job) {
preExecuteJob(job);
runningJobs.emplace(job->id, job);
switch (job->type) {
case Job::Type::cardInternal:
case Job::Type::infoForUser:
emit requestVCard(dynamic_cast<Contact*>(job)->jid);
break;
case Job::Type::ownInfoForUser:
case Job::Type::ownCardInternal:
emit requestOwnVCard();
break;
}
}
void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end())
throw JobNotFound(jobId, "jobIsDone");
Job* job = itr->second;
delete job;
runningJobs.erase(itr);
if (scheduledJobs.size() > 0) {
Job* job = scheduledJobs.front();
scheduledJobs.pop_front();
executeJob(job);
}
}
void Core::DelayManager::Manager::replaceJob(Job* job) {
preScheduleJob(job);
std::map<Job::Id, Job*>::iterator itr = runningJobs.find(job->id);
if (itr != runningJobs.end()) {
preExecuteJob(job);
delete itr->second;
itr->second = job;
} else {
StorageById::iterator sitr = scheduledJobsById.find(job->id);
if (sitr != scheduledJobsById.end()) {
delete *(sitr);
scheduledJobsById.replace(sitr, job);
} else
throw JobNotFound(job->id, "replaceJob");
}
}
void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) {
switch (job->type) {
case Job::Type::cardInternal: {
CardInternal* jb = dynamic_cast<CardInternal*>(job);
if (wasRunning)
requestedVCards.erase(jb->jid);
else
scheduledVCards.erase(jb->jid);
emit gotVCard(jb->jid, Shared::VCard());
}
break;
case Job::Type::infoForUser: {
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
switch (jb->getStage()) {
case InfoForUser::Stage::waitingForVCard:
if (wasRunning)
requestedVCards.erase(jb->jid);
else
scheduledVCards.erase(jb->jid);
emit gotVCard(jb->jid, Shared::VCard());
break;
case InfoForUser::Stage::waitingForBundles:
requestedBundles.erase(jb->jid);
break;
default:
break;
}
emit gotInfo(Shared::Info(jb->jid));
}
break;
case Job::Type::ownInfoForUser: {
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) {
ownVCardJobId = 0;
emit gotOwnVCard(Shared::VCard());
}
ownInfoJobId = 0;
emit gotOwnInfo(Shared::Info (ownJid));
}
break;
case Job::Type::ownCardInternal:
ownVCardJobId = 0;
emit gotOwnVCard(Shared::VCard());
break;
}
delete job;
}
void Core::DelayManager::Manager::disconnected() {
for (const std::pair<const Job::Id, Job*> pair : runningJobs)
jobIsCanceled(pair.second, true);
for (Job* job : scheduledJobs)
jobIsCanceled(job, false);
runningJobs.clear();
scheduledJobs.clear();
}
void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) {
std::map<QString, Job::Id>::const_iterator cardItr = requestedVCards.find(jid);
if (cardItr == requestedVCards.end()) {
qDebug() << "received VCard for" << jid << "but it was never requested through manager, ignoring";
return;
}
Job::Id jobId = cardItr->second;
requestedVCards.erase(cardItr);
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end())
throw JobNotFound(jobId, "receivedVCard");
Job* job = itr->second;
switch (job->type) {
case Job::Type::cardInternal:
jobIsDone(jobId);
emit gotVCard(jid, card);
break;
case Job::Type::infoForUser: {
#ifdef WITH_OMEMO
requestedBundles.emplace(jid, jobId);
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
jb->receivedVCard(card);
emit requestBundles(jid);
#else
Shared::Info info(jid);
info.turnIntoContact(card);
emit gotInfo(info);
jobIsDone(jobId);
#endif
emit gotVCard(jid, card);
}
break;
default:
throw UnexpectedJobType(job->type, "receivedVCard");
}
}
void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) {
if (ownVCardJobId == 0) {
qDebug() << "received own VCard for" << ownJid << "but it was never requested through manager, ignoring";
return;
}
Job::Id jobId = ownVCardJobId;
ownVCardJobId = 0;
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end())
throw JobNotFound(jobId, "receivedOwnVCard");
Job* job = itr->second;
switch (job->type) {
case Job::Type::ownCardInternal:
jobIsDone(jobId);
emit gotOwnVCard(card);
break;
case Job::Type::ownInfoForUser: {
#ifdef WITH_OMEMO
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
jb->receivedVCard(card);
emit requestOwnBundles();
#else
Shared::Info info(ownJid);
info.turnIntoOwnAccount(card);
emit gotOwnInfo(info);
jobIsDone(jobId);
#endif
emit gotOwnVCard(card);
}
break;
default:
throw UnexpectedJobType(job->type, "receivedVCard");
}
}
void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list<Shared::KeyInfo>& keys) {
std::map<QString, Job::Id>::const_iterator itr = requestedBundles.find(jid);
if (itr == requestedBundles.end()) {
qDebug() << "received bundles for" << jid << "but they were never requested through manager, ignoring";
return;
}
Job::Id jobId = itr->second;
requestedBundles.erase(itr);
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
if (jitr == runningJobs.end())
throw JobNotFound(jobId, "receivedBundles");
Job* jb = jitr->second;
InfoForUser* job = dynamic_cast<InfoForUser*>(jb);
Shared::Info info(jid);
info.turnIntoContact(job->claim(), new std::list<Shared::KeyInfo>(keys));
emit gotInfo(info);
jobIsDone(jobId);
}
void Core::DelayManager::Manager::receivedOwnBundles(const std::list<Shared::KeyInfo>& keys) {
if (ownInfoJobId == 0) {
qDebug() << "received own bundles for" << ownJid << "but they were never requested through manager, ignoring";
return;
}
Job::Id jobId = ownInfoJobId;
ownInfoJobId = 0;
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
if (jitr == runningJobs.end())
throw JobNotFound(jobId, "receivedOwnBundles");
Job* jb = jitr->second;
OwnInfoForUser* job = dynamic_cast<OwnInfoForUser*>(jb);
Shared::Info info(ownJid);
info.turnIntoOwnAccount(job->claim(), new std::list<Shared::KeyInfo>(keys));
emit gotOwnInfo(info);
jobIsDone(jobId);
}
void Core::DelayManager::Manager::setOwnJid(const QString& jid) {
ownJid = jid;
}
Core::DelayManager::Manager::UnexpectedJobType::UnexpectedJobType(Job::Type p_type, const std::string& p_method):
Exception(),
type(p_type),
method(p_method)
{}
std::string Core::DelayManager::Manager::UnexpectedJobType::getMessage() const{
std::string msg("Unexpected job type: ");
msg += Job::TypeString[static_cast<int>(type)];
if (method.size() > 0)
msg += " in method " + method;
return msg;
}
Core::DelayManager::Manager::JobNotFound::JobNotFound(Job::Id p_id, const std::string& p_method) :
Exception(),
id(p_id),
method(p_method)
{}
std::string Core::DelayManager::Manager::JobNotFound::getMessage() const {
std::string msg("Job with id ");
msg += std::to_string(id);
msg += " was not found";
if (method.size() > 0)
msg += " in method " + method;
return msg;
}

View File

@ -1,147 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <list>
#include <set>
#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/member.hpp>
#include <QObject>
#include <QString>
#include <shared/vcard.h>
#include <shared/info.h>
#include <shared/exception.h>
#include "job.h"
namespace Core {
namespace DelayManager {
class Manager : public QObject {
Q_OBJECT
public:
Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr);
~Manager();
void setOwnJid(const QString& jid);
bool isOwnVCardPending() const;
public slots:
void getOwnVCard();
void getOwnInfo();
void getVCard(const QString& jid);
void getInfo(const QString& jid);
signals:
void requestVCard(const QString& jid);
void requestOwnVCard();
void requestBundles(const QString& jid);
void requestOwnBundles();
void gotVCard(const QString& jid, const Shared::VCard& info);
void gotOwnVCard(const Shared::VCard& info);
void gotInfo(const Shared::Info& info);
void gotOwnInfo(const Shared::Info& info);
public slots:
void disconnected();
void receivedOwnVCard(const Shared::VCard& card);
void receivedVCard(const QString& jid, const Shared::VCard& card);
void receivedBundles(const QString& jid, const std::list<Shared::KeyInfo>& keys);
void receivedOwnBundles(const std::list<Shared::KeyInfo>& keys);
private:
void preScheduleJob(Job* job);
void scheduleJob(Job* job);
void preExecuteJob(Job* job);
void executeJob(Job* job);
void jobIsCanceled(Job* job, bool wasRunning);
void jobIsDone(Job::Id jobId);
Job::Id getNextJobId();
void replaceJob(Job* job);
Job* getVCardJob(const QString& jid);
private:
struct id {};
struct sequence {};
typedef boost::multi_index_container<
Job*,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<
boost::multi_index::tag<sequence>
>,
boost::multi_index::ordered_unique<
boost::multi_index::tag<id>,
boost::multi_index::member<
Job,
const Job::Id,
&Job::id
>
>
>
> Storage;
typedef Storage::index<id>::type StorageById;
typedef Storage::index<sequence>::type StorageSequence;
Job::Id maxParallelJobs;
Job::Id nextJobId;
Storage scheduledJobs;
StorageById& scheduledJobsById;
StorageSequence& jobSequence;
std::map<Job::Id, Job*> runningJobs;
Job::Id ownVCardJobId;
Job::Id ownInfoJobId;
std::map<QString, Job::Id> scheduledVCards;
std::map<QString, Job::Id> requestedVCards;
#ifdef WITH_OMEMO
std::map<QString, Job::Id> requestedBundles;
#endif
QString ownJid;
public:
class UnexpectedJobType: public Utils::Exception {
public:
UnexpectedJobType(Job::Type p_type, const std::string& p_method = "");
std::string getMessage() const override;
private:
Job::Type type;
std::string method;
};
class JobNotFound: public Utils::Exception {
public:
JobNotFound(Job::Id p_id, const std::string& p_method = "");
std::string getMessage() const override;
private:
Job::Id id;
std::string method;
};
};
}
}

View File

@ -1,32 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "owncardinternal.h"
Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id) :
Job(p_id, Type::ownCardInternal)
{}
Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id, Type p_type) :
Job(p_id, p_type)
{}
Core::DelayManager::OwnCardInternal::OwnCardInternal(const OwnCardInternal& other) :
Job(other)
{}

View File

@ -1,36 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "job.h"
namespace Core {
namespace DelayManager {
class OwnCardInternal : public Job {
protected:
OwnCardInternal(Id id, Type type);
public:
OwnCardInternal(Id id);
OwnCardInternal(const OwnCardInternal& other);
};
}
}

View File

@ -1,29 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "owninfoforuser.h"
Core::DelayManager::OwnInfoForUser::OwnInfoForUser(Id p_id) :
Job(p_id, Type::ownInfoForUser),
Info(p_id, Type::ownInfoForUser)
{}
Core::DelayManager::OwnInfoForUser::OwnInfoForUser(const OwnInfoForUser& other) :
Job(other),
Info(other)
{}

View File

@ -1,34 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "contact.h"
#include "info.h"
namespace Core {
namespace DelayManager {
class OwnInfoForUser : public Info {
public:
OwnInfoForUser(Id id);
OwnInfoForUser(const OwnInfoForUser& other);
};
}
}

View File

@ -1,22 +1,8 @@
set(SOURCE_FILES
messagehandler.cpp
rosterhandler.cpp
vcardhandler.cpp
discoveryhandler.cpp
omemohandler.cpp
trusthandler.cpp
)
set(HEADER_FILES
messagehandler.h
rosterhandler.h
vcardhandler.h
discoveryhandler.h
omemohandler.h
trusthandler.h
)
target_sources(squawk PRIVATE target_sources(squawk PRIVATE
${SOURCE_FILES} messagehandler.cpp
${HEADER_FILES} messagehandler.h
) rosterhandler.cpp
rosterhandler.h
vcardhandler.cpp
vcardhandler.h
)

View File

@ -1,155 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "discoveryhandler.h"
#include "core/account.h"
#include <QDebug>
Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account):
QObject(),
acc(account),
omemoToCarbonsConnected (false) {}
Core::DiscoveryHandler::~DiscoveryHandler() {}
void Core::DiscoveryHandler::initialize()
{
QObject::connect(acc->dm, &QXmppDiscoveryManager::itemsReceived, this, &DiscoveryHandler::onItemsReceived);
QObject::connect(acc->dm, &QXmppDiscoveryManager::infoReceived, this, &DiscoveryHandler::onInfoReceived);
acc->dm->setClientType("pc");
acc->dm->setClientCategory("client");
acc->dm->setClientName(qApp->applicationDisplayName() + " " + qApp->applicationVersion());
acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk");
}
void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items)
{
QString server = acc->getServer();
if (items.from() == server) {
std::set<QString> needToRequest;
qDebug() << "Server items list received for account " << acc->getName() << ":";
for (QXmppDiscoveryIq::Item item : items.items()) {
QString jid = item.jid();
if (jid != server) {
qDebug() << " Node" << jid;
needToRequest.insert(jid);
} else {
qDebug() << " " << item.node().toStdString().c_str();
}
}
for (const QString& jid : needToRequest) {
acc->dm->requestInfo(jid);
}
}
}
void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info)
{
QString from = info.from();
QString server = acc->getServer();
QString accName = acc->getName();
QString bareJid = acc->getBareJid();
if (from == server) {
bool enableCC = false;
qDebug() << "Server info received for account" << accName;
QStringList features = info.features();
qDebug() << "List of supported features of the server " << server << ":";
for (const QString& feature : features) {
qDebug() << " " << feature.toStdString().c_str();
if (feature == "urn:xmpp:carbons:2") {
enableCC = true;
}
}
if (enableCC) {
qDebug() << "Enabling carbon copies for account" << accName;
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
acc->cm->setCarbonsEnabled(true);
#endif
#ifdef WITH_OMEMO
if (!omemoToCarbonsConnected && acc->oh->hasOwnDevice()) {
// connect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage);
// connect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage);
omemoToCarbonsConnected = true;
}
} else {
if (omemoToCarbonsConnected) {
// disconnect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage);
// disconnect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage);
omemoToCarbonsConnected = false;
}
#endif
}
qDebug() << "Requesting account" << accName << "capabilities";
acc->dm->requestInfo(bareJid);
} else if (from == bareJid) {
qDebug() << "Received capabilities for account" << accName << ":";
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
bool pepSupported = false;
for (const QXmppDiscoveryIq::Identity& identity : identities) {
QString type = identity.type();
QString category = identity.category();
qDebug() << " " << category << type;
if (type == "pep" && category == "pubsub") {
pepSupported = true;
}
}
acc->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported);
} else {
QString node = info.queryNode();
if (!node.isEmpty()) {
qDebug() << "Received features and identities for account" << accName << "about" << from;
QStringList feats = info.features();
std::set<Shared::Identity> identities;
std::set<QString> features(feats.begin(), feats.end());
QList<QXmppDiscoveryIq::Identity> idents = info.identities();
for (const QXmppDiscoveryIq::Identity& ident : idents) {
Shared::Identity identity;
identity.category = ident.category();
identity.language = ident.language();
identity.name = ident.name();
identity.type = ident.type();
identities.insert(identity);
qDebug() << " " << identity.name << identity.category << identity.type;
}
for (const QString& feat : features) {
qDebug() << " " << feat;
}
emit acc->infoDiscovered(from, node, identities, features);
} else {
Contact* cont = acc->rh->getContact(from);
if (cont != nullptr) {
qDebug() << "Received info for account" << accName << "about contact" << from;
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
bool pepSupported = false;
for (const QXmppDiscoveryIq::Identity& identity : identities) {
QString type = identity.type();
QString category = identity.category();
qDebug() << " " << category << type;
if (type == "pep" && category == "pubsub") {
pepSupported = true;
}
}
cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported);
}
}
}
}

View File

@ -1,48 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CORE_DISCOVERYHANDLER_H
#define CORE_DISCOVERYHANDLER_H
#include <QObject>
#include <QXmppDiscoveryManager.h>
#include <QXmppDiscoveryIq.h>
namespace Core {
class Account;
class DiscoveryHandler : public QObject
{
Q_OBJECT
public:
DiscoveryHandler(Account* account);
~DiscoveryHandler();
void initialize();
private slots:
void onItemsReceived (const QXmppDiscoveryIq& items);
void onInfoReceived (const QXmppDiscoveryIq& info);
private:
Account* acc;
bool omemoToCarbonsConnected;
};
}
#endif // CORE_DISCOVERYHANDLER_H

View File

@ -19,158 +19,145 @@
#include "messagehandler.h" #include "messagehandler.h"
#include "core/account.h" #include "core/account.h"
static const QMap<QString, QVariant> statePending({{"state", static_cast<uint8_t>(Shared::Message::State::pending)}});
static const QMap<QString, QVariant> stateDelivered({{"state", static_cast<uint8_t>(Shared::Message::State::delivered)}});
static const QMap<QString, QVariant> stateSent({{"state", static_cast<uint8_t>(Shared::Message::State::sent)}});
Core::MessageHandler::MessageHandler(Core::Account* account): Core::MessageHandler::MessageHandler(Core::Account* account):
QObject(), QObject(),
acc(account), acc(account),
pendingStateMessages(), pendingStateMessages(),
uploadingSlotsQueue() uploadingSlotsQueue()
{} {
}
void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) {
#ifdef WITH_OMEMO
switch (msg.encryptionMethod()) {
case QXmpp::NoEncryption:
break; //just do nothing
case QXmpp::UnknownEncryption:
qDebug() << "Account" << acc->getName() << "received a message with unknown encryption type";
break; //let it go the way it is, there is nothing I can do here
case QXmpp::Otr:
qDebug() << "Account" << acc->getName() << "received an OTR encrypted message, not supported yet";
break; //let it go the way it is, there is nothing I can do yet
case QXmpp::LegacyOpenPgp:
qDebug() << "Account" << acc->getName() << "received an LegacyOpenPgp encrypted message, not supported yet";
break; //let it go the way it is, there is nothing I can do yet
case QXmpp::Ox:
qDebug() << "Account" << acc->getName() << "received an Ox encrypted message, not supported yet";
break; //let it go the way it is, there is nothing I can do yet
case QXmpp::Omemo0:
qDebug() << "Account" << acc->getName() << "received an Omemo0 encrypted message, not supported yet";
break; //let it go the way it is, there is nothing I can do yet
case QXmpp::Omemo1:
qDebug() << "Account" << acc->getName() << "received an Omemo1 encrypted message, not supported yet";
break; //let it go the way it is, there is nothing I can do yet
case QXmpp::Omemo2:
break;
}
#endif
#endif
bool handled = false; bool handled = false;
switch (msg.type()) { switch (msg.type()) {
case QXmppMessage::Normal: case QXmppMessage::Normal:
qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
break; break;
case QXmppMessage::Chat: case QXmppMessage::Chat:
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
handled = handleChatMessage(msg, false, msg.isCarbonForwarded(), true);
#else
handled = handleChatMessage(msg); handled = handleChatMessage(msg);
#endif
break; break;
case QXmppMessage::GroupChat: case QXmppMessage::GroupChat:
handled = handleGroupMessage(msg); handled = handleGroupMessage(msg);
break; break;
case QXmppMessage::Error: case QXmppMessage::Error: {
handled = handlePendingMessageError(msg.id(), msg.error().text()); std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
if (!handled) if (std::get<0>(ids)) {
QString id = std::get<1>(ids);
QString jid = std::get<2>(ids);
RosterItem* cnt = acc->rh->getRosterItem(jid);
QMap<QString, QVariant> cData = {
{"state", static_cast<uint>(Shared::Message::State::error)},
{"errorText", msg.error().text()}
};
if (cnt != 0) {
cnt->changeMessage(id, cData);
}
emit acc->changeMessage(jid, id, cData);
handled = true;
} else {
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
}
break; }
break;
case QXmppMessage::Headline: case QXmppMessage::Headline:
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
break; break;
} }
if (!handled) if (!handled) {
logMessage(msg); logMessage(msg);
}
} }
bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) { bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
return adjustPendingMessage(id, { {
{"state", static_cast<uint8_t>(Shared::Message::State::error)}, if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) {
{"errorText", errorText} Shared::Message sMsg(Shared::Message::chat);
}, true); initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
} QString jid = sMsg.getPenPalJid();
Contact* cnt = acc->rh->getContact(jid);
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { if (cnt == 0) {
if (msg.body().isEmpty() && msg.outOfBandUrl().isEmpty()) cnt = acc->rh->addOutOfRosterContact(jid);
return false; qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact";
}
Shared::Message sMsg(Shared::Message::chat); if (outgoing) {
initializeMessage(sMsg, msg, outgoing, forwarded, guessing); if (forwarded) {
QString jid = sMsg.getPenPalJid(); sMsg.setState(Shared::Message::State::sent);
Contact* cnt = acc->rh->getContact(jid); }
if (cnt == 0) { } else {
cnt = acc->rh->addOutOfRosterContact(jid); sMsg.setState(Shared::Message::State::delivered);
qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; }
} QString oId = msg.replaceId();
if (sMsg.getOutgoing()) { if (oId.size() > 0) {
if (sMsg.getForwarded()) QMap<QString, QVariant> cData = {
sMsg.setState(Shared::Message::State::sent); {"body", sMsg.getBody()},
} else { {"stamp", sMsg.getTime()}
sMsg.setState(Shared::Message::State::delivered); };
} cnt->correctMessageInArchive(oId, sMsg);
QString oId = msg.replaceId(); emit acc->changeMessage(jid, oId, cData);
if (oId.size() > 0) { } else {
QMap<QString, QVariant> cData = { cnt->appendMessageToArchive(sMsg);
{"body", sMsg.getBody()},
{"stamp", sMsg.getTime()}
};
cnt->correctMessageInArchive(oId, sMsg);
emit acc->changeMessage(jid, oId, cData);
} else {
cnt->appendMessageToArchive(sMsg);
emit acc->message(sMsg);
}
return true;
}
bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) {
const QString& body(msg.body());
if (body.isEmpty())
return false;
Shared::Message sMsg(Shared::Message::groupChat);
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
QString jid = sMsg.getPenPalJid();
Conference* cnt = acc->rh->getConference(jid);
if (cnt == 0)
return false;
bool result = adjustPendingMessage(msg.id(), stateDelivered, true);
if (result) //then it was an echo of my own sent message, nothing else needs to be done
return result;
QString oId = msg.replaceId();
if (oId.size() > 0) {
QMap<QString, QVariant> cData = {
{"body", sMsg.getBody()},
{"stamp", sMsg.getTime()}
};
cnt->correctMessageInArchive(oId, sMsg);
emit acc->changeMessage(jid, oId, cData);
} else {
cnt->appendMessageToArchive(sMsg);
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
if (sMsg.getTime() > minAgo) //otherwise it's considered a delayed delivery, most probably MUC history initial fetch
emit acc->message(sMsg); emit acc->message(sMsg);
} }
return true; return true;
}
return false;
} }
void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
{
const QString& body(msg.body());
if (body.size() != 0) {
Shared::Message sMsg(Shared::Message::groupChat);
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
QString jid = sMsg.getPenPalJid();
Conference* cnt = acc->rh->getConference(jid);
if (cnt == 0) {
return false;
}
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
if (std::get<0>(ids)) {
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
cnt->changeMessage(std::get<1>(ids), cData);
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
} else {
QString oId = msg.replaceId();
if (oId.size() > 0) {
QMap<QString, QVariant> cData = {
{"body", sMsg.getBody()},
{"stamp", sMsg.getTime()}
};
cnt->correctMessageInArchive(oId, sMsg);
emit acc->changeMessage(jid, oId, cData);
} else {
cnt->appendMessageToArchive(sMsg);
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt
emit acc->message(sMsg);
} else {
//qDebug() << "Delayed delivery: ";
}
}
}
return true;
}
return false;
}
void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const
{
const QDateTime& time(source.stamp()); const QDateTime& time(source.stamp());
QString id; QString id;
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
id = source.originId(); id = source.originId();
if (id.size() == 0) if (id.size() == 0) {
id = source.id(); id = source.id();
}
target.setStanzaId(source.stanzaId()); target.setStanzaId(source.stanzaId());
qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId();
#else #else
@ -187,30 +174,30 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
target.setTo(source.to()); target.setTo(source.to());
target.setBody(source.body()); target.setBody(source.body());
target.setForwarded(forwarded); target.setForwarded(forwarded);
#ifdef WITH_OMEMO
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
if (source.encryptionMethod() == QXmpp::EncryptionMethod::Omemo2)
target.setEncryption(Shared::EncryptionProtocol::omemo2);
#endif
#endif
if (guessing)
outgoing = target.getFromJid() == acc->getBareJid();
if (guessing) {
if (target.getFromJid() == acc->getBareJid()) {
outgoing = true;
} else {
outgoing = false;
}
}
target.setOutgoing(outgoing); target.setOutgoing(outgoing);
if (time.isValid()) if (time.isValid()) {
target.setTime(time); target.setTime(time);
else } else {
target.setCurrentTime(); target.setCurrentTime();
}
QString oob = source.outOfBandUrl(); QString oob = source.outOfBandUrl();
if (oob.size() > 0) if (oob.size() > 0) {
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId)); target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
}
target.setOutOfBandUrl(oob); target.setOutOfBandUrl(oob);
} }
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) { void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
{
qDebug() << reason; qDebug() << reason;
qDebug() << "- from: " << msg.from(); qDebug() << "- from: " << msg.from();
qDebug() << "- to: " << msg.to(); qDebug() << "- to: " << msg.to();
@ -226,44 +213,58 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re
qDebug() << "=============================="; qDebug() << "==============================";
} }
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg)
void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) { {
handleChatMessage(msg, false, true); handleChatMessage(msg, false, true);
} }
void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg)
{
handleChatMessage(msg, true, true); handleChatMessage(msg, true, true);
} }
#endif
std::optional<Shared::MessageInfo> Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessageId(const QString& id)
{
std::tuple<bool, QString, QString> result({false, "", ""});
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id); std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
if (itr != pendingStateMessages.end()) { if (itr != pendingStateMessages.end()) {
Shared::MessageInfo info(acc->name, itr->second, itr->first); std::get<0>(result) = true;
std::get<2>(result) = itr->second;
std::map<QString, QString>::const_iterator itrC = pendingCorrectionMessages.find(id); std::map<QString, QString>::const_iterator itrC = pendingCorrectionMessages.find(id);
if (itrC != pendingCorrectionMessages.end()) { if (itrC != pendingCorrectionMessages.end()) {
if (itrC->second.size() > 0) if (itrC->second.size() > 0) {
info.jid = itrC->second; std::get<1>(result) = itrC->second;
} else {
if (clear) std::get<1>(result) = itr->first;
pendingCorrectionMessages.erase(itrC); }
pendingCorrectionMessages.erase(itrC);
} else {
std::get<1>(result) = itr->first;
} }
if (clear) pendingStateMessages.erase(itr);
pendingStateMessages.erase(itr);
return info;
} }
return std::nullopt; return result;
} }
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) { void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
SHARED_UNUSED(jid); {
adjustPendingMessage(id, {{"state", static_cast<uint>(Shared::Message::State::delivered)}}, true); std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(id);
if (std::get<0>(ids)) {
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids));
if (ri != 0) {
ri->changeMessage(std::get<1>(ids), cData);
}
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
}
} }
void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) { void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId)
{
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) { if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId)); pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId));
prepareUpload(data, newMessage); prepareUpload(data, newMessage);
@ -272,148 +273,83 @@ void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMess
} }
} }
void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) { void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage)
{
QString jid = data.getPenPalJid(); QString jid = data.getPenPalJid();
QString id = data.getId(); QString id = data.getId();
qDebug() << "Sending message with id:" << id; qDebug() << "Sending message with id:" << id;
if (originalId.size() > 0) if (originalId.size() > 0) {
qDebug() << "To replace the one with id:" << originalId; qDebug() << "To replace one with id:" << originalId;
}
RosterItem* ri = acc->rh->getRosterItem(jid); RosterItem* ri = acc->rh->getRosterItem(jid);
if (newMessage && originalId.size() > 0) bool sent = false;
if (newMessage && originalId.size() > 0) {
newMessage = false; newMessage = false;
}
QDateTime sendTime = QDateTime::currentDateTimeUtc(); QDateTime sendTime = QDateTime::currentDateTimeUtc();
std::pair<Shared::Message::State, QString> result = scheduleSending(data, sendTime, originalId); if (acc->state == Shared::ConnectionState::connected) {
data.setState(result.first); QXmppMessage msg(createPacket(data, sendTime, originalId));
data.setErrorText(result.second);
sent = acc->client.sendPacket(msg);
if (sent) {
data.setState(Shared::Message::State::sent);
} else {
data.setState(Shared::Message::State::error);
data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
}
} else {
data.setState(Shared::Message::State::error);
data.setErrorText("You are is offline or reconnecting");
}
QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId)); QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId));
if (ri != nullptr) {
if (newMessage)
ri->appendMessageToArchive(data);
else
ri->changeMessage(originalId.isEmpty() ? id : originalId, changes);
if (data.getState() != Shared::Message::State::error) { QString realId;
if (originalId.size() > 0) {
realId = originalId;
} else {
realId = id;
}
if (ri != 0) {
if (newMessage) {
ri->appendMessageToArchive(data);
} else {
ri->changeMessage(realId, changes);
}
if (sent) {
pendingStateMessages.insert(std::make_pair(id, jid)); pendingStateMessages.insert(std::make_pair(id, jid));
if (originalId.size() > 0) if (originalId.size() > 0) {
pendingCorrectionMessages.insert(std::make_pair(id, originalId)); pendingCorrectionMessages.insert(std::make_pair(id, originalId));
}
} else { } else {
pendingStateMessages.erase(id); pendingStateMessages.erase(id);
pendingCorrectionMessages.erase(id); pendingCorrectionMessages.erase(id);
} }
} }
emit acc->changeMessage(jid, originalId.isEmpty() ? id : originalId, changes); emit acc->changeMessage(jid, realId, changes);
} }
std::pair<Shared::Message::State, QString> Core::MessageHandler::scheduleSending( QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const
const Shared::Message& message, {
const QDateTime& sendTime,
const QString& originalId
) {
if (acc->state != Shared::ConnectionState::connected)
return {Shared::Message::State::error, "You are is offline or reconnecting"};
QXmppMessage msg = createPacket(message, sendTime, originalId);
QString id = msg.id();
#ifdef WITH_OMEMO
if (message.getEncryption() == Shared::EncryptionProtocol::omemo2) {
QXmppTask<QXmppE2eeExtension::MessageEncryptResult> task = acc->om->encryptMessage(std::move(msg), std::nullopt);
if (task.isFinished()) {
const QXmppE2eeExtension::MessageEncryptResult& res = task.result();
if (std::holds_alternative<std::unique_ptr<QXmppMessage>>(res)) {
qDebug() << "Successfully encrypted a message";
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(res);
encrypted->setBody(QString());
encrypted->setOutOfBandUrl(QString());
bool success = acc->client.sendPacket(*encrypted.get());
if (success) {
qDebug() << "Successfully sent an encrypted message";
return {Shared::Message::State::sent, ""};
} else {
qDebug() << "Couldn't sent an encrypted message";
return {Shared::Message::State::error, "Error sending successfully encrypted message"};
}
} else if (std::holds_alternative<QXmppError>(res)) {
qDebug() << "Couldn't encrypt a message";
const QXmppError& err = std::get<QXmppError>(res);
return {Shared::Message::State::error, err.description};
} else {
qDebug() << "Couldn't encrypt a message";
return {Shared::Message::State::error, "Unexpected error ecryptng the message"};
}
} else {
task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) {
if (std::holds_alternative<std::unique_ptr<QXmppMessage>>(result)) {
qDebug() << "Successfully encrypted a message";
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(result);
encrypted->setBody(QString());
encrypted->setOutOfBandUrl(QString());
bool success = acc->client.sendPacket(*encrypted.get());
if (success) {
qDebug() << "Successfully sent an encrypted message";
if (!adjustPendingMessage(id, stateSent, false))
qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate";
} else {
qDebug() << "Couldn't sent an encrypted message";
handlePendingMessageError(id, "Error sending successfully encrypted message");
}
} else if (std::holds_alternative<QXmppError>(result)) {
qDebug() << "Couldn't encrypt a message";
const QXmppError& err = std::get<QXmppError>(result);
handlePendingMessageError(id, err.description);
} else {
qDebug() << "Couldn't encrypt a message";
handlePendingMessageError(id, "Unexpected error ecryptng the message");
}
});
return {Shared::Message::State::pending, ""};
}
} else
#endif
{
bool success = acc->client.sendPacket(msg);
if (success)
return {Shared::Message::State::sent, ""};
else
return {Shared::Message::State::error, "Error sending message, internal QXMPP error"};
}
}
bool Core::MessageHandler::adjustPendingMessage(const QString& messageId, const QMap<QString, QVariant>& data, bool final) {
std::optional<Shared::MessageInfo> info = getOriginalPendingMessageId(messageId, final);
if (info) {
RosterItem* ri = acc->rh->getRosterItem(info->jid);
if (ri != nullptr)
ri->changeMessage(info->messageId, data);
emit acc->changeMessage(info->jid, info->messageId, data);
return true;
}
return false;
}
QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const {
QMap<QString, QVariant> changes; QMap<QString, QVariant> changes;
QString oob = data.getOutOfBandUrl(); QString oob = data.getOutOfBandUrl();
Shared::Message::State mstate = data.getState(); Shared::Message::State mstate = data.getState();
changes.insert("state", static_cast<uint>(mstate)); changes.insert("state", static_cast<uint>(mstate));
if (mstate == Shared::Message::State::error) if (mstate == Shared::Message::State::error) {
changes.insert("errorText", data.getErrorText()); changes.insert("errorText", data.getErrorText());
}
if (oob.size() > 0) if (oob.size() > 0) {
changes.insert("outOfBandUrl", oob); changes.insert("outOfBandUrl", oob);
}
if (newMessage) if (newMessage) {
data.setTime(time); data.setTime(time);
}
if (originalId.size() > 0) if (originalId.size() > 0) {
changes.insert("body", data.getBody()); changes.insert("body", data.getBody());
}
changes.insert("stamp", time); changes.insert("stamp", time);
//sometimes (when the image is pasted with ctrl+v) //sometimes (when the image is pasted with ctrl+v)
@ -423,20 +359,22 @@ QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data,
if (attachPath.size() > 0) { if (attachPath.size() > 0) {
QString squawkified = Shared::squawkifyPath(attachPath); QString squawkified = Shared::squawkifyPath(attachPath);
changes.insert("attachPath", squawkified); changes.insert("attachPath", squawkified);
if (attachPath != squawkified) if (attachPath != squawkified) {
data.setAttachPath(squawkified); data.setAttachPath(squawkified);
}
} }
return changes; return changes;
} }
QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const { 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(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
QString id(data.getId()); QString id(data.getId());
if (originalId.size() > 0) if (originalId.size() > 0) {
msg.setReplaceId(originalId); msg.setReplaceId(originalId);
}
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
msg.setOriginId(id); msg.setOriginId(id);
@ -450,58 +388,59 @@ QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, con
return msg; return msg;
} }
void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) { void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage)
if (acc->state != Shared::ConnectionState::connected) { {
if (acc->state == Shared::ConnectionState::connected) {
QString jid = data.getPenPalJid();
QString id = data.getId();
RosterItem* ri = acc->rh->getRosterItem(jid);
if (!ri) {
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
return;
}
QString path = data.getAttachPath();
QString url = acc->network->getFileRemoteUrl(path);
if (url.size() != 0) {
sendMessageWithLocalUploadedFile(data, url, newMessage);
} else {
pendingStateMessages.insert(std::make_pair(id, jid));
if (newMessage) {
ri->appendMessageToArchive(data);
} else {
QMap<QString, QVariant> changes({
{"state", (uint)Shared::Message::State::pending}
});
ri->changeMessage(id, changes);
emit acc->changeMessage(jid, id, changes);
}
//this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file
if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
if (acc->um->serviceFound()) {
QFileInfo file(path);
if (file.exists() && file.isReadable()) {
pendingStateMessages.insert(std::make_pair(id, jid));
uploadingSlotsQueue.emplace_back(path, id);
if (uploadingSlotsQueue.size() == 1) {
acc->um->requestUploadSlot(file);
}
} else {
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
}
} else {
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
}
}
}
} else {
handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting"); handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting");
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping"; qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
return;
}
QString jid = data.getPenPalJid();
QString id = data.getId();
RosterItem* ri = acc->rh->getRosterItem(jid);
if (ri == nullptr) {
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
return;
}
QString path = data.getAttachPath();
QString url = acc->network->getFileRemoteUrl(path);
if (url.size() != 0)
return sendMessageWithLocalUploadedFile(data, url, newMessage);
pendingStateMessages.insert(std::make_pair(id, jid));
if (newMessage) {
ri->appendMessageToArchive(data);
} else {
ri->changeMessage(id, statePending);
emit acc->changeMessage(jid, id, statePending);
}
//this checks if the file is already uploading, and if so it subscribes to it's success,
//So, I need to do stuff only if the network knows nothing of this file
if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path))
return;
if (!acc->um->serviceFound()) {
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
return;
}
QFileInfo file(path);
if (file.exists() && file.isReadable()) {
pendingStateMessages.insert(std::make_pair(id, jid));
uploadingSlotsQueue.emplace_back(path, id);
if (uploadingSlotsQueue.size() == 1)
acc->um->requestUploadSlot(file);
} else {
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
} }
} }
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) { void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
{
if (uploadingSlotsQueue.size() == 0) { if (uploadingSlotsQueue.size() == 0) {
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
} else { } else {
@ -511,12 +450,14 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
uploadingSlotsQueue.pop_front(); uploadingSlotsQueue.pop_front();
if (uploadingSlotsQueue.size() > 0) if (uploadingSlotsQueue.size() > 0) {
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
}
} }
} }
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) { void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
{
QString err(request.error().text()); QString err(request.error().text());
if (uploadingSlotsQueue.size() == 0) { if (uploadingSlotsQueue.size() == 0) {
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
@ -527,38 +468,42 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err); handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
uploadingSlotsQueue.pop_front(); uploadingSlotsQueue.pop_front();
if (uploadingSlotsQueue.size() > 0) if (uploadingSlotsQueue.size() > 0) {
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
}
}
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path) {
QMap<QString, QVariant> cData = {
{"attachPath", path}
};
for (const Shared::MessageInfo& info : msgs) {
if (info.account != acc->getName())
continue;
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
if (cnt != nullptr) {
bool changed = cnt->changeMessage(info.messageId, cData);
if (changed)
emit acc->changeMessage(info.jid, info.messageId, cData);
} }
} }
} }
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up) { void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
if (!up) {
return; QMap<QString, QVariant> cData = {
{"attachPath", path}
for (const Shared::MessageInfo& info : msgs) };
if (info.account == acc->getName()) for (const Shared::MessageInfo& info : msgs) {
handleUploadError(info.jid, info.messageId, text); if (info.account == acc->getName()) {
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
if (cnt != 0) {
if (cnt->changeMessage(info.messageId, cData)) {
emit acc->changeMessage(info.jid, info.messageId, cData);
}
}
}
}
} }
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) { void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up)
{
if (up) {
for (const Shared::MessageInfo& info : msgs) {
if (info.account == acc->getName()) {
handleUploadError(info.jid, info.messageId, text);
}
}
}
}
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
{
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText); emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
pendingStateMessages.erase(messageId); pendingStateMessages.erase(messageId);
pendingCorrectionMessages.erase(messageId); pendingCorrectionMessages.erase(messageId);
@ -568,27 +513,28 @@ void Core::MessageHandler::handleUploadError(const QString& jid, const QString&
}); });
} }
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path) { void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
{
for (const Shared::MessageInfo& info : msgs) { for (const Shared::MessageInfo& info : msgs) {
if (info.account != acc->getName()) if (info.account == acc->getName()) {
continue; RosterItem* ri = acc->rh->getRosterItem(info.jid);
if (ri != 0) {
RosterItem* ri = acc->rh->getRosterItem(info.jid); Shared::Message msg = ri->getMessage(info.messageId);
if (ri != nullptr) { msg.setAttachPath(path);
Shared::Message msg = ri->getMessage(info.messageId); sendMessageWithLocalUploadedFile(msg, url, false);
msg.setAttachPath(path); } else {
sendMessageWithLocalUploadedFile(msg, url, false); qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
} else { }
qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
} }
} }
} }
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage) { void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage)
{
msg.setOutOfBandUrl(url); msg.setOutOfBandUrl(url);
if (msg.getBody().size() == 0) //not sure why, but most messengers do that if (msg.getBody().size() == 0) { //not sure why, but most messages do that
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
}
performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage); performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage);
//TODO removal/progress update //TODO removal/progress update
} }
@ -600,9 +546,10 @@ static const std::set<QString> allowedToChangeKeys({
"errorText" "errorText"
}); });
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data) { void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
{
RosterItem* cnt = acc->rh->getRosterItem(jid); RosterItem* cnt = acc->rh->getRosterItem(jid);
if (cnt != nullptr) { if (cnt != 0) {
bool allSupported = true; bool allSupported = true;
QString unsupportedString; QString unsupportedString;
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
@ -622,13 +569,14 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin
} }
} }
void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) { void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
{
RosterItem* cnt = acc->rh->getRosterItem(jid); RosterItem* cnt = acc->rh->getRosterItem(jid);
if (cnt != nullptr) { if (cnt != 0) {
try { try {
Shared::Message msg = cnt->getMessage(id); Shared::Message msg = cnt->getMessage(id);
if (msg.getState() == Shared::Message::State::error) { if (msg.getState() == Shared::Message::State::error) {
if (msg.getEdited()) { if (msg.getEdited()){
QString originalId = msg.getId(); QString originalId = msg.getId();
msg.generateRandomId(); msg.generateRandomId();
sendMessage(msg, false, originalId); sendMessage(msg, false, originalId);
@ -638,7 +586,7 @@ void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
} else { } else {
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping"; qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping";
} }
} catch (const LMDBAL::NotFound& err) { } catch (const Archive::NotFound& err) {
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping"; qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
} }
} else { } else {

View File

@ -16,29 +16,31 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_MESSAGEHANDLER_H
#define CORE_MESSAGEHANDLER_H
#include <QObject> #include <QObject>
#include <deque> #include <deque>
#include <map> #include <map>
#include <functional>
#include <optional>
#include <QXmppMessage.h> #include <QXmppMessage.h>
#include <QXmppHttpUploadIq.h> #include <QXmppHttpUploadIq.h>
#ifdef WITH_OMEMO
#include <QXmppE2eeExtension.h>
#endif
#include <shared/message.h> #include <shared/message.h>
#include <shared/messageinfo.h> #include <shared/messageinfo.h>
#include <shared/pathcheck.h> #include <shared/pathcheck.h>
namespace Core { namespace Core {
/**
* @todo write docs
*/
class Account; class Account;
class MessageHandler : public QObject { class MessageHandler : public QObject
{
Q_OBJECT Q_OBJECT
public: public:
MessageHandler(Account* account); MessageHandler(Account* account);
@ -50,10 +52,8 @@ public:
public slots: public slots:
void onMessageReceived(const QXmppMessage& message); void onMessageReceived(const QXmppMessage& message);
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
void onCarbonMessageReceived(const QXmppMessage& message); void onCarbonMessageReceived(const QXmppMessage& message);
void onCarbonMessageSent(const QXmppMessage& message); void onCarbonMessageSent(const QXmppMessage& message);
#endif
void onReceiptReceived(const QString& jid, const QString& id); void onReceiptReceived(const QString& jid, const QString& id);
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
@ -72,10 +72,7 @@ private:
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const;
QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const;
std::optional<Shared::MessageInfo> getOriginalPendingMessageId(const QString& id, bool clear = true); std::tuple<bool, QString, QString> getOriginalPendingMessageId(const QString& id);
bool handlePendingMessageError(const QString& id, const QString& errorText);
std::pair<Shared::Message::State, QString> scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId);
bool adjustPendingMessage(const QString& messageId, const QMap<QString, QVariant>& data, bool final);
private: private:
Account* acc; Account* acc;
@ -85,3 +82,5 @@ private:
}; };
} }
#endif // CORE_MESSAGEHANDLER_H

View File

@ -1,285 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include "omemohandler.h"
#include "core/account.h"
#include "core/adapterfunctions.h"
Core::OmemoHandler::OmemoHandler(Account* account) :
QObject(),
QXmppOmemoStorage(),
acc(account),
ownDevice(std::nullopt),
db(acc->getName() + "/omemo"),
meta(db.addCache<QString, QVariant>("meta")),
devices(db.addCache<QString, QHash<uint32_t, Device>>("devices")),
preKeyPairs(db.addCache<uint32_t, QByteArray>("preKeyPairs")),
signedPreKeyPairs(db.addCache<uint32_t, SignedPreKeyPair>("signedPreKeyPairs"))
{
db.open();
try {
QVariant own = meta->getRecord("ownDevice");
ownDevice = own.value<OwnDevice>();
qDebug() << "Successfully found own device omemo data for account" << acc->getName();
} catch (const LMDBAL::NotFound& e) {
qDebug() << "No device omemo data was found for account" << acc->getName();
}
}
Core::OmemoHandler::~OmemoHandler() {
db.close();
}
bool Core::OmemoHandler::hasOwnDevice() {
return ownDevice.has_value();
}
QXmppTask<QXmppOmemoStorage::OmemoData> Core::OmemoHandler::allData() {
OmemoData data;
data.ownDevice = ownDevice;
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
std::map<uint32_t, QByteArray> pkeys = preKeyPairs->readAll(txn);
for (const std::pair<const uint32_t, QByteArray>& pair : pkeys)
data.preKeyPairs.insert(pair.first, pair.second);
std::map<uint32_t, SignedPreKeyPair> spre = signedPreKeyPairs->readAll(txn);
for (const std::pair<const uint32_t, SignedPreKeyPair>& pair : spre) {
QXmppOmemoStorage::SignedPreKeyPair qxpair = {pair.second.first, pair.second.second};
data.signedPreKeyPairs.insert(pair.first, qxpair);
}
std::map<QString, QHash<uint32_t, Device>> devs = devices->readAll(txn);
for (const std::pair<const QString, QHash<uint32_t, Device>>& pair : devs)
data.devices.insert(pair.first, pair.second);
return Core::makeReadyTask(std::move(data));
}
QXmppTask<void> Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) {
QHash<uint32_t, Device> devs;
LMDBAL::WriteTransaction txn = db.beginTransaction();
bool had = true;
try {
devices->getRecord(jid, devs, txn);
} catch (const LMDBAL::NotFound& error) {
had = false;
}
devs.insert(deviceId, device); //overwrites
if (had)
devices->changeRecord(jid, devs, txn);
else
devices->addRecord(jid, devs, txn);
txn.commit();
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::addPreKeyPairs(const QHash<uint32_t, QByteArray>& keyPairs) {
LMDBAL::WriteTransaction txn = db.beginTransaction();
for (QHash<uint32_t, QByteArray>::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr)
preKeyPairs->forceRecord(itr.key(), itr.value(), txn);
txn.commit();
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) {
signedPreKeyPairs->forceRecord(keyId, std::make_pair(keyPair.creationDate, keyPair.data));
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) {
LMDBAL::WriteTransaction txn = db.beginTransaction();
QHash<uint32_t, Device> devs = devices->getRecord(jid, txn);
devs.remove(deviceId);
if (devs.isEmpty())
devices->removeRecord(jid, txn);
else
devices->changeRecord(jid, devs, txn);
txn.commit();
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::removeDevices(const QString& jid) {
devices->removeRecord(jid);
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::removePreKeyPair(uint32_t keyId) {
try {
preKeyPairs->removeRecord(keyId);
} catch (const LMDBAL::NotFound& e) {
qDebug() << "Couldn't remove preKeyPair " << e.what();
}
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) {
try {
signedPreKeyPairs->removeRecord(keyId);
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::setOwnDevice(const std::optional<OwnDevice>& device) {
bool had = ownDevice.has_value();
ownDevice = device;
if (ownDevice.has_value()) {
if (had)
meta->changeRecord("ownDevice", QVariant::fromValue(ownDevice.value()));
else
meta->addRecord("ownDevice", QVariant::fromValue(ownDevice.value()));
} else if (had) {
meta->removeRecord("ownDevice");
}
return Core::makeReadyTask();
}
QXmppTask<void> Core::OmemoHandler::resetAll() {
ownDevice = std::nullopt;
db.drop();
return Core::makeReadyTask();
}
void Core::OmemoHandler::getDevices(const QString& jid, std::list<Shared::KeyInfo>& out) const {
QHash<uint32_t, Device> devs;
try {
devices->getRecord(jid, devs);
} catch (const LMDBAL::NotFound& error) {}
for (QHash<uint32_t, Device>::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) {
const Device& dev = itr.value();
out.emplace_back(
itr.key(),
dev.keyId,
dev.label,
dev.removalFromDeviceListDate,
Shared::TrustLevel::undecided,
Shared::EncryptionProtocol::omemo2,
false
);
}
}
void Core::OmemoHandler::requestBundles(const QString& jid) {
QXmppTask<void> task = acc->om->buildMissingSessions({jid});
Contact* cnt = acc->rh->getContact(jid);
if (cnt)
cnt->omemoBundles = Shared::Possible::discovering;
task.then(this, std::bind(&OmemoHandler::onBundlesReceived, this, jid));
}
void Core::OmemoHandler::requestOwnBundles() {
QXmppTask<void> task = acc->om->buildMissingSessions({acc->getBareJid()});
task.then(this, std::bind(&OmemoHandler::onOwnBundlesReceived, this));
}
void Core::OmemoHandler::onBundlesReceived(const QString& jid) {
std::list<Shared::KeyInfo> keys = readKeys(jid);
Contact* cnt = acc->rh->getContact(jid);
if (cnt)
cnt->omemoBundles = Shared::Possible::present;
acc->delay->receivedBundles(jid, keys);
}
void Core::OmemoHandler::onOwnBundlesReceived() {
std::list<Shared::KeyInfo> keys = readKeys(acc->getBareJid());
if (ownDevice)
keys.emplace_front(
ownDevice->id,
ownDevice->publicIdentityKey,
ownDevice->label,
QDateTime::currentDateTime(),
Shared::TrustLevel::authenticated,
Shared::EncryptionProtocol::omemo2,
true
);
acc->delay->receivedOwnBundles(keys);
}
std::list<Shared::KeyInfo> Core::OmemoHandler::readKeys(const QString& jid) {
std::list<Shared::KeyInfo> keys;
getDevices(jid, keys);
std::map<QByteArray, Shared::TrustLevel> trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid);
for (Shared::KeyInfo& key : keys) {
std::map<QByteArray, Shared::TrustLevel>::const_iterator itr = trustLevels.find(key.fingerPrint);
if (itr != trustLevels.end())
key.trustLevel = itr->second;
}
return keys;
}
void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) {
SHARED_UNUSED(id);
qDebug() << "OMEMO device added for" << jid;
}
QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) {
in >> device.label;
in >> device.keyId;
in >> device.session;
in >> device.unrespondedSentStanzasCount;
in >> device.unrespondedReceivedStanzasCount;
in >> device.removalFromDeviceListDate;
return in;
}
QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::Device& device) {
out << device.label;
out << device.keyId;
out << device.session;
out << device.unrespondedSentStanzasCount;
out << device.unrespondedReceivedStanzasCount;
out << device.removalFromDeviceListDate;
return out;
}
QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::OwnDevice& device) {
in >> device.id;
in >> device.label;
in >> device.privateIdentityKey;
in >> device.publicIdentityKey;
in >> device.latestSignedPreKeyId;
in >> device.latestPreKeyId;
return in;
}
QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::OwnDevice& device) {
out << device.id;
out << device.label;
out << device.privateIdentityKey;
out << device.publicIdentityKey;
out << device.latestSignedPreKeyId;
out << device.latestPreKeyId;
return out;
}

View File

@ -1,90 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <map>
#include <list>
#include <functional>
#include <QXmppOmemoStorage.h>
#include <cache.h>
#include <shared/keyinfo.h>
#include <shared/enums.h>
Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice);
Q_DECLARE_METATYPE(QXmppOmemoStorage::Device);
namespace Core {
class Account;
class OmemoHandler : public QObject, public QXmppOmemoStorage {
Q_OBJECT
public:
typedef std::pair<QDateTime, QByteArray> SignedPreKeyPair;
OmemoHandler(Account* account);
~OmemoHandler() override;
virtual QXmppTask<OmemoData> allData() override;
virtual QXmppTask<void> setOwnDevice(const std::optional<OwnDevice> &device) override;
virtual QXmppTask<void> addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override;
virtual QXmppTask<void> removeSignedPreKeyPair(uint32_t keyId) override;
virtual QXmppTask<void> addPreKeyPairs(const QHash<uint32_t, QByteArray> &keyPairs) override;
virtual QXmppTask<void> removePreKeyPair(uint32_t keyId) override;
virtual QXmppTask<void> addDevice(const QString &jid, uint32_t deviceId, const Device &device) override;
virtual QXmppTask<void> removeDevice(const QString &jid, uint32_t deviceId) override;
virtual QXmppTask<void> removeDevices(const QString &jid) override;
virtual QXmppTask<void> resetAll() override;
bool hasOwnDevice();
void requestBundles(const QString& jid);
void requestOwnBundles();
void getDevices(const QString& jid, std::list<Shared::KeyInfo>& out) const;
public slots:
void onOmemoDeviceAdded(const QString& jid, uint32_t id);
private slots:
void onBundlesReceived(const QString& jid);
void onOwnBundlesReceived();
std::list<Shared::KeyInfo> readKeys(const QString& jid);
private:
Account* acc;
std::optional<OwnDevice> ownDevice;
LMDBAL::Base db;
LMDBAL::Cache<QString, QVariant>* meta;
LMDBAL::Cache<QString, QHash<uint32_t, Device>>* devices;
LMDBAL::Cache<uint32_t, QByteArray>* preKeyPairs;
LMDBAL::Cache<uint32_t, SignedPreKeyPair>* signedPreKeyPairs;
};
}
QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::Device& device);
QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device);
QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device);
QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device);

View File

@ -26,9 +26,9 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
conferences(), conferences(),
groups(), groups(),
queuedContacts(), queuedContacts(),
outOfRosterContacts() {} outOfRosterContacts(),
pepSupport(false)
void Core::RosterHandler::initialize() { {
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved); connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved);
@ -37,30 +37,23 @@ void Core::RosterHandler::initialize() {
connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded); connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded);
connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived); connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived);
connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged);
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
connect(acc->th, &TrustHandler::trustLevelsChanged, this, &RosterHandler::onTrustChanged);
#endif
} }
Core::RosterHandler::~RosterHandler() { Core::RosterHandler::~RosterHandler()
clear(); {
for (std::map<QString, Contact*>::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) {
delete itr->second;
}
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
delete itr->second;
}
} }
void Core::RosterHandler::clear() { void Core::RosterHandler::onRosterReceived()
for (const std::pair<const QString, Contact*>& pair : contacts) {
delete pair.second; acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards
for (const std::pair<const QString, Conference*>& pair : conferences)
delete pair.second;
contacts.clear();
conferences.clear();
}
void Core::RosterHandler::onRosterReceived() {
QStringList bj = acc->rm->getRosterBareJids(); QStringList bj = acc->rm->getRosterBareJids();
for (int i = 0; i < bj.size(); ++i) { for (int i = 0; i < bj.size(); ++i) {
const QString& jid = bj[i]; const QString& jid = bj[i];
@ -68,7 +61,8 @@ void Core::RosterHandler::onRosterReceived() {
} }
} }
void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) { void Core::RosterHandler::onRosterItemAdded(const QString& bareJid)
{
QString lcJid = bareJid.toLower(); QString lcJid = bareJid.toLower();
addedAccount(lcJid); addedAccount(lcJid);
std::map<QString, QString>::const_iterator itr = queuedContacts.find(lcJid); std::map<QString, QString>::const_iterator itr = queuedContacts.find(lcJid);
@ -78,7 +72,8 @@ void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) {
} }
} }
void Core::RosterHandler::addedAccount(const QString& jid) { void Core::RosterHandler::addedAccount(const QString& jid)
{
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid); std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid); QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid);
Contact* contact; Contact* contact;
@ -87,6 +82,7 @@ void Core::RosterHandler::addedAccount(const QString& jid) {
newContact = true; newContact = true;
contact = new Contact(jid, acc->name); contact = new Contact(jid, acc->name);
contacts.insert(std::make_pair(jid, contact)); contacts.insert(std::make_pair(jid, contact));
} else { } else {
contact = itr->second; contact = itr->second;
} }
@ -98,44 +94,70 @@ void Core::RosterHandler::addedAccount(const QString& jid) {
contact->setName(re.name()); contact->setName(re.name());
if (newContact) { if (newContact) {
handleNewContact(contact); QMap<QString, QVariant> cData({
QMap<QString, QVariant> cData = contact->getInfo(); {"name", re.name()},
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) {"state", QVariant::fromValue(state)}
cData.insert("trust", QVariant::fromValue(acc->th->getSummary(jid))); });
#endif
careAboutAvatar(contact, cData);
int grCount = 0; int grCount = 0;
for (const QString& groupName : gr) { for (QSet<QString>::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) {
const QString& groupName = *itr;
addToGroup(jid, groupName); addToGroup(jid, groupName);
emit acc->addContact(jid, groupName, cData); emit acc->addContact(jid, groupName, cData);
grCount++; grCount++;
} }
if (grCount == 0) if (grCount == 0) {
emit acc->addContact(jid, "", cData); emit acc->addContact(jid, "", cData);
if (acc->pepSupport == Shared::Support::supported) {
acc->dm->requestInfo(jid);
//acc->dm->requestItems(jid);
} }
handleNewContact(contact);
} }
} }
void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) { void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin)
{
QXmppMucRoom* room = acc->mm->addRoom(jid); QXmppMucRoom* room = acc->mm->addRoom(jid);
QString lNick = nick; QString lNick = nick;
if (lNick.size() == 0) if (lNick.size() == 0) {
lNick = acc->getName(); lNick = acc->getName();
}
Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room); Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room);
conferences.insert(std::make_pair(jid, conf)); conferences.insert(std::make_pair(jid, conf));
handleNewConference(conf); handleNewConference(conf);
QMap<QString, QVariant> cData = conf->getInfo(); QMap<QString, QVariant> cData = {
{"autoJoin", conf->getAutoJoin()},
{"joined", conf->getJoined()},
{"nick", conf->getNick()},
{"name", conf->getName()},
{"avatars", conf->getAllAvatars()}
};
careAboutAvatar(conf, cData);
emit acc->addRoom(jid, cData); emit acc->addRoom(jid, cData);
} }
void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups) { void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data)
{
Archive::AvatarInfo info;
bool hasAvatar = item->readAvatarInfo(info);
if (hasAvatar) {
if (info.autogenerated) {
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
} else {
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
}
data.insert("avatarPath", item->avatarPath() + "." + info.type);
} else {
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
data.insert("avatarPath", "");
acc->requestVCard(item->jid);
}
}
void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups)
{
if (acc->state == Shared::ConnectionState::connected) { if (acc->state == Shared::ConnectionState::connected) {
std::map<QString, QString>::const_iterator itr = queuedContacts.find(jid); std::map<QString, QString>::const_iterator itr = queuedContacts.find(jid);
if (itr != queuedContacts.end()) { if (itr != queuedContacts.end()) {
@ -149,7 +171,8 @@ void Core::RosterHandler::addContactRequest(const QString& jid, const QString& n
} }
} }
void Core::RosterHandler::removeContactRequest(const QString& jid) { void Core::RosterHandler::removeContactRequest(const QString& jid)
{
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
if (acc->state == Shared::ConnectionState::connected) { if (acc->state == Shared::ConnectionState::connected) {
std::set<QString>::const_iterator itr = outOfRosterContacts.find(lcJid); std::set<QString>::const_iterator itr = outOfRosterContacts.find(lcJid);
@ -164,23 +187,25 @@ void Core::RosterHandler::removeContactRequest(const QString& jid) {
} }
} }
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) { void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact)
{
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory); connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse); connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse);
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged); connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged); connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
connect(contact, &RosterItem::encryptionChanged, this, &RosterHandler::onContactEncryptionChanged); connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
connect(contact, &RosterItem::requestVCard, acc->delay, &DelayManager::Manager::getVCard);
} }
void Core::RosterHandler::handleNewContact(Core::Contact* contact) { void Core::RosterHandler::handleNewContact(Core::Contact* contact)
{
handleNewRosterItem(contact); handleNewRosterItem(contact);
connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded); connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded);
connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved); connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved);
connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged); connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged);
} }
void Core::RosterHandler::handleNewConference(Core::Conference* contact) { void Core::RosterHandler::handleNewConference(Core::Conference* contact)
{
handleNewRosterItem(contact); handleNewRosterItem(contact);
connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged); connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged);
connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged); connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged);
@ -191,27 +216,34 @@ void Core::RosterHandler::handleNewConference(Core::Conference* contact) {
connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant); connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant);
} }
void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data) { void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
{
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->addRoomParticipant(room->jid, nickName, data); emit acc->addRoomParticipant(room->jid, nickName, data);
} }
void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data) { void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
{
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->changeRoomParticipant(room->jid, nickName, data); emit acc->changeRoomParticipant(room->jid, nickName, data);
} }
void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) { void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName)
{
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->removeRoomParticipant(room->jid, nickName); emit acc->removeRoomParticipant(room->jid, nickName);
} }
void Core::RosterHandler::onMucSubjectChanged(const QString& subject) { void Core::RosterHandler::onMucSubjectChanged(const QString& subject)
{
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->changeRoom(room->jid, {{"subject", subject}}); emit acc->changeRoom(room->jid, {
{"subject", subject}
});
} }
void Core::RosterHandler::onContactGroupAdded(const QString& group) { void Core::RosterHandler::onContactGroupAdded(const QString& group)
{
Contact* contact = static_cast<Contact*>(sender()); Contact* contact = static_cast<Contact*>(sender());
if (contact->groupsCount() == 1) { if (contact->groupsCount() == 1) {
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
@ -219,17 +251,14 @@ void Core::RosterHandler::onContactGroupAdded(const QString& group) {
QMap<QString, QVariant> cData({ QMap<QString, QVariant> cData({
{"name", contact->getName()}, {"name", contact->getName()},
{"state", QVariant::fromValue(contact->getSubscriptionState())}, {"state", QVariant::fromValue(contact->getSubscriptionState())}
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
{"trust", QVariant::fromValue(acc->th->getSummary(contact->jid))},
#endif
{"encryption", QVariant::fromValue(contact->encryption())}
}); });
addToGroup(contact->jid, group); addToGroup(contact->jid, group);
emit acc->addContact(contact->jid, group, cData); emit acc->addContact(contact->jid, group, cData);
} }
void Core::RosterHandler::onContactGroupRemoved(const QString& group) { void Core::RosterHandler::onContactGroupRemoved(const QString& group)
{
Contact* contact = static_cast<Contact*>(sender()); Contact* contact = static_cast<Contact*>(sender());
if (contact->groupsCount() == 0) { if (contact->groupsCount() == 0) {
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
@ -239,26 +268,26 @@ void Core::RosterHandler::onContactGroupRemoved(const QString& group) {
removeFromGroup(contact->jid, group); removeFromGroup(contact->jid, group);
} }
void Core::RosterHandler::onContactNameChanged(const QString& cname) { void Core::RosterHandler::onContactNameChanged(const QString& cname)
RosterItem* contact = static_cast<RosterItem*>(sender()); {
emit acc->changeContact(contact->jid, {{"name", cname}});
}
void Core::RosterHandler::onContactEncryptionChanged(Shared::EncryptionProtocol value) {
RosterItem* contact = static_cast<RosterItem*>(sender());
emit acc->changeContact(contact->jid, {{"encryption", QVariant::fromValue(value)}});
}
void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) {
Contact* contact = static_cast<Contact*>(sender()); Contact* contact = static_cast<Contact*>(sender());
emit acc->changeContact(contact->jid, {{"state", QVariant::fromValue(cstate)}}); QMap<QString, QVariant> cData({
{"name", cname},
});
emit acc->changeContact(contact->jid, cData);
} }
void Core::RosterHandler::onTrustChanged(const QString& jid, const Shared::TrustSummary& trust) { void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate)
emit acc->changeContact(jid, {{"trust", QVariant::fromValue(trust)}}); {
Contact* contact = static_cast<Contact*>(sender());
QMap<QString, QVariant> cData({
{"state", QVariant::fromValue(cstate)},
});
emit acc->changeContact(contact->jid, cData);
} }
void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { void Core::RosterHandler::addToGroup(const QString& jid, const QString& group)
{
std::map<QString, std::set<QString>>::iterator gItr = groups.find(group); std::map<QString, std::set<QString>>::iterator gItr = groups.find(group);
if (gItr == groups.end()) { if (gItr == groups.end()) {
gItr = groups.insert(std::make_pair(group, std::set<QString>())).first; gItr = groups.insert(std::make_pair(group, std::set<QString>())).first;
@ -267,7 +296,8 @@ void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) {
gItr->second.insert(jid.toLower()); gItr->second.insert(jid.toLower());
} }
void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) { void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group)
{
QSet<QString> toRemove; QSet<QString> toRemove;
std::map<QString, std::set<QString>>::iterator itr = groups.find(group); std::map<QString, std::set<QString>>::iterator itr = groups.find(group);
if (itr == groups.end()) { if (itr == groups.end()) {
@ -285,53 +315,58 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro
} }
} }
Core::RosterItem* Core::RosterHandler::getRosterItem(const QString& jid) { Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
RosterItem* item = nullptr; {
RosterItem* item = 0;
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
std::map<QString, Contact*>::const_iterator citr = contacts.find(lcJid); std::map<QString, Contact*>::const_iterator citr = contacts.find(lcJid);
if (citr != contacts.end()) { if (citr != contacts.end()) {
item = citr->second; item = citr->second;
} else { } else {
std::map<QString, Conference*>::const_iterator coitr = conferences.find(lcJid); std::map<QString, Conference*>::const_iterator coitr = conferences.find(lcJid);
if (coitr != conferences.end()) if (coitr != conferences.end()) {
item = coitr->second; item = coitr->second;
}
} }
return item; return item;
} }
Core::Conference* Core::RosterHandler::getConference(const QString& jid) { Core::Conference * Core::RosterHandler::getConference(const QString& jid)
{
Conference* item = 0; Conference* item = 0;
std::map<QString, Conference*>::const_iterator coitr = conferences.find(jid.toLower()); std::map<QString, Conference*>::const_iterator coitr = conferences.find(jid.toLower());
if (coitr != conferences.end()) if (coitr != conferences.end()) {
item = coitr->second; item = coitr->second;
}
return item; return item;
} }
Core::Contact* Core::RosterHandler::getContact(const QString& jid) { Core::Contact * Core::RosterHandler::getContact(const QString& jid)
{
Contact* item = 0; Contact* item = 0;
std::map<QString, Contact*>::const_iterator citr = contacts.find(jid.toLower()); std::map<QString, Contact*>::const_iterator citr = contacts.find(jid.toLower());
if (citr != contacts.end()) if (citr != contacts.end()) {
item = citr->second; item = citr->second;
}
return item; return item;
} }
Core::Contact* Core::RosterHandler::addOutOfRosterContact(const QString& jid) { Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid)
{
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
Contact* cnt = new Contact(lcJid, acc->name); Contact* cnt = new Contact(lcJid, acc->name);
contacts.insert(std::make_pair(lcJid, cnt)); contacts.insert(std::make_pair(lcJid, cnt));
outOfRosterContacts.insert(lcJid); outOfRosterContacts.insert(lcJid);
cnt->setSubscriptionState(Shared::SubscriptionState::unknown); cnt->setSubscriptionState(Shared::SubscriptionState::unknown);
emit acc->addContact(lcJid, "", QMap<QString, QVariant>({ emit acc->addContact(lcJid, "", QMap<QString, QVariant>({
{"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}, {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}
{"encryption", QVariant::fromValue(Shared::EncryptionProtocol::none)}
})); }));
handleNewContact(cnt); handleNewContact(cnt);
return cnt; return cnt;
} }
void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) { void Core::RosterHandler::onRosterItemChanged(const QString& bareJid)
{
QString lcJid = bareJid.toLower(); QString lcJid = bareJid.toLower();
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid); std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
if (itr == contacts.end()) { if (itr == contacts.end()) {
@ -348,7 +383,8 @@ void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) {
contact->setName(re.name()); contact->setName(re.name());
} }
void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) { void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid)
{
QString lcJid = bareJid.toLower(); QString lcJid = bareJid.toLower();
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid); std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
if (itr == contacts.end()) { if (itr == contacts.end()) {
@ -358,20 +394,22 @@ void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) {
Contact* contact = itr->second; Contact* contact = itr->second;
contacts.erase(itr); contacts.erase(itr);
QSet<QString> cGroups = contact->getGroups(); QSet<QString> cGroups = contact->getGroups();
for (const QString& group : cGroups) for (QSet<QString>::const_iterator itr = cGroups.begin(), end = cGroups.end(); itr != end; ++itr) {
removeFromGroup(lcJid, group); removeFromGroup(lcJid, *itr);
}
emit acc->removeContact(lcJid); emit acc->removeContact(lcJid);
contact->deleteLater(); contact->deleteLater();
} }
void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) { void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room)
{
qDebug() << "room" << room->jid() << "added with name" << room->name() qDebug() << "room" << room->jid() << "added with name" << room->name()
<< ", account" << acc->getName() << "joined:" << room->isJoined(); << ", account" << acc->getName() << "joined:" << room->isJoined();
} }
void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) { void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks)
{
QList<QXmppBookmarkConference> confs = bookmarks.conferences(); QList<QXmppBookmarkConference> confs = bookmarks.conferences();
for (QList<QXmppBookmarkConference>::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) { for (QList<QXmppBookmarkConference>::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) {
const QXmppBookmarkConference& c = *itr; const QXmppBookmarkConference& c = *itr;
@ -381,42 +419,54 @@ void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) {
if (cItr == conferences.end()) { if (cItr == conferences.end()) {
addNewRoom(jid, c.nickName(), c.name(), c.autoJoin()); addNewRoom(jid, c.nickName(), c.name(), c.autoJoin());
} else { } else {
if (c.autoJoin()) if (c.autoJoin()) {
cItr->second->setJoined(true); cItr->second->setJoined(true);
else } else {
cItr->second->setAutoJoin(false); cItr->second->setAutoJoin(false);
}
} }
} }
} }
void Core::RosterHandler::onMucJoinedChanged(bool joined){ void Core::RosterHandler::onMucJoinedChanged(bool joined)
{
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->changeRoom(room->jid, {{"joined", joined}}); emit acc->changeRoom(room->jid, {
{"joined", joined}
});
} }
void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) { void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin)
{
storeConferences(); storeConferences();
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->changeRoom(room->jid, {{"autoJoin", autoJoin}}); emit acc->changeRoom(room->jid, {
{"autoJoin", autoJoin}
});
} }
void Core::RosterHandler::onMucNickNameChanged(const QString& nickName){ void Core::RosterHandler::onMucNickNameChanged(const QString& nickName)
{
storeConferences(); storeConferences();
Conference* room = static_cast<Conference*>(sender()); Conference* room = static_cast<Conference*>(sender());
emit acc->changeRoom(room->jid, {{"nick", nickName}}); emit acc->changeRoom(room->jid, {
{"nick", nickName}
});
} }
Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs){ Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs)
{
Shared::SubscriptionState state; Shared::SubscriptionState state;
if (qs == QXmppRosterIq::Item::NotSet) if (qs == QXmppRosterIq::Item::NotSet) {
state = Shared::SubscriptionState::unknown; state = Shared::SubscriptionState::unknown;
else } else {
state = static_cast<Shared::SubscriptionState>(qs); state = static_cast<Shared::SubscriptionState>(qs);
}
return state; return state;
} }
void Core::RosterHandler::storeConferences() { void Core::RosterHandler::storeConferences()
{
QXmppBookmarkSet bms = acc->bm->bookmarks(); QXmppBookmarkSet bms = acc->bm->bookmarks();
QList<QXmppBookmarkConference> confs; QList<QXmppBookmarkConference> confs;
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
@ -432,7 +482,8 @@ void Core::RosterHandler::storeConferences() {
acc->bm->setBookmarks(bms); acc->bm->setBookmarks(bms);
} }
void Core::RosterHandler::clearConferences() { void Core::RosterHandler::clearConferences()
{
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) { for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) {
itr->second->deleteLater(); itr->second->deleteLater();
emit acc->removeRoom(itr->first); emit acc->removeRoom(itr->first);
@ -440,20 +491,21 @@ void Core::RosterHandler::clearConferences() {
conferences.clear(); conferences.clear();
} }
void Core::RosterHandler::removeRoomRequest(const QString& jid) { void Core::RosterHandler::removeRoomRequest(const QString& jid)
{
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
std::map<QString, Conference*>::const_iterator itr = conferences.find(lcJid); std::map<QString, Conference*>::const_iterator itr = conferences.find(lcJid);
if (itr == conferences.end()) if (itr == conferences.end()) {
qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping"; qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping";
}
itr->second->deleteLater(); itr->second->deleteLater();
conferences.erase(itr); conferences.erase(itr);
emit acc->removeRoom(lcJid); emit acc->removeRoom(lcJid);
storeConferences(); storeConferences();
} }
void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) { void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin)
SHARED_UNUSED(password); {
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
std::map<QString, Conference*>::const_iterator cItr = conferences.find(lcJid); std::map<QString, Conference*>::const_iterator cItr = conferences.find(lcJid);
if (cItr == conferences.end()) { if (cItr == conferences.end()) {
@ -464,7 +516,8 @@ void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick
} }
} }
void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) { void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName)
{
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid); std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
if (itr == contacts.end()) { if (itr == contacts.end()) {
@ -488,43 +541,45 @@ void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QSt
} }
} }
void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
{
QString lcJid = jid.toLower(); QString lcJid = jid.toLower();
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid); std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
if (itr == contacts.end()) { if (itr == contacts.end()) {
qDebug() << "An attempt to remove non existing contact" << lcJid << "of account" qDebug() << "An attempt to remove non existing contact" << lcJid << "of account"
<< acc->name << "from the group" << groupName << ", skipping"; << acc->name << "from the group" << groupName << ", skipping";
return;
}
QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
QSet<QString> groups = item.groups();
QSet<QString>::const_iterator gItr = groups.find(groupName);
if (gItr != groups.end()) {
groups.erase(gItr);
item.setGroups(groups);
QXmppRosterIq iq;
iq.setType(QXmppIq::Set);
iq.addItem(item);
acc->client.sendPacket(iq);
} else { } else {
qDebug() << "An attempt to remove contact" << lcJid << "of account" QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
<< acc->name << "from the group" << groupName << "but it's not in that group, skipping"; QSet<QString> groups = item.groups();
QSet<QString>::const_iterator gItr = groups.find(groupName);
if (gItr != groups.end()) {
groups.erase(gItr);
item.setGroups(groups);
QXmppRosterIq iq;
iq.setType(QXmppIq::Set);
iq.addItem(item);
acc->client.sendPacket(iq);
} else {
qDebug() << "An attempt to remove contact" << lcJid << "of account"
<< acc->name << "from the group" << groupName << "but it's not in that group, skipping";
}
} }
} }
void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) { void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path)
{
RosterItem* item = static_cast<RosterItem*>(sender()); RosterItem* item = static_cast<RosterItem*>(sender());
QMap<QString, QVariant> cData({ QMap<QString, QVariant> cData({
{"avatarState", QVariant::fromValue(type)}, {"avatarState", static_cast<uint>(type)},
{"avatarPath", path} {"avatarPath", path}
}); });
emit acc->changeContact(item->jid, cData); emit acc->changeContact(item->jid, cData);
} }
void Core::RosterHandler::handleOffline() { void Core::RosterHandler::handleOffline()
{
for (const std::pair<const QString, Conference*>& pair : conferences) { for (const std::pair<const QString, Conference*>& pair : conferences) {
pair.second->clearArchiveRequests(); pair.second->clearArchiveRequests();
pair.second->downgradeDatabaseState(); pair.second->downgradeDatabaseState();
@ -533,13 +588,13 @@ void Core::RosterHandler::handleOffline() {
pair.second->clearArchiveRequests(); pair.second->clearArchiveRequests();
pair.second->downgradeDatabaseState(); pair.second->downgradeDatabaseState();
} }
setPepSupport(false);
} }
void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) {
if (support == Shared::Support::supported) { void Core::RosterHandler::setPepSupport(bool support)
for (const std::pair<const QString, Contact*>& pair : contacts) { {
if (pair.second->getPepSupport() == Shared::Support::unknown) if (pepSupport != support) {
acc->dm->requestInfo(pair.first); pepSupport = support;
}
} }
} }

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_ROSTERHANDLER_H
#define CORE_ROSTERHANDLER_H
#include <QObject> #include <QObject>
#include <QSet> #include <QSet>
@ -26,24 +27,22 @@
#include <list> #include <list>
#include <map> #include <map>
#include <set> #include <set>
#include <optional>
#include <QXmppBookmarkSet.h> #include <QXmppBookmarkSet.h>
#include <QXmppMucManager.h> #include <QXmppMucManager.h>
#include <QXmppRosterIq.h> #include <QXmppRosterIq.h>
#include <shared/enums.h>
#include <shared/message.h> #include <shared/message.h>
#include <shared/trustsummary.h>
#include <core/contact.h> #include <core/contact.h>
#include <core/conference.h> #include <core/conference.h>
#include <core/delayManager/manager.h>
namespace Core { namespace Core {
class Account; class Account;
class RosterHandler : public QObject { class RosterHandler : public QObject
{
Q_OBJECT Q_OBJECT
public: public:
RosterHandler(Account* account); RosterHandler(Account* account);
@ -65,16 +64,13 @@ public:
void storeConferences(); void storeConferences();
void clearConferences(); void clearConferences();
void setPepSupport(bool support);
void initialize();
void clear();
private slots: private slots:
void onRosterReceived(); void onRosterReceived();
void onRosterItemAdded(const QString& bareJid); void onRosterItemAdded(const QString& bareJid);
void onRosterItemChanged(const QString& bareJid); void onRosterItemChanged(const QString& bareJid);
void onRosterItemRemoved(const QString& bareJid); void onRosterItemRemoved(const QString& bareJid);
void onTrustChanged(const QString& jid, const Shared::TrustSummary& trust);
void onMucRoomAdded(QXmppMucRoom* room); void onMucRoomAdded(QXmppMucRoom* room);
void onMucJoinedChanged(bool joined); void onMucJoinedChanged(bool joined);
@ -92,8 +88,6 @@ private slots:
void onContactNameChanged(const QString& name); void onContactNameChanged(const QString& name);
void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
void onContactAvatarChanged(Shared::Avatar, const QString& path); void onContactAvatarChanged(Shared::Avatar, const QString& path);
void onContactEncryptionChanged(Shared::EncryptionProtocol value);
void onPepSupportedChanged(Shared::Support support);
private: private:
void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin); void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin);
@ -103,6 +97,7 @@ private:
void handleNewRosterItem(Core::RosterItem* contact); void handleNewRosterItem(Core::RosterItem* contact);
void handleNewContact(Core::Contact* contact); void handleNewContact(Core::Contact* contact);
void handleNewConference(Core::Conference* contact); void handleNewConference(Core::Conference* contact);
void careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data);
static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs); static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs);
@ -113,6 +108,9 @@ private:
std::map<QString, std::set<QString>> groups; std::map<QString, std::set<QString>> groups;
std::map<QString, QString> queuedContacts; std::map<QString, QString> queuedContacts;
std::set<QString> outOfRosterContacts; std::set<QString> outOfRosterContacts;
bool pepSupport;
}; };
} }
#endif // CORE_ROSTERHANDLER_H

View File

@ -1,463 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "trusthandler.h"
#include "core/account.h"
#include "core/adapterfunctions.h"
Core::TrustHandler::TrustHandler(Account* account):
QObject(),
QXmppTrustStorage(),
acc(account),
db(acc->getName() + "/trust"),
protocols(db.createDirectory() + "/protocols"),
securityPolicies(db.addCache<QString, uint8_t>("securityPolicies")),
ownKeys(db.addCache<QString, QByteArray>("ownKeys")),
keysByProtocol()
{
if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) //never supposed to happen since I have just created a directory;
throw LMDBAL::Directory(protocols.fileName().toStdString());
QTextStream in(&protocols);
while(!in.atEnd()) {
QString protocol = in.readLine();
if (protocol.size() > 1) { //I'm afraid of reading some nonsence like separately standing \n or EF or BOM, so... let it be at least 2 chars long
KeyCache* cache = db.addCache<QString, Keys>(protocol.toStdString());
keysByProtocol.insert(std::make_pair(protocol, cache));
}
}
protocols.close();
db.open();
}
Core::TrustHandler::~TrustHandler() {
protocols.close();
db.close();
}
Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) {
std::map<QString, KeyCache*>::iterator itr = keysByProtocol.find(encryption);
if (itr == keysByProtocol.end())
return createNewCache(encryption);
else
return itr->second;
}
Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) {
db.close();
KeyCache* cache = db.addCache<QString, Keys>(encryption.toStdString());
keysByProtocol.insert(std::make_pair(encryption, cache));
if (!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
throw LMDBAL::Directory(protocols.fileName().toStdString());
QTextStream out(&protocols);
out << encryption + "\n";
protocols.close();
db.open();
return cache;
}
QXmppTask<void> Core::TrustHandler::resetAll(const QString& encryption) {
securityPolicies->removeRecord(encryption);
ownKeys->removeRecord(encryption);
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
if (itr == keysByProtocol.end())
return Core::makeReadyTask();
LMDBAL::WriteTransaction txn = db.beginTransaction();
KeyCache* cache = itr->second;
std::map<QString, Keys> keys = cache->readAll(txn);
cache->drop(txn);
txn.commit();
for (const std::pair<const QString, Keys>& pair : keys) {
bool empty = true;
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : pair.second) {
if (trust.second != Shared::TrustLevel::undecided) {
empty = false;
break;
}
}
if (!empty)
emit trustLevelsChanged(pair.first, getSummary(pair.first));
}
return Core::makeReadyTask();
}
QXmppTask<QXmpp::TrustLevel> Core::TrustHandler::trustLevel(
const QString& encryption,
const QString& keyOwnerJid,
const QByteArray& keyId
) {
KeyCache* cache = getCache(encryption);
Shared::TrustLevel level = Shared::TrustLevel::undecided;
try {
Keys map = cache->getRecord(keyOwnerJid);
Keys::const_iterator itr = map.find(keyId);
if (itr != map.end())
level = itr->second;
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask(std::move(convert(level)));
}
QXmppTask<QHash<QString, QMultiHash<QString, QByteArray>>> Core::TrustHandler::setTrustLevel(
const QString& encryption,
const QList<QString>& keyOwnerJids,
QXmpp::TrustLevel oldTrustLevel,
QXmpp::TrustLevel newTrustLevel
) {
QHash<QString, QMultiHash<QString, QByteArray>> modifiedKeys;
Shared::TrustLevel oldLevel = convert(oldTrustLevel);
Shared::TrustLevel newLevel = convert(newTrustLevel);
std::set<QString> modifiedJids;
KeyCache* cache = getCache(encryption);
LMDBAL::WriteTransaction txn = db.beginTransaction();
for (const QString& keyOwnerJid : keyOwnerJids) {
Keys map = cache->getRecord(keyOwnerJid, txn);
uint count = 0;
for (std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
Shared::TrustLevel& current = pair.second;
if (current == oldLevel) {
current = newLevel;
modifiedKeys[encryption].insert(keyOwnerJid, pair.first);
modifiedJids.insert(keyOwnerJid);
++count;
}
}
if (count > 0)
cache->changeRecord(keyOwnerJid, map, txn);
}
txn.commit();
for (const QString& jid : modifiedJids)
emit trustLevelsChanged(jid, getSummary(jid));
return Core::makeReadyTask(std::move(modifiedKeys));
}
QXmppTask<QHash<QString, QMultiHash<QString, QByteArray>>> Core::TrustHandler::setTrustLevel(
const QString& encryption,
const QMultiHash<QString, QByteArray>& keyIds,
QXmpp::TrustLevel trustLevel
) {
QHash<QString, QMultiHash<QString, QByteArray>> modifiedKeys;
Shared::TrustLevel level = convert(trustLevel);
std::set<QString> modifiedJids;
KeyCache* cache = getCache(encryption);
LMDBAL::WriteTransaction txn = db.beginTransaction();
for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) {
const QString& keyOwnerJid = itr.key();
const QByteArray& keyId = itr.value();
try {
Keys map = cache->getRecord(keyOwnerJid, txn);
std::pair<Keys::iterator, bool> result = map.insert(std::make_pair(keyId, level));
bool changed = result.second;
if (!changed && result.first->second != level) {
result.first->second = level;
changed = true;
}
if (changed) {
modifiedKeys[encryption].insert(keyOwnerJid, keyId);
modifiedJids.insert(keyOwnerJid);
cache->changeRecord(keyOwnerJid, map, txn);
}
} catch (const LMDBAL::NotFound& e) {
Keys map({{keyId, level}});
modifiedKeys[encryption].insert(keyOwnerJid, keyId);
modifiedJids.insert(keyOwnerJid);
cache->addRecord(keyOwnerJid, map, txn);
}
}
txn.commit();
for (const QString& jid : modifiedJids)
emit trustLevelsChanged(jid, getSummary(jid));
return Core::makeReadyTask(std::move(modifiedKeys));
}
QXmppTask<bool> Core::TrustHandler::hasKey(
const QString& encryption,
const QString& keyOwnerJid,
QXmpp::TrustLevels trustLevels
) {
KeyCache* cache = getCache(encryption);
bool found = false;
try {
Keys map = cache->getRecord(keyOwnerJid);
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
if (trustLevels.testFlag(convert(pair.second))) {
found = true;
break;
}
}
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask(std::move(found));
}
QXmppTask<QHash<QString, QHash<QByteArray, QXmpp::TrustLevel>>> Core::TrustHandler::keys(
const QString& encryption,
const QList<QString>& keyOwnerJids,
QXmpp::TrustLevels trustLevels
) {
HSHBTL res;
KeyCache* cache = getCache(encryption);
for (const QString& keyOwnerJid : keyOwnerJids) {
try {
Keys map = cache->getRecord(keyOwnerJid);
QHash<QByteArray, QXmpp::TrustLevel>& pRes = res[keyOwnerJid];
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
QXmpp::TrustLevel level = convert(pair.second);
if (!trustLevels || trustLevels.testFlag(level)) {
pRes.insert(pair.first, level);
}
}
} catch (const LMDBAL::NotFound& e) {}
}
return Core::makeReadyTask(std::move(res));
}
QXmppTask<QHash<QXmpp::TrustLevel, QMultiHash<QString, QByteArray>>> Core::TrustHandler::keys(
const QString& encryption,
QXmpp::TrustLevels trustLevels
) {
QHash<TL, MultySB> res;
KeyCache* cache = getCache(encryption);
std::map<QString, Keys> storage = cache->readAll();
for (const std::pair<const QString, Keys>& value : storage) {
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : value.second) {
QXmpp::TrustLevel level = convert(pair.second);
if (!trustLevels || trustLevels.testFlag(level))
res[level].insert(value.first, pair.first);
}
}
return Core::makeReadyTask(std::move(res));
}
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption) {
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
if (itr == keysByProtocol.end())
return Core::makeReadyTask();
LMDBAL::WriteTransaction txn = db.beginTransaction();
KeyCache* cache = itr->second;
std::map<QString, Keys> keys = cache->readAll(txn);
cache->drop(txn);
txn.commit();
for (const std::pair<const QString, Keys>& pair : keys) {
bool empty = true;
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : pair.second) {
if (trust.second != Shared::TrustLevel::undecided) {
empty = false;
break;
}
}
if (!empty)
emit trustLevelsChanged(pair.first, getSummary(pair.first));
}
return Core::makeReadyTask();
}
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) {
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
if (itr == keysByProtocol.end())
return Core::makeReadyTask();
KeyCache* cache = itr->second;
try {
cache->removeRecord(keyOwnerJid);
emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change
} catch (const LMDBAL::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys
return Core::makeReadyTask();
}
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption, const QList<QByteArray>& keyIds) {
std::set<QByteArray> set;
for (const QByteArray& keyId : keyIds)
set.insert(keyId);
LMDBAL::WriteTransaction txn = db.beginTransaction();
KeyCache* cache = getCache(encryption);
std::map<QString, Keys> data = cache->readAll(txn);
std::set<QString> modifiedJids;
for (std::map<QString, Keys>::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) {
Keys& byOwner = cItr->second;
for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) {
const QByteArray& keyId = itr->first;
if (set.erase(keyId)) {
byOwner.erase(itr++);
modifiedJids.insert(cItr->first);
} else
++itr;
}
if (byOwner.size() > 0)
data.erase(cItr++);
else
++cItr;
}
if (modifiedJids.size() > 0)
cache->replaceAll(data, txn);
txn.commit();
for (const QString& jid : modifiedJids)
emit trustLevelsChanged(jid, getSummary(jid));
return Core::makeReadyTask();
}
QXmppTask<void> Core::TrustHandler::addKeys(
const QString& encryption,
const QString& keyOwnerJid,
const QList<QByteArray>& keyIds,
QXmpp::TrustLevel trustLevel
) {
KeyCache* cache = getCache(encryption);
Shared::TrustLevel level = convert(trustLevel);
Keys data;
bool had = false;
LMDBAL::WriteTransaction txn = db.beginTransaction();
try {
data = cache->getRecord(keyOwnerJid, txn);
had = true;
} catch (const LMDBAL::NotFound& e) {}
for (const QByteArray& keyId : keyIds) {
std::pair<Keys::iterator, bool> result = data.insert(std::make_pair(keyId, level));
if (!result.second)
result.first->second = level;
}
if (had)
cache->changeRecord(keyOwnerJid, data, txn);
else
cache->addRecord(keyOwnerJid, data, txn);
txn.commit();
emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid));
return Core::makeReadyTask();
}
QXmppTask<QByteArray> Core::TrustHandler::ownKey(const QString& encryption) {
QByteArray res;
try {
res = ownKeys->getRecord(encryption);
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask(std::move(res));
}
QXmppTask<void> Core::TrustHandler::resetOwnKey(const QString& encryption) {
try {
ownKeys->removeRecord(encryption);
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask();
}
QXmppTask<void> Core::TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) {
ownKeys->forceRecord(encryption, keyId);
return Core::makeReadyTask();
}
QXmppTask<QXmpp::TrustSecurityPolicy> Core::TrustHandler::securityPolicy(const QString& encryption) {
QXmpp::TrustSecurityPolicy res;
try {
res = static_cast<QXmpp::TrustSecurityPolicy>(securityPolicies->getRecord(encryption));
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask(std::move(res));
}
QXmppTask<void> Core::TrustHandler::resetSecurityPolicy(const QString& encryption) {
try {
securityPolicies->removeRecord(encryption);
} catch (const LMDBAL::NotFound& e) {}
return Core::makeReadyTask();
}
QXmppTask<void> Core::TrustHandler::setSecurityPolicy(
const QString& encryption,
QXmpp::TrustSecurityPolicy securityPolicy)
{
uint8_t pol = securityPolicy;
securityPolicies->forceRecord(encryption, pol);
return Core::makeReadyTask();
}
Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const {
const QString& prt = Shared::TrustSummary::protocolKeys.at(protocol);
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(prt);
if (itr != keysByProtocol.end()) {
try {
Keys map = itr->second->getRecord(jid);
return map;
} catch (const LMDBAL::NotFound& e) {
return Keys();
}
} else {
return Keys();
}
}
Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const {
Shared::TrustSummary result;
for (const std::pair<const QString, KeyCache*>& pair : keysByProtocol) {
try {
Keys keys = pair.second->getRecord(jid);
Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first);
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : keys)
result.increment(protocol, trust.second);
} catch (const LMDBAL::NotFound& e) {}
}
return result;
}
Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) {
switch (level) {
case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided;
case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted;
case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::manuallyDistrusted;
case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted;
case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted;
case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated;
default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning
}
}
Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) {
switch (level) {
case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided;
case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted;
case Shared::TrustLevel::manuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted;
case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted;
case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted;
case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated;
default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning
}
}

View File

@ -1,89 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CORE_TRUSTHANDLER_H
#define CORE_TRUSTHANDLER_H
#include <shared/enums.h>
#include <shared/trustsummary.h>
#include <QXmppTrustStorage.h>
#include <cache.h>
namespace Core {
class Account;
class TrustHandler : public QObject, public QXmppTrustStorage {
Q_OBJECT
public:
TrustHandler(Account* account);
~TrustHandler();
signals:
void trustLevelsChanged(const QString& jid, const Shared::TrustSummary& summary) const;
public:
typedef QMultiHash<QString, QByteArray> MultySB;
typedef QHash<QString, MultySB> HashSM;
typedef const QList<QString>& CLSR;
typedef const QList<QByteArray>& CLBAR;
typedef const QString& CSR;
typedef QXmpp::TrustLevel TL;
typedef QHash<QString, QHash<QByteArray, TL>> HSHBTL;
typedef std::map<QByteArray, Shared::TrustLevel> Keys;
typedef LMDBAL::Cache<QString, Keys> KeyCache;
virtual QXmppTask<void> resetAll(CSR encryption) override;
virtual QXmppTask<TL> trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId) override;
virtual QXmppTask<HashSM> setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel) override;
virtual QXmppTask<HashSM> setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel) override;
virtual QXmppTask<bool> hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels) override;
virtual QXmppTask<HSHBTL> keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels) override;
virtual QXmppTask<QHash<TL, MultySB>> keys(CSR encryption, QXmpp::TrustLevels trustLevels) override;
virtual QXmppTask<void> removeKeys(CSR encryption) override;
virtual QXmppTask<void> removeKeys(CSR encryption, CSR keyOwnerJid) override;
virtual QXmppTask<void> removeKeys(CSR encryption, CLBAR keyIds) override;
virtual QXmppTask<void> addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel) override;
virtual QXmppTask<QByteArray> ownKey(CSR encryption) override;
virtual QXmppTask<void> resetOwnKey(CSR encryption) override;
virtual QXmppTask<void> setOwnKey(CSR encryption, const QByteArray& keyId) override;
virtual QXmppTask<QXmpp::TrustSecurityPolicy> securityPolicy(CSR encryption) override;
virtual QXmppTask<void> resetSecurityPolicy(CSR encryption) override;
virtual QXmppTask<void> setSecurityPolicy(const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) override;
static TL convert(Shared::TrustLevel level);
static Shared::TrustLevel convert(TL level);
Keys getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const;
Shared::TrustSummary getSummary(const QString& jid) const;
private:
KeyCache* createNewCache(const QString& encryption);
KeyCache* getCache(const QString& encryption);
private:
Account* acc;
LMDBAL::Base db;
QFile protocols;
LMDBAL::Cache<QString, uint8_t>* securityPolicies;
LMDBAL::Cache<QString, QByteArray>* ownKeys;
std::map<QString, KeyCache*> keysByProtocol;
};
}
#endif // CORE_TRUSTHANDLER_H

View File

@ -20,9 +20,15 @@
Core::VCardHandler::VCardHandler(Account* account): Core::VCardHandler::VCardHandler(Account* account):
QObject(), QObject(),
acc(account), acc(account),
ownVCardRequestInProgress(false),
pendingVCardRequests(),
avatarHash(), avatarHash(),
avatarType() avatarType()
{ {
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
//for some reason it doesn't work, launching from common handler
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + acc->name; path += "/" + acc->name;
QDir dir(path); QDir dir(path);
@ -61,15 +67,6 @@ Core::VCardHandler::VCardHandler(Account* account):
avatarType = type; avatarType = type;
} }
} }
}
Core::VCardHandler::~VCardHandler() {}
void Core::VCardHandler::initialize() {
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
//for some reason it doesn't work, launching from common handler
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
if (avatarType.size() != 0) { if (avatarType.size() != 0) {
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
acc->presence.setPhotoHash(avatarHash.toUtf8()); acc->presence.setPhotoHash(avatarHash.toUtf8());
@ -78,7 +75,13 @@ void Core::VCardHandler::initialize() {
} }
} }
void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { Core::VCardHandler::~VCardHandler()
{
}
void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card)
{
QString id = card.from(); QString id = card.from();
QStringList comps = id.split("/"); QStringList comps = id.split("/");
QString jid = comps.front().toLower(); QString jid = comps.front().toLower();
@ -86,23 +89,25 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) {
if (comps.size() > 1) { if (comps.size() > 1) {
resource = comps.back(); resource = comps.back();
} }
pendingVCardRequests.erase(id);
RosterItem* item = acc->rh->getRosterItem(jid); RosterItem* item = acc->rh->getRosterItem(jid);
if (item == nullptr) { if (item == 0) {
if (jid == acc->getBareJid()) if (jid == acc->getBareJid()) {
onOwnVCardReceived(card); onOwnVCardReceived(card);
else } else {
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
}
return; return;
} }
Shared::VCard vCard; Shared::VCard vCard = item->handleResponseVCard(card, resource);
item->handleResponseVCard(card, resource, vCard);
acc->delay->receivedVCard(id, vCard); emit acc->receivedVCard(jid, vCard);
} }
void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card)
{
QByteArray ava = card.photo(); QByteArray ava = card.photo();
bool avaChanged = false; bool avaChanged = false;
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/";
@ -181,6 +186,8 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) {
emit acc->changed(change); emit acc->changed(change);
} }
ownVCardRequestInProgress = false;
Shared::VCard vCard; Shared::VCard vCard;
initializeVCard(vCard, card); initializeVCard(vCard, card);
@ -191,30 +198,62 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) {
vCard.setAvatarType(Shared::Avatar::empty); vCard.setAvatarType(Shared::Avatar::empty);
} }
if (acc->delay->isOwnVCardPending()) emit acc->receivedVCard(acc->getBareJid(), vCard);
acc->delay->receivedOwnVCard(vCard);
} }
void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { void Core::VCardHandler::handleOffline()
if (!acc->delay->isOwnVCardPending()) { {
pendingVCardRequests.clear();
Shared::VCard vCard; //just to show, that there is now more pending request
for (const QString& jid : pendingVCardRequests) {
emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error
}
pendingVCardRequests.clear();
ownVCardRequestInProgress = false;
}
void Core::VCardHandler::requestVCard(const QString& jid)
{
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
qDebug() << "requesting vCard" << jid;
if (jid == acc->getBareJid()) {
if (!ownVCardRequestInProgress) {
acc->vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
} else {
acc->vm->requestVCard(jid);
pendingVCardRequests.insert(jid);
}
}
}
void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence)
{
if (!ownVCardRequestInProgress) {
switch (p_presence.vCardUpdateType()) { switch (p_presence.vCardUpdateType()) {
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
break; break;
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
break; break;
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
if (avatarType.size() > 0) if (avatarType.size() > 0) {
acc->delay->getOwnVCard(); acc->vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
break; break;
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
if (avatarHash != p_presence.photoHash()) if (avatarHash != p_presence.photoHash()) {
acc->delay->getOwnVCard(); acc->vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
break; break;
} }
} }
} }
void Core::VCardHandler::uploadVCard(const Shared::VCard& card) { void Core::VCardHandler::uploadVCard(const Shared::VCard& card)
{
QXmppVCardIq iq; QXmppVCardIq iq;
initializeQXmppVCard(iq, card); initializeQXmppVCard(iq, card);
@ -243,10 +282,11 @@ void Core::VCardHandler::uploadVCard(const Shared::VCard& card) {
} else { } else {
if (avatarType.size() > 0) { if (avatarType.size() > 0) {
QFile oA(oldPath); QFile oA(oldPath);
if (!oA.open(QFile::ReadOnly)) if (!oA.open(QFile::ReadOnly)) {
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
else } else {
data = oA.readAll(); data = oA.readAll();
}
} }
} }
@ -262,9 +302,11 @@ void Core::VCardHandler::uploadVCard(const Shared::VCard& card) {
onOwnVCardReceived(iq); onOwnVCardReceived(iq);
} }
QString Core::VCardHandler::getAvatarPath() const { QString Core::VCardHandler::getAvatarPath() const
if (avatarType.size() == 0) {
if (avatarType.size() == 0) {
return ""; return "";
else } else {
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType;
}
} }

View File

@ -42,12 +42,12 @@ public:
VCardHandler(Account* account); VCardHandler(Account* account);
~VCardHandler(); ~VCardHandler();
void handlePresenceOfMyAccountChange(const QXmppPresence& p_presence); void handleOffline();
void requestVCard(const QString& jid);
void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence);
void uploadVCard(const Shared::VCard& card); void uploadVCard(const Shared::VCard& card);
QString getAvatarPath() const; QString getAvatarPath() const;
void initialize();
private slots: private slots:
void onVCardReceived(const QXmppVCardIq& card); void onVCardReceived(const QXmppVCardIq& card);
void onOwnVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card);
@ -55,6 +55,8 @@ private slots:
private: private:
Account* acc; Account* acc;
bool ownVCardRequestInProgress;
std::set<QString> pendingVCardRequests;
QString avatarHash; QString avatarHash;
QString avatarType; QString avatarType;
}; };

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtCore/QDir> #include <QtCore/QDir>
@ -34,11 +35,13 @@ Core::NetworkAccess::NetworkAccess(QObject* parent):
currentPath = settings.value("downloadsPath").toString(); currentPath = settings.value("downloadsPath").toString();
} }
Core::NetworkAccess::~NetworkAccess() { Core::NetworkAccess::~NetworkAccess()
{
stop(); stop();
} }
void Core::NetworkAccess::downladFile(const QString& url) { void Core::NetworkAccess::downladFile(const QString& url)
{
std::map<QString, Transfer*>::iterator itr = downloads.find(url); std::map<QString, Transfer*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) { if (itr != downloads.end()) {
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping"; qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
@ -47,25 +50,27 @@ void Core::NetworkAccess::downladFile(const QString& url) {
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url); std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
if (p.first.size() > 0) { if (p.first.size() > 0) {
QFileInfo info(p.first); QFileInfo info(p.first);
if (info.exists() && info.isFile()) if (info.exists() && info.isFile()) {
emit downloadFileComplete(p.second, p.first); emit downloadFileComplete(p.second, p.first);
else } else {
startDownload(p.second, url); startDownload(p.second, url);
}
} else { } else {
startDownload(p.second, url); startDownload(p.second, url);
} }
} catch (const LMDBAL::NotFound& e) { } catch (const Archive::NotFound& e) {
qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway"; qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway";
storage.addFile(url); storage.addFile(url);
startDownload(std::list<Shared::MessageInfo>(), url); startDownload(std::list<Shared::MessageInfo>(), url);
} catch (const LMDBAL::Unknown& e) { } catch (const Archive::Unknown& e) {
qDebug() << "Error requesting file path:" << e.what(); qDebug() << "Error requesting file path:" << e.what();
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false); emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
} }
} }
} }
void Core::NetworkAccess::start() { void Core::NetworkAccess::start()
{
if (!running) { if (!running) {
manager = new QNetworkAccessManager(); manager = new QNetworkAccessManager();
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
@ -76,7 +81,8 @@ void Core::NetworkAccess::start() {
} }
} }
void Core::NetworkAccess::stop() { void Core::NetworkAccess::stop()
{
if (running) { if (running) {
storage.close(); storage.close();
manager->deleteLater(); manager->deleteLater();
@ -90,7 +96,8 @@ void Core::NetworkAccess::stop() {
} }
} }
void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url); std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
@ -108,7 +115,8 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
} }
} }
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
{
qDebug() << "DEBUG: DOWNLOAD ERROR"; qDebug() << "DEBUG: DOWNLOAD ERROR";
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
qDebug() << rpl->errorString(); qDebug() << rpl->errorString();
@ -126,11 +134,12 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) {
} }
} }
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors) { void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
{
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS"; qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
for (const QSslError& err : errors) for (const QSslError& err : errors) {
qDebug() << err.errorString(); qDebug() << err.errorString();
}
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url); std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
@ -145,7 +154,9 @@ void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors) {
} }
} }
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) {
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
{
QString errorText(""); QString errorText("");
switch (code) { switch (code) {
case QNetworkReply::NoError: case QNetworkReply::NoError:
@ -269,7 +280,8 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) {
} }
void Core::NetworkAccess::onDownloadFinished() { void Core::NetworkAccess::onDownloadFinished()
{
qDebug() << "DEBUG: DOWNLOAD FINISHED"; qDebug() << "DEBUG: DOWNLOAD FINISHED";
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
@ -284,11 +296,11 @@ void Core::NetworkAccess::onDownloadFinished() {
QStringList hops = url.split("/"); QStringList hops = url.split("/");
QString fileName = hops.back(); QString fileName = hops.back();
QString jid; QString jid;
if (dwn->messages.size() > 0) if (dwn->messages.size() > 0) {
jid = dwn->messages.front().jid; jid = dwn->messages.front().jid;
else } else {
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken"; qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
}
QString path = prepareDirectory(jid); QString path = prepareDirectory(jid);
if (path.size() > 0) { if (path.size() > 0) {
path = checkFileName(fileName, path); path = checkFileName(fileName, path);
@ -307,10 +319,11 @@ void Core::NetworkAccess::onDownloadFinished() {
err = "Couldn't prepare a directory for file"; err = "Couldn't prepare a directory for file";
} }
if (path.size() > 0) if (path.size() > 0) {
emit downloadFileComplete(dwn->messages, path); emit downloadFileComplete(dwn->messages, path);
else } else {
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false); emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
}
} }
dwn->reply->deleteLater(); dwn->reply->deleteLater();
@ -319,7 +332,8 @@ void Core::NetworkAccess::onDownloadFinished() {
} }
} }
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url) { void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url)
{
Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0}); Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0});
QNetworkRequest req(url); QNetworkRequest req(url);
dwn->reply = manager->get(req); dwn->reply = manager->get(req);
@ -335,7 +349,8 @@ void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& ms
emit loadFileProgress(dwn->messages, 0, false); emit loadFileProgress(dwn->messages, 0, false);
} }
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) { void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url); std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
@ -353,7 +368,8 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) {
} }
} }
void Core::NetworkAccess::onUploadFinished() { void Core::NetworkAccess::onUploadFinished()
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url); std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
@ -373,16 +389,20 @@ void Core::NetworkAccess::onUploadFinished() {
// Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder
bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath)); bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath));
if (copyResult)
upl->path = newPath; // Change storage if (copyResult) {
else // Change storage
upl->path = newPath;
} else {
err = "copying to " + newPath + " failed"; err = "copying to " + newPath + " failed";
}
} else { } else {
err = "Couldn't prepare a directory for file"; err = "Couldn't prepare a directory for file";
} }
if (err.size() != 0) if (err.size() != 0) {
qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err;
}
} }
storage.addFile(upl->messages, upl->url, upl->path); storage.addFile(upl->messages, upl->url, upl->path);
@ -397,7 +417,8 @@ void Core::NetworkAccess::onUploadFinished() {
} }
} }
void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) { void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url); std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
@ -415,12 +436,13 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
} }
} }
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) { QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
{
QString p = Shared::squawkifyPath(path); QString p = Shared::squawkifyPath(path);
try { try {
p = storage.getUrl(p); p = storage.getUrl(p);
} catch (const LMDBAL::NotFound& err) { } catch (const Archive::NotFound& err) {
p = ""; p = "";
} catch (...) { } catch (...) {
throw; throw;
@ -429,13 +451,8 @@ QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) {
return p; return p;
} }
void Core::NetworkAccess::uploadFile( void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
const Shared::MessageInfo& info, {
const QString& path,
const QUrl& put,
const QUrl& get,
const QMap<QString, QString> headers
) {
QFile* file = new QFile(path); QFile* file = new QFile(path);
Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file}); Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file});
QNetworkRequest req(put); QNetworkRequest req(put);
@ -462,18 +479,22 @@ void Core::NetworkAccess::uploadFile(
} }
} }
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) { void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id)
{
storage.addFile(url, account, jid, id); storage.addFile(url, account, jid, id);
std::map<QString, Transfer*>::iterator itr = downloads.find(url); std::map<QString, Transfer*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) if (itr != downloads.end()) {
itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay? itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay?
}
} }
void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) { void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
{
storage.addFile(url, path, account, jid, id); storage.addFile(url, path, account, jid, id);
} }
bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) { bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path)
{
for (const std::pair<const QString, Transfer*>& pair : uploads) { for (const std::pair<const QString, Transfer*>& pair : uploads) {
Transfer* info = pair.second; Transfer* info = pair.second;
if (pair.second->path == path) { if (pair.second->path == path) {
@ -495,7 +516,8 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri
return false; return false;
} }
QString Core::NetworkAccess::prepareDirectory(const QString& jid) { QString Core::NetworkAccess::prepareDirectory(const QString& jid)
{
QString path = currentPath; QString path = currentPath;
QString addition; QString addition;
if (jid.size() > 0) { if (jid.size() > 0) {
@ -507,23 +529,25 @@ QString Core::NetworkAccess::prepareDirectory(const QString& jid) {
if (!location.exists()) { if (!location.exists()) {
bool res = location.mkpath(path); bool res = location.mkpath(path);
if (!res) if (!res) {
return ""; return "";
else } else {
return "squawk://" + addition; return "squawk://" + addition;
}
} }
return "squawk://" + addition; return "squawk://" + addition;
} }
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) { QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
{
QStringList parts = name.split("."); QStringList parts = name.split(".");
QString suffix(""); QString suffix("");
QStringList::const_iterator sItr = parts.begin(); QStringList::const_iterator sItr = parts.begin();
QString realName = *sItr; QString realName = *sItr;
++sItr; ++sItr;
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
suffix += "." + (*sItr); suffix += "." + (*sItr);
}
QString postfix(""); QString postfix("");
QString resolvedPath = Shared::resolvePath(path); QString resolvedPath = Shared::resolvePath(path);
QString count(""); QString count("");
@ -538,15 +562,18 @@ QString Core::NetworkAccess::checkFileName(const QString& name, const QString& p
return path + QDir::separator() + realName + count + suffix; return path + QDir::separator() + realName + count + suffix;
} }
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) { QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
{
return storage.addMessageAndCheckForPath(url, account, jid, id); return storage.addMessageAndCheckForPath(url, account, jid, id);
} }
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path) { std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path)
{
return storage.deletedFile(path); return storage.deletedFile(path);
} }
void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { void Core::NetworkAccess::moveFilesDirectory(const QString& newPath)
{
QDir dir(currentPath); QDir dir(currentPath);
bool success = true; bool success = true;
qDebug() << "moving" << currentPath << "to" << newPath; qDebug() << "moving" << currentPath << "to" << newPath;
@ -555,8 +582,8 @@ void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) {
success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success;
} }
if (!success) if (!success) {
qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; qDebug() << "couldn't move downloads directory, most probably downloads will be broken";
}
currentPath = newPath; currentPath = newPath;
} }

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_NETWORKACCESS_H
#define CORE_NETWORKACCESS_H
#include <QObject> #include <QObject>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -29,13 +30,18 @@
#include <set> #include <set>
#include <shared/pathcheck.h> #include "storage/urlstorage.h"
#include "urlstorage.h" #include "shared/pathcheck.h"
namespace Core { namespace Core {
/**
* @todo write docs
*/
//TODO Need to describe how to get rid of records when file is no longer reachable; //TODO Need to describe how to get rid of records when file is no longer reachable;
class NetworkAccess : public QObject { class NetworkAccess : public QObject
{
Q_OBJECT Q_OBJECT
struct Transfer; struct Transfer;
public: public:
@ -98,3 +104,5 @@ private:
}; };
} }
#endif // CORE_NETWORKACCESS_H

View File

@ -2,7 +2,7 @@ if (WITH_KWALLET)
target_sources(squawk PRIVATE target_sources(squawk PRIVATE
kwallet.cpp kwallet.cpp
kwallet.h kwallet.h
) )
add_subdirectory(wrappers) add_subdirectory(wrappers)
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>) target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)

View File

@ -27,7 +27,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
account(pAccount), account(pAccount),
name(), name(),
archiveState(empty), archiveState(empty),
archive(new Archive(account, jid)), archive(new Archive(jid)),
syncronizing(false), syncronizing(false),
requestedCount(0), requestedCount(0),
requestedBefore(), requestedBefore(),
@ -38,36 +38,42 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
toCorrect(), toCorrect(),
muc(false) muc(false)
{ {
archive->open(); archive->open(account);
if (archive->size() != 0) { if (archive->size() != 0) {
if (archive->isFromTheBeginning()) if (archive->isFromTheBeginning()) {
archiveState = beginning; archiveState = beginning;
else } else {
archiveState = chunk; archiveState = chunk;
}
} }
} }
Core::RosterItem::~RosterItem() { Core::RosterItem::~RosterItem()
{
delete archive; delete archive;
} }
Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const { Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const
{
return archiveState; return archiveState;
} }
QString Core::RosterItem::getName() const { QString Core::RosterItem::getName() const
{
return name; return name;
} }
void Core::RosterItem::setName(const QString& n) { void Core::RosterItem::setName(const QString& n)
{
if (name != n) { if (name != n) {
name = n; name = n;
emit nameChanged(name); emit nameChanged(name);
} }
} }
void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) { void Core::RosterItem::addMessageToArchive(const Shared::Message& msg)
{
if (msg.storable()) { if (msg.storable()) {
hisoryCache.push_back(msg); hisoryCache.push_back(msg);
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(msg.getId()); std::map<QString, Shared::Message>::iterator itr = toCorrect.find(msg.getId());
@ -81,7 +87,8 @@ void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) {
} }
} }
void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) { void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg)
{
if (msg.storable()) { if (msg.storable()) {
QDateTime thisTime = msg.getTime(); QDateTime thisTime = msg.getTime();
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(originalId); std::map<QString, Shared::Message>::iterator itr = toCorrect.find(originalId);
@ -102,7 +109,8 @@ void Core::RosterItem::correctMessageInArchive(const QString& originalId, const
} }
} }
void Core::RosterItem::requestHistory(int count, const QString& before) { void Core::RosterItem::requestHistory(int count, const QString& before)
{
if (syncronizing) { if (syncronizing) {
requestCache.emplace_back(count, before); requestCache.emplace_back(count, before);
} else { } else {
@ -110,7 +118,8 @@ void Core::RosterItem::requestHistory(int count, const QString& before) {
} }
} }
void Core::RosterItem::nextRequest() { void Core::RosterItem::nextRequest()
{
if (syncronizing) { if (syncronizing) {
if (requestedCount != -1) { if (requestedCount != -1) {
bool last = false; bool last = false;
@ -126,8 +135,7 @@ void Core::RosterItem::nextRequest() {
last = true; last = true;
} }
} }
//} catch (const Archive::Empty& e) { } catch (const Archive::Empty& e) {
} catch (const LMDBAL::NotFound& e) {
last = true; last = true;
} }
} else if (archiveState == empty && responseCache.size() == 0) { } else if (archiveState == empty && responseCache.size() == 0) {
@ -149,7 +157,8 @@ void Core::RosterItem::nextRequest() {
} }
} }
void Core::RosterItem::performRequest(int count, const QString& before) { void Core::RosterItem::performRequest(int count, const QString& before)
{
syncronizing = true; syncronizing = true;
requestedCount = count; requestedCount = count;
requestedBefore = before; requestedBefore = before;
@ -169,8 +178,7 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
try { try {
Shared::Message msg = archive->newest(); Shared::Message msg = archive->newest();
emit needHistory("", getId(msg), msg.getTime()); emit needHistory("", getId(msg), msg.getTime());
//} catch (const Archive::Empty& e) { } catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example)
} catch (const LMDBAL::NotFound& e) { //this can happen when the only message in archive is not server stored (error, for example)
emit needHistory(before, ""); emit needHistory(before, "");
} }
} }
@ -188,14 +196,14 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true; found = true;
} catch (const LMDBAL::NotFound& e) { } catch (const Archive::NotFound& e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(getId(archive->oldest()), "");
} catch (const Archive::Empty& e) {
requestCache.emplace_back(requestedCount, before); requestCache.emplace_back(requestedCount, before);
requestedCount = -1; requestedCount = -1;
emit needHistory(getId(archive->oldest()), ""); emit needHistory(getId(archive->oldest()), "");
// } catch (const Archive::Empty& e) {
// requestCache.emplace_back(requestedCount, before);
// requestedCount = -1;
// emit needHistory(getId(archive->oldest()), "");
} }
if (found) { if (found) {
@ -228,17 +236,18 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
try { try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
} catch (const LMDBAL::NotFound& e) { } catch (const Archive::NotFound& e) {
qDebug("requesting id hasn't been found in archive, skipping");
} catch (const Archive::Empty& e) {
qDebug("requesting id hasn't been found in archive, skipping"); qDebug("requesting id hasn't been found in archive, skipping");
// } catch (const Archive::Empty& e) {
// qDebug("requesting id hasn't been found in archive, skipping");
} }
nextRequest(); nextRequest();
break; break;
} }
} }
QString Core::RosterItem::getId(const Shared::Message& msg) { QString Core::RosterItem::getId(const Shared::Message& msg)
{
QString id; QString id;
if (muc) { if (muc) {
id = msg.getStanzaId(); id = msg.getStanzaId();
@ -248,7 +257,8 @@ QString Core::RosterItem::getId(const Shared::Message& msg) {
return id; return id;
} }
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) { void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
{
if (msg.getId().size() > 0) { if (msg.getId().size() > 0) {
if (msg.storable()) { if (msg.storable()) {
switch (archiveState) { switch (archiveState) {
@ -289,7 +299,8 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) {
} }
} }
bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVariant>& data) { bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
{
bool found = false; bool found = false;
for (Shared::Message& msg : appendCache) { for (Shared::Message& msg : appendCache) {
if (msg.getId() == id) { if (msg.getId() == id) {
@ -313,7 +324,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVar
try { try {
archive->changeMessage(id, data); archive->changeMessage(id, data);
found = true; found = true;
} catch (const LMDBAL::NotFound& e) { } catch (const Archive::NotFound& e) {
qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found"; qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found";
} }
} }
@ -330,7 +341,8 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVar
return found; return found;
} }
void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId) { void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId)
{
unsigned int added(0); unsigned int added(0);
if (hisoryCache.size() > 0) { if (hisoryCache.size() > 0) {
added = archive->addElements(hisoryCache); added = archive->addElements(hisoryCache);
@ -389,8 +401,10 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true; found = true;
} catch (const LMDBAL::NotFound& e) { } catch (const Archive::NotFound& e) {
// } catch (const Archive::Empty& e) {
} catch (const Archive::Empty& e) {
} }
if (!found || requestedCount > int(responseCache.size())) { if (!found || requestedCount > int(responseCache.size())) {
if (archiveState == complete) { if (archiveState == complete) {
@ -415,43 +429,51 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
} }
} }
QString Core::RosterItem::getServer() const { QString Core::RosterItem::getServer() const
{
QStringList lst = jid.split("@"); QStringList lst = jid.split("@");
return lst.back(); return lst.back();
} }
bool Core::RosterItem::isMuc() const { bool Core::RosterItem::isMuc() const
{
return muc; return muc;
} }
QString Core::RosterItem::avatarPath(const QString& resource) const { QString Core::RosterItem::avatarPath(const QString& resource) const
{
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource); QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
return path; return path;
} }
QString Core::RosterItem::folderPath() const { QString Core::RosterItem::folderPath() const
{
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + account + "/" + jid; path += "/" + account + "/" + jid;
return path; return path;
} }
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
{
bool result = archive->setAvatar(data, info, false, resource); bool result = archive->setAvatar(data, info, false, resource);
if (resource.size() == 0 && result) { if (resource.size() == 0 && result) {
if (data.size() == 0) if (data.size() == 0) {
emit avatarChanged(Shared::Avatar::empty, ""); emit avatarChanged(Shared::Avatar::empty, "");
else } else {
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type); emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
}
} }
return result; return result;
} }
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) { bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
{
Archive::AvatarInfo info; Archive::AvatarInfo info;
return setAutoGeneratedAvatar(info, resource); return setAutoGeneratedAvatar(info, resource);
} }
bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) { bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource)
{
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied); QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
QPainter painter(&image); QPainter painter(&image);
quint8 colorIndex = rand() % Shared::colorPalette.size(); quint8 colorIndex = rand() % Shared::colorPalette.size();
@ -461,11 +483,11 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q
f.setBold(true); f.setBold(true);
f.setPixelSize(72); f.setPixelSize(72);
painter.setFont(f); painter.setFont(f);
if (bg.lightnessF() > 0.5) if (bg.lightnessF() > 0.5) {
painter.setPen(Qt::black); painter.setPen(Qt::black);
else } else {
painter.setPen(Qt::white); painter.setPen(Qt::white);
}
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper()); painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper());
QByteArray arr; QByteArray arr;
QBuffer stream(&arr); QBuffer stream(&arr);
@ -473,22 +495,25 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q
image.save(&stream, "PNG"); image.save(&stream, "PNG");
stream.close(); stream.close();
bool result = archive->setAvatar(arr, info, true, resource); bool result = archive->setAvatar(arr, info, true, resource);
if (resource.size() == 0 && result) if (resource.size() == 0 && result) {
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png"); emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
}
return result; return result;
} }
bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const { bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const
{
return archive->readAvatarInfo(target, resource); return archive->readAvatarInfo(target, resource);
} }
void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) { Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
{
Archive::AvatarInfo info; Archive::AvatarInfo info;
Archive::AvatarInfo newInfo; Archive::AvatarInfo newInfo;
bool hasAvatar = readAvatarInfo(info, resource); bool hasAvatar = readAvatarInfo(info, resource);
QByteArray ava = card.photo(); QByteArray ava = card.photo();
Shared::VCard vCard;
initializeVCard(vCard, card); initializeVCard(vCard, card);
Shared::Avatar type = Shared::Avatar::empty; Shared::Avatar type = Shared::Avatar::empty;
QString path = ""; QString path = "";
@ -508,9 +533,9 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri
} }
} }
} else { } else {
if (!hasAvatar || !info.autogenerated) if (!hasAvatar || !info.autogenerated) {
setAutoGeneratedAvatar(resource); setAutoGeneratedAvatar(resource);
}
type = Shared::Avatar::autocreated; type = Shared::Avatar::autocreated;
path = avatarPath(resource) + ".png"; path = avatarPath(resource) + ".png";
} }
@ -518,11 +543,15 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri
vCard.setAvatarType(type); vCard.setAvatarType(type);
vCard.setAvatarPath(path); vCard.setAvatarPath(path);
if (resource.size() == 0) if (resource.size() == 0) {
emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath()); emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath());
}
return vCard;
} }
void Core::RosterItem::clearArchiveRequests() { void Core::RosterItem::clearArchiveRequests()
{
syncronizing = false; syncronizing = false;
requestedCount = 0; requestedCount = 0;
requestedBefore = ""; requestedBefore = "";
@ -538,72 +567,30 @@ void Core::RosterItem::clearArchiveRequests() {
requestCache.clear(); requestCache.clear();
} }
void Core::RosterItem::downgradeDatabaseState() { void Core::RosterItem::downgradeDatabaseState()
if (archiveState == ArchiveState::complete) {
if (archiveState == ArchiveState::complete) {
archiveState = ArchiveState::beginning; archiveState = ArchiveState::beginning;
}
if (archiveState == ArchiveState::end) {
if (archiveState == ArchiveState::end)
archiveState = ArchiveState::chunk; archiveState = ArchiveState::chunk;
}
} }
Shared::Message Core::RosterItem::getMessage(const QString& id) { Shared::Message Core::RosterItem::getMessage(const QString& id)
{
for (const Shared::Message& msg : appendCache) { for (const Shared::Message& msg : appendCache) {
if (msg.getId() == id) if (msg.getId() == id) {
return msg; return msg;
}
} }
for (Shared::Message& msg : hisoryCache) { for (Shared::Message& msg : hisoryCache) {
if (msg.getId() == id) if (msg.getId() == id) {
return msg; return msg;
}
} }
return archive->getElement(id); return archive->getElement(id);
} }
Shared::EncryptionProtocol Core::RosterItem::encryption() const {
return archive->encryption();
}
void Core::RosterItem::setEncryption(Shared::EncryptionProtocol value) {
bool changed = archive->setEncryption(value);
if (changed)
emit encryptionChanged(value);
}
QMap<QString, QVariant> Core::RosterItem::getInfo() const {
QMap<QString, QVariant> result({
{"name", name},
{"encryption", QVariant::fromValue(encryption())},
});
Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info);
careAboutAvatar(hasAvatar, info, result);
return result;
}
void Core::RosterItem::careAboutAvatar (
bool hasAvatar,
const Archive::AvatarInfo& info,
QMap<QString, QVariant>& output,
const QString& resource,
const QString& subject
) const {
if (hasAvatar) {
if (info.autogenerated)
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
else
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
output.insert("avatarPath", avatarPath(resource) + "." + info.type);
} else {
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
output.insert("avatarPath", "");
if (subject.size() == 0)
emit requestVCard(jid);
else
emit requestVCard(subject);
}
}

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_ROSTERITEM_H
#define CORE_ROSTERITEM_H
#include <QObject> #include <QObject>
#include <QString> #include <QString>
@ -33,7 +34,7 @@
#include "shared/enums.h" #include "shared/enums.h"
#include "shared/message.h" #include "shared/message.h"
#include "shared/vcard.h" #include "shared/vcard.h"
#include "components/archive.h" #include "storage/archive.h"
#include "adapterfunctions.h" #include "adapterfunctions.h"
namespace Core { namespace Core {
@ -61,8 +62,6 @@ public:
void setName(const QString& n); void setName(const QString& n);
QString getServer() const; QString getServer() const;
bool isMuc() const; bool isMuc() const;
Shared::EncryptionProtocol encryption() const;
void setEncryption(Shared::EncryptionProtocol value);
void addMessageToArchive(const Shared::Message& msg); void addMessageToArchive(const Shared::Message& msg);
void correctMessageInArchive(const QString& originalId, const Shared::Message& msg); void correctMessageInArchive(const QString& originalId, const Shared::Message& msg);
@ -73,24 +72,22 @@ public:
QString folderPath() const; QString folderPath() const;
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const; bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
virtual bool setAutoGeneratedAvatar(const QString& resource = ""); virtual bool setAutoGeneratedAvatar(const QString& resource = "");
virtual void handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& out); virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
virtual void handlePresence(const QXmppPresence& pres) = 0; virtual void handlePresence(const QXmppPresence& pres) = 0;
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data); bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
void clearArchiveRequests(); void clearArchiveRequests();
void downgradeDatabaseState(); void downgradeDatabaseState();
virtual QMap<QString, QVariant> getInfo() const;
Shared::Message getMessage(const QString& id); Shared::Message getMessage(const QString& id);
signals: signals:
void nameChanged(const QString& name) const; void nameChanged(const QString& name);
void subscriptionStateChanged(Shared::SubscriptionState state) const; void subscriptionStateChanged(Shared::SubscriptionState state);
void historyResponse(const std::list<Shared::Message>& messages, bool last) const; void historyResponse(const std::list<Shared::Message>& messages, bool last);
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()) const; void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
void avatarChanged(Shared::Avatar, const QString& path) const; void avatarChanged(Shared::Avatar, const QString& path);
void requestVCard(const QString& jid) const; void requestVCard(const QString& jid);
void encryptionChanged(Shared::EncryptionProtocol value) const;
public: public:
const QString jid; const QString jid;
@ -99,13 +96,6 @@ public:
protected: protected:
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = ""); virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = ""); virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
void careAboutAvatar(
bool hasAvatar,
const Archive::AvatarInfo& info,
QMap<QString, QVariant>& output,
const QString& resource = "",
const QString& subject = ""
) const;
protected: protected:
QString name; QString name;
@ -129,3 +119,5 @@ private:
}; };
} }
#endif // CORE_ROSTERITEM_H

View File

@ -21,8 +21,6 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include "shared/defines.h"
int SignalCatcher::sigintFd[2] = {0,0}; int SignalCatcher::sigintFd[2] = {0,0};
SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
@ -30,10 +28,14 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
app(p_app) app(p_app)
{ {
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd)) if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
{
qFatal("Couldn't create INT socketpair"); qFatal("Couldn't create INT socketpair");
}
if (setup_unix_signal_handlers() != 0) if (setup_unix_signal_handlers() != 0)
{
qFatal("Couldn't install unix handlers"); qFatal("Couldn't install unix handlers");
}
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this); snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt); connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt);
@ -42,25 +44,25 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
SignalCatcher::~SignalCatcher() SignalCatcher::~SignalCatcher()
{} {}
void SignalCatcher::handleSigInt() { void SignalCatcher::handleSigInt()
{
snInt->setEnabled(false); snInt->setEnabled(false);
char tmp; char tmp;
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp)); ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
SHARED_UNUSED(s);
emit interrupt(); emit interrupt();
snInt->setEnabled(true); snInt->setEnabled(true);
} }
void SignalCatcher::intSignalHandler(int unused) { void SignalCatcher::intSignalHandler(int unused)
{
char a = 1; char a = 1;
ssize_t s = ::write(sigintFd[0], &a, sizeof(a)); ssize_t s = ::write(sigintFd[0], &a, sizeof(a));
SHARED_UNUSED(s);
SHARED_UNUSED(unused);
} }
int SignalCatcher::setup_unix_signal_handlers() { int SignalCatcher::setup_unix_signal_handlers()
{
struct sigaction s_int; struct sigaction s_int;
s_int.sa_handler = SignalCatcher::intSignalHandler; s_int.sa_handler = SignalCatcher::intSignalHandler;

View File

@ -28,10 +28,9 @@ Core::Squawk::Squawk(QObject* parent):
amap(), amap(),
state(Shared::Availability::offline), state(Shared::Availability::offline),
network(), network(),
isInitialized(false), isInitialized(false)
clientCache(),
#ifdef WITH_KWALLET #ifdef WITH_KWALLET
kwallet() ,kwallet()
#endif #endif
{ {
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
@ -50,7 +49,8 @@ Core::Squawk::Squawk(QObject* parent):
#endif #endif
} }
Core::Squawk::~Squawk() { Core::Squawk::~Squawk()
{
Accounts::const_iterator itr = accounts.begin(); Accounts::const_iterator itr = accounts.begin();
Accounts::const_iterator end = accounts.end(); Accounts::const_iterator end = accounts.end();
for (; itr != end; ++itr) { for (; itr != end; ++itr) {
@ -58,14 +58,15 @@ Core::Squawk::~Squawk() {
} }
} }
void Core::Squawk::onWalletOpened(bool success) { void Core::Squawk::onWalletOpened(bool success)
{
qDebug() << "KWallet opened: " << success; qDebug() << "KWallet opened: " << success;
} }
void Core::Squawk::stop() { void Core::Squawk::stop()
{
qDebug("Stopping squawk core.."); qDebug("Stopping squawk core..");
network.stop(); network.stop();
clientCache.close();
if (isInitialized) { if (isInitialized) {
QSettings settings; QSettings settings;
@ -107,16 +108,17 @@ void Core::Squawk::stop() {
emit quit(); emit quit();
} }
void Core::Squawk::start() { void Core::Squawk::start()
{
qDebug("Starting squawk core.."); qDebug("Starting squawk core..");
readSettings(); readSettings();
isInitialized = true; isInitialized = true;
network.start(); network.start();
clientCache.open();
} }
void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map) { void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
{
QString name = map.value("name").toString(); QString name = map.value("name").toString();
QString login = map.value("login").toString(); QString login = map.value("login").toString();
QString server = map.value("server").toString(); QString server = map.value("server").toString();
@ -175,12 +177,10 @@ void Core::Squawk::addAccount(
connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence); connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence);
connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence); connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence);
connect(acc, &Account::infoReady, this, &Squawk::responseInfo); connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError); connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
connect(acc, &Account::infoDiscovered, this, &Squawk::onAccountInfoDiscovered);
QMap<QString, QVariant> map = { QMap<QString, QVariant> map = {
{"login", login}, {"login", login},
{"server", server}, {"server", server},
@ -200,28 +200,31 @@ void Core::Squawk::addAccount(
switch (passwordType) { switch (passwordType) {
case Shared::AccountPassword::alwaysAsk: case Shared::AccountPassword::alwaysAsk:
case Shared::AccountPassword::kwallet: case Shared::AccountPassword::kwallet:
if (password == "") if (password == "") {
acc->invalidatePassword(); acc->invalidatePassword();
break;
break; }
default: default:
break; break;
} }
if (state != Shared::Availability::offline) { if (state != Shared::Availability::offline) {
acc->setAvailability(state); acc->setAvailability(state);
if (acc->getActive()) if (acc->getActive()) {
acc->connect(); acc->connect();
}
} }
} }
void Core::Squawk::changeState(Shared::Availability p_state) { void Core::Squawk::changeState(Shared::Availability p_state)
{
if (state != p_state) { if (state != p_state) {
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
Account* acc = *itr; Account* acc = *itr;
acc->setAvailability(p_state); acc->setAvailability(p_state);
if (state == Shared::Availability::offline && acc->getActive()) if (state == Shared::Availability::offline && acc->getActive()) {
acc->connect(); acc->connect();
}
} }
state = p_state; state = p_state;
@ -229,18 +232,21 @@ void Core::Squawk::changeState(Shared::Availability p_state) {
} }
} }
void Core::Squawk::connectAccount(const QString& account) { void Core::Squawk::connectAccount(const QString& account)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to connect non existing account, skipping"); qDebug("An attempt to connect non existing account, skipping");
return; return;
} }
itr->second->setActive(true); itr->second->setActive(true);
if (state != Shared::Availability::offline) if (state != Shared::Availability::offline) {
itr->second->connect(); itr->second->connect();
}
} }
void Core::Squawk::disconnectAccount(const QString& account) { void Core::Squawk::disconnectAccount(const QString& account)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to connect non existing account, skipping"); qDebug("An attempt to connect non existing account, skipping");
@ -251,15 +257,13 @@ void Core::Squawk::disconnectAccount(const QString& account) {
itr->second->disconnect(); itr->second->disconnect();
} }
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) { void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
QMap<QString, QVariant> changes = { emit changeAccount(acc->getName(), {
{"state", QVariant::fromValue(p_state)} {"state", QVariant::fromValue(p_state)},
}; {"error", ""}
if (acc->getLastError() == Account::Error::none) });
changes.insert("error", "");
emit changeAccount(acc->getName(), changes);
#ifdef WITH_KWALLET #ifdef WITH_KWALLET
if (p_state == Shared::ConnectionState::connected) { if (p_state == Shared::ConnectionState::connected) {
@ -270,85 +274,74 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
#endif #endif
} }
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit addContact(acc->getName(), jid, group, data); emit addContact(acc->getName(), jid, group, data);
} }
void Core::Squawk::onAccountAddGroup(const QString& name) { void Core::Squawk::onAccountAddGroup(const QString& name)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit addGroup(acc->getName(), name); emit addGroup(acc->getName(), name);
} }
void Core::Squawk::onAccountRemoveGroup(const QString& name) { void Core::Squawk::onAccountRemoveGroup(const QString& name)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeGroup(acc->getName(), name); emit removeGroup(acc->getName(), name);
} }
void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeContact(acc->getName(), jid, data); emit changeContact(acc->getName(), jid, data);
} }
void Core::Squawk::onAccountRemoveContact(const QString& jid) { void Core::Squawk::onAccountRemoveContact(const QString& jid)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeContact(acc->getName(), jid); emit removeContact(acc->getName(), jid);
} }
void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) { void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeContact(acc->getName(), jid, group); emit removeContact(acc->getName(), jid, group);
} }
void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
Account* acc = static_cast<Account*>(sender());
emit addPresence(acc->getName(), jid, name, data);
//it's equal if a MUC sends its status with presence of the same jid (ex: muc@srv.im/muc@srv.im), it's not a client, so, no need to request
if (jid != name) {
const Shared::ClientId& id = data["client"].value<Shared::ClientId>();
if (!id.valid())
return;
if (!clientCache.checkClient(id))
acc->discoverInfo(jid + "/" + name, id.getId());
}
}
void Core::Squawk::onAccountInfoDiscovered(
const QString& address,
const QString& node,
const std::set<Shared::Identity>& identities,
const std::set<QString>& features)
{ {
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit addPresence(acc->getName(), jid, name, data);
if (!clientCache.registerClientInfo(address, node, identities, features)) {
qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node;
}
} }
void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) { void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removePresence(acc->getName(), jid, name); emit removePresence(acc->getName(), jid, name);
} }
void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) { void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}}); emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}});
} }
void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeAccount(acc->getName(), data); emit changeAccount(acc->getName(), data);
} }
void Core::Squawk::onAccountMessage(const Shared::Message& data) { void Core::Squawk::onAccountMessage(const Shared::Message& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit accountMessage(acc->getName(), data); emit accountMessage(acc->getName(), data);
} }
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) { void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to send a message with non existing account" << account << ", skipping"; qDebug() << "An attempt to send a message with non existing account" << account << ", skipping";
@ -358,7 +351,8 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
itr->second->sendMessage(data); itr->second->sendMessage(data);
} }
void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) { void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping"; qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping";
@ -368,7 +362,8 @@ void Core::Squawk::replaceMessage(const QString& account, const QString& origina
itr->second->replaceMessage(originalId, data); itr->second->replaceMessage(originalId, data);
} }
void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) { void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping"; qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping";
@ -378,7 +373,8 @@ void Core::Squawk::resendMessage(const QString& account, const QString& jid, con
itr->second->resendMessage(jid, id); itr->second->resendMessage(jid, id);
} }
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) { void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to request an archive of non existing account, skipping"); qDebug("An attempt to request an archive of non existing account, skipping");
@ -387,12 +383,14 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in
itr->second->requestArchive(jid, count, before); itr->second->requestArchive(jid, count, before);
} }
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last) { void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit responseArchive(acc->getName(), jid, list, last); emit responseArchive(acc->getName(), jid, list, last);
} }
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map) { void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
{
AccountsMap::const_iterator itr = amap.find(name); AccountsMap::const_iterator itr = amap.find(name);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to modify non existing account, skipping"); qDebug("An attempt to modify non existing account, skipping");
@ -442,24 +440,29 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
} }
mItr = map.find("login"); mItr = map.find("login");
if (mItr != map.end()) if (mItr != map.end()) {
acc->setLogin(mItr->toString()); acc->setLogin(mItr->toString());
}
mItr = map.find("password"); mItr = map.find("password");
if (mItr != map.end()) if (mItr != map.end()) {
acc->setPassword(mItr->toString()); acc->setPassword(mItr->toString());
}
mItr = map.find("resource"); mItr = map.find("resource");
if (mItr != map.end()) if (mItr != map.end()) {
acc->setResource(mItr->toString()); acc->setResource(mItr->toString());
}
mItr = map.find("server"); mItr = map.find("server");
if (mItr != map.end()) if (mItr != map.end()) {
acc->setServer(mItr->toString()); acc->setServer(mItr->toString());
}
mItr = map.find("passwordType"); mItr = map.find("passwordType");
if (mItr != map.end()) if (mItr != map.end()) {
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt())); acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
}
#ifdef WITH_KWALLET #ifdef WITH_KWALLET
if (acc->getPasswordType() == Shared::AccountPassword::kwallet if (acc->getPasswordType() == Shared::AccountPassword::kwallet
@ -471,24 +474,28 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
#endif #endif
if (state != Shared::Availability::offline) { if (state != Shared::Availability::offline) {
if (activeChanged && acc->getActive()) if (activeChanged && acc->getActive()) {
acc->connect(); acc->connect();
else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) } else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) {
acc->connect(); acc->connect();
}
} }
emit changeAccount(name, map); emit changeAccount(name, map);
} }
void Core::Squawk::onAccountError(const QString& text) { void Core::Squawk::onAccountError(const QString& text)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeAccount(acc->getName(), {{"error", text}}); emit changeAccount(acc->getName(), {{"error", text}});
if (acc->getLastError() == Account::Error::authentication) if (acc->getLastError() == Account::Error::authentication) {
emit requestPassword(acc->getName(), true); emit requestPassword(acc->getName(), true);
}
} }
void Core::Squawk::removeAccountRequest(const QString& name) { void Core::Squawk::removeAccountRequest(const QString& name)
{
AccountsMap::const_iterator itr = amap.find(name); AccountsMap::const_iterator itr = amap.find(name);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to remove non existing account " << name << " from core, skipping"; qDebug() << "An attempt to remove non existing account " << name << " from core, skipping";
@ -496,8 +503,9 @@ void Core::Squawk::removeAccountRequest(const QString& name) {
} }
Account* acc = itr->second; Account* acc = itr->second;
if (acc->getState() != Shared::ConnectionState::disconnected) if (acc->getState() != Shared::ConnectionState::disconnected) {
acc->disconnect(); acc->disconnect();
}
for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) { for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) {
if (*aItr == acc) { if (*aItr == acc) {
@ -517,7 +525,8 @@ void Core::Squawk::removeAccountRequest(const QString& name) {
acc->deleteLater(); acc->deleteLater();
} }
void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) { void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to subscribe to the contact with non existing account, skipping"); qDebug("An attempt to subscribe to the contact with non existing account, skipping");
@ -527,7 +536,8 @@ void Core::Squawk::subscribeContact(const QString& account, const QString& jid,
itr->second->subscribeToContact(jid, reason); itr->second->subscribeToContact(jid, reason);
} }
void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) { void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to subscribe to the contact with non existing account, skipping"); qDebug("An attempt to subscribe to the contact with non existing account, skipping");
@ -537,7 +547,8 @@ void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid
itr->second->unsubscribeFromContact(jid, reason); itr->second->unsubscribeFromContact(jid, reason);
} }
void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) { void Core::Squawk::removeContactRequest(const QString& account, const QString& jid)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to remove contact from non existing account, skipping"); qDebug("An attempt to remove contact from non existing account, skipping");
@ -547,7 +558,8 @@ void Core::Squawk::removeContactRequest(const QString& account, const QString& j
itr->second->removeContactRequest(jid); itr->second->removeContactRequest(jid);
} }
void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups) { void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug("An attempt to add contact to a non existing account, skipping"); qDebug("An attempt to add contact to a non existing account, skipping");
@ -557,22 +569,26 @@ void Core::Squawk::addContactRequest(const QString& account, const QString& jid,
itr->second->addContactRequest(jid, name, groups); itr->second->addContactRequest(jid, name, groups);
} }
void Core::Squawk::onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit addRoom(acc->getName(), jid, data); emit addRoom(acc->getName(), jid, data);
} }
void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeRoom(acc->getName(), jid, data); emit changeRoom(acc->getName(), jid, data);
} }
void Core::Squawk::onAccountRemoveRoom(const QString jid) { void Core::Squawk::onAccountRemoveRoom(const QString jid)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeRoom(acc->getName(), jid); emit removeRoom(acc->getName(), jid);
} }
void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) { void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping"; qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping";
@ -581,7 +597,8 @@ void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, boo
itr->second->setRoomJoined(jid, joined); itr->second->setRoomJoined(jid, joined);
} }
void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) { void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping"; qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping";
@ -590,36 +607,32 @@ void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, b
itr->second->setRoomAutoJoin(jid, joined); itr->second->setRoomAutoJoin(jid, joined);
} }
void Core::Squawk::setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value) { void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data)
AccountsMap::const_iterator itr = amap.find(account); {
if (itr == amap.end()) {
qDebug() << "An attempt to set encryption to the contact" << jid << "of non existing account" << account << ", skipping";
return;
}
itr->second->setContactEncryption(jid, value);
}
void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data) {
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit addRoomParticipant(acc->getName(), jid, nick, data); emit addRoomParticipant(acc->getName(), jid, nick, data);
} }
void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeRoomParticipant(acc->getName(), jid, nick, data); emit changeRoomParticipant(acc->getName(), jid, nick, data);
} }
void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) { void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit removeRoomParticipant(acc->getName(), jid, nick); emit removeRoomParticipant(acc->getName(), jid, nick);
} }
void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data) { void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit changeMessage(acc->getName(), jid, id, data); emit changeMessage(acc->getName(), jid, id, data);
} }
void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) { void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping"; qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping";
@ -628,7 +641,8 @@ void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid)
itr->second->removeRoomRequest(jid); itr->second->removeRoomRequest(jid);
} }
void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) { void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping"; qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping";
@ -637,11 +651,13 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co
itr->second->addRoomRequest(jid, nick, password, autoJoin); itr->second->addRoomRequest(jid, nick, password, autoJoin);
} }
void Core::Squawk::fileDownloadRequest(const QString& url) { void Core::Squawk::fileDownloadRequest(const QString& url)
{
network.downladFile(url); network.downladFile(url);
} }
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) { void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
@ -650,7 +666,8 @@ void Core::Squawk::addContactToGroupRequest(const QString& account, const QStrin
itr->second->addContactToGroupRequest(jid, groupName); itr->second->addContactToGroupRequest(jid, groupName);
} }
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) { void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
@ -659,7 +676,8 @@ void Core::Squawk::removeContactFromGroupRequest(const QString& account, const Q
itr->second->removeContactFromGroupRequest(jid, groupName); itr->second->removeContactFromGroupRequest(jid, groupName);
} }
void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) { void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping"; qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping";
@ -668,25 +686,28 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j
itr->second->renameContactRequest(jid, newName); itr->second->renameContactRequest(jid, newName);
} }
void Core::Squawk::requestInfo(const QString& account, const QString& jid) { void Core::Squawk::requestVCard(const QString& account, const QString& jid)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to request info about" << jid << "of non existing account" << account << ", skipping"; qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping";
return; return;
} }
itr->second->requestInfo(jid); itr->second->requestVCard(jid);
} }
void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) { void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to update info to non existing account" << account << ", skipping"; qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping";
return; return;
} }
itr->second->updateInfo(info); itr->second->uploadVCard(card);
} }
void Core::Squawk::readSettings() { void Core::Squawk::readSettings()
{
QSettings settings; QSettings settings;
settings.beginGroup("core"); settings.beginGroup("core");
int size = settings.beginReadArray("accounts"); int size = settings.beginReadArray("accounts");
@ -720,7 +741,8 @@ void Core::Squawk::readSettings() {
emit ready(); emit ready();
} }
void Core::Squawk::onAccountNeedPassword() { void Core::Squawk::onAccountNeedPassword()
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
switch (acc->getPasswordType()) { switch (acc->getPasswordType()) {
case Shared::AccountPassword::alwaysAsk: case Shared::AccountPassword::alwaysAsk:
@ -743,11 +765,13 @@ void Core::Squawk::onAccountNeedPassword() {
} }
} }
void Core::Squawk::onWalletRejectPassword(const QString& login) { void Core::Squawk::onWalletRejectPassword(const QString& login)
{
emit requestPassword(login, false); emit requestPassword(login, false);
} }
void Core::Squawk::responsePassword(const QString& account, const QString& password) { void Core::Squawk::responsePassword(const QString& account, const QString& password)
{
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) { if (itr == amap.end()) {
qDebug() << "An attempt to set password to non existing account" << account << ", skipping"; qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
@ -756,19 +780,24 @@ void Core::Squawk::responsePassword(const QString& account, const QString& passw
Account* acc = itr->second; Account* acc = itr->second;
acc->setPassword(password); acc->setPassword(password);
emit changeAccount(account, {{"password", password}}); emit changeAccount(account, {{"password", password}});
if (state != Shared::Availability::offline && acc->getActive()) if (state != Shared::Availability::offline && acc->getActive()) {
acc->connect(); acc->connect();
}
} }
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) { void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
{
Account* acc = static_cast<Account*>(sender()); Account* acc = static_cast<Account*>(sender());
emit fileError({{acc->getName(), jid, id}}, errorText, true); emit fileError({{acc->getName(), jid, id}}, errorText, true);
} }
void Core::Squawk::onLocalPathInvalid(const QString& path) { void Core::Squawk::onLocalPathInvalid(const QString& path)
{
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path); std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
QMap<QString, QVariant> data({{"attachPath", ""}}); QMap<QString, QVariant> data({
{"attachPath", ""}
});
for (const Shared::MessageInfo& info : list) { for (const Shared::MessageInfo& info : list) {
AccountsMap::const_iterator itr = amap.find(info.account); AccountsMap::const_iterator itr = amap.find(info.account);
if (itr != amap.end()) { if (itr != amap.end()) {
@ -779,7 +808,8 @@ void Core::Squawk::onLocalPathInvalid(const QString& path) {
} }
} }
void Core::Squawk::changeDownloadsPath(const QString& path) { void Core::Squawk::changeDownloadsPath(const QString& path)
{
network.moveFilesDirectory(path); network.moveFilesDirectory(path);
} }

View File

@ -31,13 +31,9 @@
#include "shared/enums.h" #include "shared/enums.h"
#include "shared/message.h" #include "shared/message.h"
#include "shared/global.h" #include "shared/global.h"
#include "shared/info.h" #include "networkaccess.h"
#include "shared/clientinfo.h"
#include "external/simpleCrypt/simplecrypt.h" #include "external/simpleCrypt/simplecrypt.h"
#include <core/components/clientcache.h>
#include <core/components/networkaccess.h>
#ifdef WITH_KWALLET #ifdef WITH_KWALLET
#include "passwordStorageEngines/kwallet.h" #include "passwordStorageEngines/kwallet.h"
#endif #endif
@ -88,7 +84,7 @@ signals:
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path); void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path); void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
void responseInfo(const Shared::Info& info); 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 changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
void requestPassword(const QString& account, bool authernticationError); void requestPassword(const QString& account, bool authernticationError);
@ -116,7 +112,6 @@ public slots:
void removeContactRequest(const QString& account, const QString& jid); void removeContactRequest(const QString& account, const QString& jid);
void renameContactRequest(const QString& account, const QString& jid, const QString& newName); void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups); void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
void setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value);
void setRoomJoined(const QString& account, const QString& jid, bool joined); void setRoomJoined(const QString& account, const QString& jid, bool joined);
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
@ -125,8 +120,8 @@ public slots:
void fileDownloadRequest(const QString& url); void fileDownloadRequest(const QString& url);
void requestInfo(const QString& account, const QString& jid); void requestVCard(const QString& account, const QString& jid);
void updateInfo(const QString& account, const Shared::Info& info); void uploadVCard(const QString& account, const Shared::VCard& card);
void responsePassword(const QString& account, const QString& password); void responsePassword(const QString& account, const QString& password);
void onLocalPathInvalid(const QString& path); void onLocalPathInvalid(const QString& path);
void changeDownloadsPath(const QString& path); void changeDownloadsPath(const QString& path);
@ -140,7 +135,6 @@ private:
Shared::Availability state; Shared::Availability state;
NetworkAccess network; NetworkAccess network;
bool isInitialized; bool isInitialized;
ClientCache clientCache;
#ifdef WITH_KWALLET #ifdef WITH_KWALLET
PSE::KWallet kwallet; PSE::KWallet kwallet;
@ -185,10 +179,17 @@ private slots:
void onWalletOpened(bool success); void onWalletOpened(bool success);
void onWalletRejectPassword(const QString& login); void onWalletRejectPassword(const QString& login);
void onAccountInfoDiscovered(const QString& address, const QString& node, const std::set<Shared::Identity>& identities, const std::set<QString>& features);
private: private:
void readSettings(); void readSettings();
void parseAccount(
const QString& login,
const QString& server,
const QString& password,
const QString& name,
const QString& resource,
bool active,
Shared::AccountPassword passwordType
);
static const quint64 passwordHash = 0x08d054225ac4871d; static const quint64 passwordHash = 0x08d054225ac4871d;
}; };

View File

@ -0,0 +1,8 @@
target_sources(squawk PRIVATE
archive.cpp
archive.h
storage.cpp
storage.h
urlstorage.cpp
urlstorage.h
)

1007
core/storage/archive.cpp Normal file

File diff suppressed because it is too large Load Diff

197
core/storage/archive.h Normal file
View File

@ -0,0 +1,197 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_ARCHIVE_H
#define CORE_ARCHIVE_H
#include <QObject>
#include <QCryptographicHash>
#include <QMimeDatabase>
#include <QMimeType>
#include "shared/message.h"
#include "shared/exception.h"
#include <lmdb.h>
#include <list>
namespace Core {
class Archive : public QObject
{
Q_OBJECT
public:
class AvatarInfo;
Archive(const QString& jid, QObject* parent = 0);
~Archive();
void open(const QString& account);
void close();
bool addElement(const Shared::Message& message);
unsigned int addElements(const std::list<Shared::Message>& messages);
Shared::Message getElement(const QString& id) const;
bool hasElement(const QString& id) const;
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
Shared::Message oldest();
QString oldestId();
Shared::Message newest();
QString newestId();
void clear();
long unsigned int size() const;
std::list<Shared::Message> getBefore(int count, const QString& id);
bool isFromTheBeginning();
void setFromTheBeginning(bool is);
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
AvatarInfo getAvatarInfo(const QString& resource = "") const;
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
QString idByStanzaId(const QString& stanzaId) const;
QString stanzaIdById(const QString& id) const;
public:
const QString jid;
public:
class Directory:
public Utils::Exception
{
public:
Directory(const std::string& p_path):Exception(), path(p_path){}
std::string getMessage() const{return "Can't create directory for database at " + path;}
private:
std::string path;
};
class Closed:
public Utils::Exception
{
public:
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
private:
std::string operation;
std::string account;
};
class NotFound:
public Utils::Exception
{
public:
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
private:
std::string key;
std::string account;
};
class Empty:
public Utils::Exception
{
public:
Empty(const std::string& acc):Exception(), account(acc){}
std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";}
private:
std::string account;
};
class Exist:
public Utils::Exception
{
public:
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
private:
std::string account;
std::string key;
};
class NoAvatar:
public Utils::Exception
{
public:
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
if (resource.size() == 0) {
resource = "for himself";
}
}
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
private:
std::string element;
std::string resource;
};
class Unknown:
public Utils::Exception
{
public:
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
private:
std::string account;
std::string msg;
};
class AvatarInfo {
public:
AvatarInfo();
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
void deserialize(char* pointer, uint32_t size);
void serialize(QByteArray* ba) const;
QString type;
QByteArray hash;
bool autogenerated;
};
private:
bool opened;
bool fromTheBeginning;
MDB_env* environment;
MDB_dbi main; //id to message
MDB_dbi order; //time to id
MDB_dbi stats;
MDB_dbi avatars;
MDB_dbi sid; //stanzaId to id
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const;
void printOrder();
void printKeys();
bool dropAvatar(const std::string& resource);
Shared::Message getMessage(const std::string& id, MDB_txn* txn) const;
Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc);
Shared::Message edge(bool end);
};
}
#endif // CORE_ARCHIVE_H

184
core/storage/storage.cpp Normal file
View File

@ -0,0 +1,184 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QStandardPaths>
#include <QDir>
#include "storage.h"
Core::Storage::Storage(const QString& p_name):
name(p_name),
opened(false),
environment(),
base()
{
}
Core::Storage::~Storage()
{
close();
}
void Core::Storage::open()
{
if (!opened) {
mdb_env_create(&environment);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name;
QDir cache(path);
if (!cache.exists()) {
bool res = cache.mkpath(path);
if (!res) {
throw Archive::Directory(path.toStdString());
}
}
mdb_env_set_maxdbs(environment, 1);
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
mdb_txn_commit(txn);
opened = true;
}
}
void Core::Storage::close()
{
if (opened) {
mdb_dbi_close(environment, base);
mdb_env_close(environment);
opened = false;
}
}
void Core::Storage::addRecord(const QString& key, const QString& value)
{
if (!opened) {
throw Archive::Closed("addRecord", name.toStdString());
}
const std::string& id = key.toStdString();
const std::string& val = value.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
lmdbData.mv_size = val.size();
lmdbData.mv_data = (char*)val.c_str();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
if (rc != 0) {
mdb_txn_abort(txn);
if (rc == MDB_KEYEXIST) {
throw Archive::Exist(name.toStdString(), id);
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
void Core::Storage::changeRecord(const QString& key, const QString& value)
{
if (!opened) {
throw Archive::Closed("changeRecord", name.toStdString());
}
const std::string& id = key.toStdString();
const std::string& val = value.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
lmdbData.mv_size = val.size();
lmdbData.mv_data = (char*)val.c_str();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
if (rc != 0) {
mdb_txn_abort(txn);
if (rc) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
QString Core::Storage::getRecord(const QString& key) const
{
if (!opened) {
throw Archive::Closed("addElement", name.toStdString());
}
const std::string& id = key.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
MDB_txn *txn;
int rc;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
if (rc) {
mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) {
throw Archive::NotFound(id, name.toStdString());
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
QString value(sId.c_str());
mdb_txn_abort(txn);
return value;
}
}
void Core::Storage::removeRecord(const QString& key)
{
if (!opened) {
throw Archive::Closed("addElement", name.toStdString());
}
const std::string& id = key.toStdString();
MDB_val lmdbKey;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
MDB_txn *txn;
int rc;
mdb_txn_begin(environment, NULL, 0, &txn);
rc = mdb_del(txn, base, &lmdbKey, NULL);
if (rc) {
mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) {
throw Archive::NotFound(id, name.toStdString());
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Squawk messenger. * Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me> * Copyright (C) 2019 Yury Gubich <blue@macaw.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -16,23 +16,41 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_STORAGE_H
#define CORE_STORAGE_H
#include <QString> #include <QString>
#include <lmdb.h>
#include "job.h" #include "archive.h"
namespace Core { namespace Core {
namespace DelayManager {
class Contact : public virtual Job {
protected:
Contact(Id id, const QString& jid, Type type);
Contact(const Contact& other);
/**
* @todo write docs
*/
class Storage
{
public: public:
const QString jid; Storage(const QString& name);
~Storage();
void open();
void close();
void addRecord(const QString& key, const QString& value);
void changeRecord(const QString& key, const QString& value);
void removeRecord(const QString& key);
QString getRecord(const QString& key) const;
private:
QString name;
bool opened;
MDB_env* environment;
MDB_dbi base;
}; };
} }
}
#endif // CORE_STORAGE_H

491
core/storage/urlstorage.cpp Normal file
View File

@ -0,0 +1,491 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include "urlstorage.h"
Core::UrlStorage::UrlStorage(const QString& p_name):
name(p_name),
opened(false),
environment(),
base(),
map()
{
}
Core::UrlStorage::~UrlStorage()
{
close();
}
void Core::UrlStorage::open()
{
if (!opened) {
mdb_env_create(&environment);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name;
QDir cache(path);
if (!cache.exists()) {
bool res = cache.mkpath(path);
if (!res) {
throw Archive::Directory(path.toStdString());
}
}
mdb_env_set_maxdbs(environment, 2);
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
mdb_dbi_open(txn, "map", MDB_CREATE, &map);
mdb_txn_commit(txn);
opened = true;
}
}
void Core::UrlStorage::close()
{
if (opened) {
mdb_dbi_close(environment, map);
mdb_dbi_close(environment, base);
mdb_env_close(environment);
opened = false;
}
}
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite)
{
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
try {
writeInfo(key, info, txn, overwrite);
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
}
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite)
{
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
info.serialize(ds);
const std::string& id = key.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
lmdbData.mv_size = ba.size();
lmdbData.mv_data = (uint8_t*)ba.data();
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE);
if (rc != 0) {
if (rc == MDB_KEYEXIST) {
if (!overwrite) {
throw Archive::Exist(name.toStdString(), id);
}
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
}
if (info.hasPath()) {
std::string sp = info.getPath().toStdString();
lmdbData.mv_size = sp.size();
lmdbData.mv_data = (char*)sp.c_str();
rc = mdb_put(txn, map, &lmdbData, &lmdbKey, 0);
if (rc != 0) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
}
}
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info, MDB_txn* txn)
{
const std::string& id = key.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
int rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
if (rc == 0) {
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
QDataStream ds(&ba, QIODevice::ReadOnly);
info.deserialize(ds);
} else if (rc == MDB_NOTFOUND) {
throw Archive::NotFound(id, name.toStdString());
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
}
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info)
{
MDB_txn *txn;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
try {
readInfo(key, info, txn);
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
}
void Core::UrlStorage::addFile(const QString& url)
{
if (!opened) {
throw Archive::Closed("addFile(no message, no path)", name.toStdString());
}
addToInfo(url, "", "", "");
}
void Core::UrlStorage::addFile(const QString& url, const QString& path)
{
if (!opened) {
throw Archive::Closed("addFile(no message, with path)", name.toStdString());
}
addToInfo(url, "", "", "", path);
}
void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id)
{
if (!opened) {
throw Archive::Closed("addFile(with message, no path)", name.toStdString());
}
addToInfo(url, account, jid, id);
}
void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
{
if (!opened) {
throw Archive::Closed("addFile(with message, with path)", name.toStdString());
}
addToInfo(url, account, jid, id, path);
}
void Core::UrlStorage::addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
{
if (!opened) {
throw Archive::Closed("addFile(with list)", name.toStdString());
}
UrlInfo info (path, msgs);
writeInfo(url, info, true);;
}
QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
{
if (!opened) {
throw Archive::Closed("addMessageAndCheckForPath", name.toStdString());
}
return addToInfo(url, account, jid, id).getPath();
}
Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path)
{
UrlInfo info;
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
try {
readInfo(url, info, txn);
} catch (const Archive::NotFound& e) {
} catch (...) {
mdb_txn_abort(txn);
throw;
}
bool pathChange = false;
bool listChange = false;
if (path != "-s") {
if (info.getPath() != path) {
info.setPath(path);
pathChange = true;
}
}
if (account.size() > 0 && jid.size() > 0 && id.size() > 0) {
listChange = info.addMessage(account, jid, id);
}
if (pathChange || listChange) {
try {
writeInfo(url, info, txn, true);
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
} else {
mdb_txn_abort(txn);
}
return info;
}
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path)
{
std::list<Shared::MessageInfo> list;
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
UrlInfo info;
try {
readInfo(url, info, txn);
info.getMessages(list);
} catch (const Archive::NotFound& e) {
} catch (...) {
mdb_txn_abort(txn);
throw;
}
info.setPath(path);
try {
writeInfo(url, info, txn, true);
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
return list;
}
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url)
{
std::list<Shared::MessageInfo> list;
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
UrlInfo info;
try {
std::string id = url.toStdString();
readInfo(url, info, txn);
info.getMessages(list);
MDB_val lmdbKey;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
int rc = mdb_del(txn, base, &lmdbKey, NULL);
if (rc != 0) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
if (info.hasPath()) {
std::string path = info.getPath().toStdString();
lmdbKey.mv_size = path.size();
lmdbKey.mv_data = (char*)path.c_str();
int rc = mdb_del(txn, map, &lmdbKey, NULL);
if (rc != 0) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
}
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
return list;
}
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path)
{
std::list<Shared::MessageInfo> list;
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
try {
std::string spath = path.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = spath.size();
lmdbKey.mv_data = (char*)spath.c_str();
QString url;
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
if (rc == 0) {
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
url = QString(surl.c_str());
} else if (rc == MDB_NOTFOUND) {
qDebug() << "Have been asked to remove file" << path << ", which isn't in the database, skipping";
mdb_txn_abort(txn);
return list;
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
UrlInfo info;
std::string id = url.toStdString();
readInfo(url, info, txn);
info.getMessages(list);
info.setPath(QString());
writeInfo(url, info, txn, true);
rc = mdb_del(txn, map, &lmdbKey, NULL);
if (rc != 0) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
mdb_txn_commit(txn);
} catch (...) {
mdb_txn_abort(txn);
throw;
}
return list;
}
QString Core::UrlStorage::getUrl(const QString& path)
{
std::list<Shared::MessageInfo> list;
MDB_txn *txn;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
std::string spath = path.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = spath.size();
lmdbKey.mv_data = (char*)spath.c_str();
QString url;
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
if (rc == 0) {
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
url = QString(surl.c_str());
mdb_txn_abort(txn);
return url;
} else if (rc == MDB_NOTFOUND) {
mdb_txn_abort(txn);
throw Archive::NotFound(spath, name.toStdString());
} else {
mdb_txn_abort(txn);
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
}
std::pair<QString, std::list<Shared::MessageInfo>> Core::UrlStorage::getPath(const QString& url)
{
UrlInfo info;
readInfo(url, info);
std::list<Shared::MessageInfo> container;
info.getMessages(container);
return std::make_pair(info.getPath(), container);
}
Core::UrlStorage::UrlInfo::UrlInfo():
localPath(),
messages() {}
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path):
localPath(path),
messages() {}
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs):
localPath(path),
messages(msgs) {}
Core::UrlStorage::UrlInfo::~UrlInfo() {}
bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id)
{
for (const Shared::MessageInfo& info : messages) {
if (info.account == acc && info.jid == jid && info.messageId == id) {
return false;
}
}
messages.emplace_back(acc, jid, id);
return true;
}
void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const
{
data << localPath;
std::list<Shared::MessageInfo>::size_type size = messages.size();
data << quint32(size);
for (const Shared::MessageInfo& info : messages) {
data << info.account;
data << info.jid;
data << info.messageId;
}
}
void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data)
{
data >> localPath;
quint32 size;
data >> size;
for (quint32 i = 0; i < size; ++i) {
messages.emplace_back();
Shared::MessageInfo& info = messages.back();
data >> info.account;
data >> info.jid;
data >> info.messageId;
}
}
void Core::UrlStorage::UrlInfo::getMessages(std::list<Shared::MessageInfo>& container) const
{
for (const Shared::MessageInfo& info : messages) {
container.emplace_back(info);
}
}
QString Core::UrlStorage::UrlInfo::getPath() const
{
return localPath;
}
bool Core::UrlStorage::UrlInfo::hasPath() const
{
return localPath.size() > 0;
}
void Core::UrlStorage::UrlInfo::setPath(const QString& path)
{
localPath = path;
}

View File

@ -16,23 +16,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef CORE_URLSTORAGE_H
#define CORE_URLSTORAGE_H
#include <QString> #include <QString>
#include <QDataStream> #include <QDataStream>
#include <lmdb.h>
#include <list> #include <list>
#include <storage.h> #include "archive.h"
#include <shared/messageinfo.h> #include <shared/messageinfo.h>
namespace Core { namespace Core {
class UrlStorage { /**
public: * @todo write docs
*/
class UrlStorage
{
class UrlInfo; class UrlInfo;
public: public:
UrlStorage(const QString& name); UrlStorage(const QString& name);
~UrlStorage(); ~UrlStorage();
@ -53,16 +55,20 @@ public:
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url); std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
private: private:
LMDBAL::Base base; QString name;
LMDBAL::Storage<QString, UrlInfo>* urlToInfo; bool opened;
LMDBAL::Storage<QString, QString>* pathToUrl; MDB_env* environment;
MDB_dbi base;
MDB_dbi map;
private: private:
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false); void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
void writeInfo(const QString& key, const UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite = false); void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false);
void readInfo(const QString& key, UrlInfo& info);
void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn);
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s"); UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
public: private:
class UrlInfo { class UrlInfo {
public: public:
UrlInfo(const QString& path); UrlInfo(const QString& path);
@ -90,5 +96,4 @@ public:
} }
QDataStream& operator >> (QDataStream &in, Core::UrlStorage::UrlInfo& info); #endif // CORE_URLSTORAGE_H
QDataStream& operator << (QDataStream &out, const Core::UrlStorage::UrlInfo& info);

1
external/lmdbal vendored

@ -1 +0,0 @@
Subproject commit d62eddc47edbec9f8c071459e045578f61ab58df

2
external/qxmpp vendored

@ -1 +1 @@
Subproject commit 0cd7379bd78aa01af7e84f2fad6269ef0c0ba49c Subproject commit fe83e9c3d42c3becf682e2b5ecfc9d77b24c614f

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.0)
project(simplecrypt LANGUAGES CXX) project(simplecrypt LANGUAGES CXX)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)

View File

@ -1,14 +1,7 @@
set(SOURCE_FILES target_sources(squawk PRIVATE
main.cpp main.cpp
application.cpp application.cpp
dialogqueue.cpp
root.cpp
)
set(HEADER_FILES
application.h application.h
dialogqueue.cpp
dialogqueue.h dialogqueue.h
root.h
) )
target_sources(squawk PRIVATE ${SOURCE_FILES})

View File

@ -50,7 +50,6 @@ Application::Application(Core::Squawk* p_core):
connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage); connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage);
connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage); connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage);
connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage); connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage);
connect(this, &Application::setEncryption, core, &Core::Squawk::setContactEncryption);
connect(&roster, &Models::Roster::requestArchive, connect(&roster, &Models::Roster::requestArchive,
std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3)); std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3));
@ -121,7 +120,8 @@ Application::Application(Core::Squawk* p_core):
Application::~Application() {} Application::~Application() {}
void Application::quit() { void Application::quit()
{
if (!nowQuitting) { if (!nowQuitting) {
nowQuitting = true; nowQuitting = true;
emit quitting(); emit quitting();
@ -135,27 +135,32 @@ void Application::quit() {
conversations.clear(); conversations.clear();
dialogueQueue.quit(); dialogueQueue.quit();
if (squawk != nullptr) if (squawk != nullptr) {
squawk->close(); squawk->close();
}
if (trayIcon != nullptr) { if (trayIcon != nullptr) {
trayIcon->deleteLater(); trayIcon->deleteLater();
trayIcon = nullptr; trayIcon = nullptr;
} }
if (!destroyingSquawk) if (!destroyingSquawk) {
checkForTheLastWindow(); checkForTheLastWindow();
}
} }
} }
void Application::checkForTheLastWindow() { void Application::checkForTheLastWindow()
if (QApplication::topLevelWidgets().size() > 0) {
if (QApplication::topLevelWidgets().size() > 0) {
emit readyToQuit(); emit readyToQuit();
else } else {
connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit); connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit);
}
} }
void Application::createMainWindow() { void Application::createMainWindow()
{
if (squawk == nullptr) { if (squawk == nullptr) {
squawk = new Squawk(roster); squawk = new Squawk(roster);
@ -183,11 +188,11 @@ void Application::createMainWindow() {
connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest); connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest);
connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest); connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest);
connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest); connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest);
connect(squawk, &Squawk::requestInfo, core, &Core::Squawk::requestInfo); connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard);
connect(squawk, &Squawk::updateInfo, core, &Core::Squawk::updateInfo); connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard);
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath); connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
connect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
dialogueQueue.setParentWidnow(squawk); dialogueQueue.setParentWidnow(squawk);
squawk->stateChanged(availability); squawk->stateChanged(availability);
@ -197,8 +202,9 @@ void Application::createMainWindow() {
for (const std::list<QString>& entry : expandedPaths) { for (const std::list<QString>& entry : expandedPaths) {
QModelIndex ind = roster.getIndexByPath(entry); QModelIndex ind = roster.getIndexByPath(entry);
if (ind.isValid()) if (ind.isValid()) {
squawk->expand(ind); squawk->expand(ind);
}
} }
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded); connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
@ -206,11 +212,12 @@ void Application::createMainWindow() {
} }
} }
void Application::onSquawkClosing() { void Application::onSquawkClosing()
{
dialogueQueue.setParentWidnow(nullptr); dialogueQueue.setParentWidnow(nullptr);
if (!nowQuitting) { if (!nowQuitting) {
disconnect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
} }
destroyingSquawk = true; destroyingSquawk = true;
@ -230,15 +237,18 @@ void Application::onSquawkClosing() {
void Application::onSquawkDestroyed() { void Application::onSquawkDestroyed() {
destroyingSquawk = false; destroyingSquawk = false;
if (nowQuitting) if (nowQuitting) {
checkForTheLastWindow(); checkForTheLastWindow();
}
} }
void Application::onChangeTray(bool enabled, bool hide) { void Application::onChangeTray(bool enabled, bool hide)
{
if (enabled) { if (enabled) {
if (trayIcon == nullptr) { if (trayIcon == nullptr) {
if (!hide || squawk == nullptr) if (!hide || squawk == nullptr) {
createTrayIcon(); createTrayIcon();
}
} else { } else {
if (hide && squawk != nullptr) { if (hide && squawk != nullptr) {
trayIcon->deleteLater(); trayIcon->deleteLater();
@ -251,7 +261,8 @@ void Application::onChangeTray(bool enabled, bool hide) {
} }
} }
void Application::createTrayIcon() { void Application::createTrayIcon()
{
trayIcon = new QSystemTrayIcon(); trayIcon = new QSystemTrayIcon();
QMenu* trayIconMenu = new QMenu(); QMenu* trayIconMenu = new QMenu();
@ -268,7 +279,8 @@ void Application::createTrayIcon() {
trayIcon->show(); trayIcon->show();
} }
void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) { void Application::trayClicked(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) { switch (reason) {
case QSystemTrayIcon::Trigger: case QSystemTrayIcon::Trigger:
case QSystemTrayIcon::DoubleClick: case QSystemTrayIcon::DoubleClick:
@ -280,7 +292,8 @@ void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) {
} }
} }
void Application::toggleSquawk() { void Application::toggleSquawk()
{
QSettings settings; QSettings settings;
if (squawk == nullptr) { if (squawk == nullptr) {
createMainWindow(); createMainWindow();
@ -295,27 +308,34 @@ void Application::toggleSquawk() {
} }
} }
void Application::onItemCollapsed(const QModelIndex& index) { void Application::onItemCollapsed(const QModelIndex& index)
{
std::list<QString> address = roster.getItemPath(index); std::list<QString> address = roster.getItemPath(index);
if (address.size() > 0) if (address.size() > 0) {
expandedPaths.erase(address); expandedPaths.erase(address);
}
void Application::onItemExpanded(const QModelIndex& index) {
std::list<QString> address = roster.getItemPath(index);
if (address.size() > 0)
expandedPaths.insert(address);
}
void Application::onAddedElement(const std::list<QString>& path) {
if (squawk != nullptr && expandedPaths.count(path) > 0) {
QModelIndex index = roster.getIndexByPath(path);
if (index.isValid())
squawk->expand(index);
} }
} }
void Application::notify(const QString& account, const Shared::Message& msg) { void Application::onItemExpanded(const QModelIndex& index)
{
std::list<QString> address = roster.getItemPath(index);
if (address.size() > 0) {
expandedPaths.insert(address);
}
}
void Application::onAddedElement(const std::list<QString>& path)
{
if (squawk != nullptr && expandedPaths.count(path) > 0) {
QModelIndex index = roster.getIndexByPath(path);
if (index.isValid()) {
squawk->expand(index);
}
}
}
void Application::notify(const QString& account, const Shared::Message& msg)
{
QString jid = msg.getPenPalJid(); QString jid = msg.getPenPalJid();
QString name = QString(roster.getContactName(account, jid)); QString name = QString(roster.getContactName(account, jid));
QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource())); QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource()));
@ -324,15 +344,16 @@ void Application::notify(const QString& account, const Shared::Message& msg) {
uint32_t notificationId = qHash(msg.getId()); uint32_t notificationId = qHash(msg.getId());
args << notificationId; args << notificationId;
if (path.size() > 0) if (path.size() > 0) {
args << path; args << path;
else } else {
args << QString("mail-message"); //TODO should here better be unknown user icon? args << QString("mail-message"); //TODO should here better be unknown user icon?
}
if (msg.getType() == Shared::Message::groupChat) if (msg.getType() == Shared::Message::groupChat) {
args << msg.getFromResource() + tr(" from ") + name; args << msg.getFromResource() + tr(" from ") + name;
else } else {
args << name; args << name;
}
QString body(msg.getBody()); QString body(msg.getBody());
QString oob(msg.getOutOfBandUrl()); QString oob(msg.getOutOfBandUrl());
@ -357,11 +378,13 @@ void Application::notify(const QString& account, const Shared::Message& msg) {
storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId()))); storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId())));
if (squawk != nullptr) if (squawk != nullptr) {
QApplication::alert(squawk); QApplication::alert(squawk);
}
} }
void Application::onNotificationClosed(quint32 id, quint32 reason) { void Application::onNotificationClosed(quint32 id, quint32 reason)
{
Notifications::const_iterator itr = storage.find(id); Notifications::const_iterator itr = storage.find(id);
if (itr != storage.end()) { if (itr != storage.end()) {
if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html) if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
@ -374,18 +397,21 @@ void Application::onNotificationClosed(quint32 id, quint32 reason) {
} }
} }
void Application::onNotificationInvoked(quint32 id, const QString& action) { void Application::onNotificationInvoked(quint32 id, const QString& action)
{
qDebug() << "Notification" << id << action << "request"; qDebug() << "Notification" << id << action << "request";
Notifications::const_iterator itr = storage.find(id); Notifications::const_iterator itr = storage.find(id);
if (itr != storage.end()) { if (itr != storage.end()) {
if (action == "markAsRead") if (action == "markAsRead") {
roster.markMessageAsRead(itr->second.first, itr->second.second); roster.markMessageAsRead(itr->second.first, itr->second.second);
else if (action == "openConversation") } else if (action == "openConversation") {
focusConversation(itr->second.first, "", itr->second.second); focusConversation(itr->second.first, "", itr->second.second);
}
} }
} }
void Application::unreadMessagesCountChanged(int count) { void Application::unreadMessagesCountChanged(int count)
{
QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update"); QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update");
signal << qApp->desktopFileName() + QLatin1String(".desktop"); signal << qApp->desktopFileName() + QLatin1String(".desktop");
signal << QVariantMap ({ signal << QVariantMap ({
@ -395,49 +421,54 @@ void Application::unreadMessagesCountChanged(int count) {
QDBusConnection::sessionBus().send(signal); QDBusConnection::sessionBus().send(signal);
} }
void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) { void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId)
{
if (squawk != nullptr) { if (squawk != nullptr) {
if (squawk->currentConversationId() != id) { if (squawk->currentConversationId() != id) {
QModelIndex index = roster.getContactIndex(id.account, id.name, resource); QModelIndex index = roster.getContactIndex(id.account, id.name, resource);
squawk->select(index); squawk->select(index);
} }
if (squawk->isMinimized()) if (squawk->isMinimized()) {
squawk->showNormal(); squawk->showNormal();
else } else {
squawk->show(); squawk->show();
}
squawk->raise(); squawk->raise();
squawk->activateWindow(); squawk->activateWindow();
} else { } else {
openConversation(id, resource); openConversation(id, resource);
} }
SHARED_UNUSED(messageId);
//TODO focus messageId; //TODO focus messageId;
} }
void Application::setState(Shared::Availability p_availability) { void Application::setState(Shared::Availability p_availability)
{
if (availability != p_availability) { if (availability != p_availability) {
availability = p_availability; availability = p_availability;
emit changeState(availability); emit changeState(availability);
} }
} }
void Application::stateChanged(Shared::Availability state) { void Application::stateChanged(Shared::Availability state)
{
availability = state; availability = state;
if (squawk != nullptr) if (squawk != nullptr) {
squawk->stateChanged(state); squawk->stateChanged(state);
}
} }
void Application::readSettings() { void Application::readSettings()
{
QSettings settings; QSettings settings;
settings.beginGroup("ui"); settings.beginGroup("ui");
int avail; int avail;
if (settings.contains("availability")) if (settings.contains("availability")) {
avail = settings.value("availability").toInt(); avail = settings.value("availability").toInt();
else } else {
avail = static_cast<int>(Shared::Availability::online); avail = static_cast<int>(Shared::Availability::online);
}
settings.beginGroup("roster"); settings.beginGroup("roster");
QStringList entries = settings.allKeys(); QStringList entries = settings.allKeys();
@ -455,11 +486,13 @@ void Application::readSettings() {
setState(Shared::Global::fromInt<Shared::Availability>(avail)); setState(Shared::Global::fromInt<Shared::Availability>(avail));
createMainWindow(); createMainWindow();
if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) {
createTrayIcon(); createTrayIcon();
}
} }
void Application::writeSettings() { void Application::writeSettings()
{
QSettings settings; QSettings settings;
settings.beginGroup("ui"); settings.beginGroup("ui");
settings.setValue("availability", static_cast<int>(availability)); settings.setValue("availability", static_cast<int>(availability));
@ -468,9 +501,9 @@ void Application::writeSettings() {
settings.beginGroup("roster"); settings.beginGroup("roster");
for (const std::list<QString>& address : expandedPaths) { for (const std::list<QString>& address : expandedPaths) {
QString path = ""; QString path = "";
for (const QString& hop : address) for (const QString& hop : address) {
path += hop + "/"; path += hop + "/";
}
path += "expanded"; path += "expanded";
settings.setValue(path, true); settings.setValue(path, true);
} }
@ -480,58 +513,63 @@ void Application::writeSettings() {
} }
void Application::requestPassword(const QString& account, bool authenticationError) { void Application::requestPassword(const QString& account, bool authenticationError) {
if (authenticationError) if (authenticationError) {
dialogueQueue.addAction(account, DialogQueue::askCredentials); dialogueQueue.addAction(account, DialogQueue::askCredentials);
else } else {
dialogueQueue.addAction(account, DialogQueue::askPassword); dialogueQueue.addAction(account, DialogQueue::askPassword);
} }
void Application::onConversationClosed() { }
void Application::onConversationClosed()
{
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
Models::Roster::ElId id(conv->getAccount(), conv->getJid()); Models::Roster::ElId id(conv->getAccount(), conv->getJid());
Conversations::const_iterator itr = conversations.find(id); Conversations::const_iterator itr = conversations.find(id);
if (itr != conversations.end()) if (itr != conversations.end()) {
conversations.erase(itr); conversations.erase(itr);
}
if (conv->isMuc) { if (conv->isMuc) {
Room* room = static_cast<Room*>(conv); Room* room = static_cast<Room*>(conv);
if (!room->autoJoined()) if (!room->autoJoined()) {
emit setRoomJoined(id.account, id.name, false); emit setRoomJoined(id.account, id.name, false);
}
} }
} }
void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) { void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe)
{
Models::Item::Type type = roster.getContactType(id); Models::Item::Type type = roster.getContactType(id);
switch (type) { switch (type) {
case Models::Item::contact: case Models::Item::contact:
if (subscribe) if (subscribe) {
emit subscribeContact(id.account, id.name, ""); emit subscribeContact(id.account, id.name, "");
else } else {
emit unsubscribeContact(id.account, id.name, ""); emit unsubscribeContact(id.account, id.name, "");
}
break; break;
case Models::Item::room: case Models::Item::room:
setRoomAutoJoin(id.account, id.name, subscribe); setRoomAutoJoin(id.account, id.name, subscribe);
if (!isConverstationOpened(id)) if (!isConverstationOpened(id)) {
emit setRoomJoined(id.account, id.name, subscribe); emit setRoomJoined(id.account, id.name, subscribe);
}
break; break;
default: default:
break; break;
} }
} }
void Application::subscribeConversation(Conversation* conv) { void Application::subscribeConversation(Conversation* conv)
{
connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage); connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage);
connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage); connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage);
connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend); connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend);
connect(conv, &Conversation::setEncryption, this, &Application::onConversationSetEncryption);
connect(conv, &Conversation::notifyableMessage, this, &Application::notify); connect(conv, &Conversation::notifyableMessage, this, &Application::notify);
} }
void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) { void Application::openConversation(const Models::Roster::ElId& id, const QString& resource)
{
Conversations::const_iterator itr = conversations.find(id); Conversations::const_iterator itr = conversations.find(id);
Models::Account* acc = roster.getAccount(id.account); Models::Account* acc = roster.getAccount(id.account);
Conversation* conv = nullptr; Conversation* conv = nullptr;
@ -545,8 +583,9 @@ void Application::openConversation(const Models::Roster::ElId& id, const QString
created = true; created = true;
Models::Room* room = static_cast<Models::Room*>(el); Models::Room* room = static_cast<Models::Room*>(el);
conv = new Room(acc, room); conv = new Room(acc, room);
if (!room->getJoined()) if (!room->getJoined()) {
emit setRoomJoined(id.account, id.name, true); emit setRoomJoined(id.account, id.name, true);
}
} else if (el->type == Models::Item::contact) { } else if (el->type == Models::Item::contact) {
created = true; created = true;
conv = new Chat(acc, static_cast<Models::Contact*>(el)); conv = new Chat(acc, static_cast<Models::Contact*>(el));
@ -565,12 +604,14 @@ void Application::openConversation(const Models::Roster::ElId& id, const QString
conv->raise(); conv->raise();
conv->activateWindow(); conv->activateWindow();
if (resource.size() > 0) if (resource.size() > 0) {
conv->setPalResource(resource); conv->setPalResource(resource);
}
} }
} }
void Application::onConversationMessage(const Shared::Message& msg) { void Application::onConversationMessage(const Shared::Message& msg)
{
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount(); QString acc = conv->getAccount();
@ -578,7 +619,8 @@ void Application::onConversationMessage(const Shared::Message& msg) {
emit sendMessage(acc, msg); emit sendMessage(acc, msg);
} }
void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) { void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg)
{
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount(); QString acc = conv->getAccount();
@ -588,15 +630,8 @@ void Application::onConversationReplaceMessage(const QString& originalId, const
emit replaceMessage(acc, originalId, msg); emit replaceMessage(acc, originalId, msg);
} }
void Application::onConversationSetEncryption(Shared::EncryptionProtocol value) { void Application::onConversationResend(const QString& id)
Conversation* conv = static_cast<Conversation*>(sender()); {
QString acc = conv->getAccount();
QString jid = conv->getJid();
emit setEncryption(acc, jid, value);
}
void Application::onConversationResend(const QString& id) {
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount(); QString acc = conv->getAccount();
QString jid = conv->getJid(); QString jid = conv->getJid();
@ -614,7 +649,8 @@ void Application::onSquawkOpenedConversation() {
} }
} }
void Application::removeAccount(const QString& account) { void Application::removeAccount(const QString& account)
{
Conversations::const_iterator itr = conversations.begin(); Conversations::const_iterator itr = conversations.begin();
while (itr != conversations.end()) { while (itr != conversations.end()) {
if (itr->first.account == account) { if (itr->first.account == account) {
@ -629,20 +665,23 @@ void Application::removeAccount(const QString& account) {
} }
} }
if (squawk != nullptr && squawk->currentConversationId().account == account) if (squawk != nullptr && squawk->currentConversationId().account == account) {
squawk->closeCurrentConversation(); squawk->closeCurrentConversation();
}
roster.removeAccount(account); roster.removeAccount(account);
} }
void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data) { void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data)
{
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
QString attr = itr.key(); QString attr = itr.key();
roster.updateAccount(account, attr, *itr); roster.updateAccount(account, attr, *itr);
} }
} }
void Application::addGroup(const QString& account, const QString& name) { void Application::addGroup(const QString& account, const QString& name)
{
roster.addGroup(account, name); roster.addGroup(account, name);
if (squawk != nullptr) { if (squawk != nullptr) {
@ -653,8 +692,9 @@ void Application::addGroup(const QString& account, const QString& name) {
if (settings.value("expanded", false).toBool()) { if (settings.value("expanded", false).toBool()) {
QModelIndex ind = roster.getAccountIndex(account); QModelIndex ind = roster.getAccountIndex(account);
squawk->expand(ind); squawk->expand(ind);
if (settings.value(name + "/expanded", false).toBool()) if (settings.value(name + "/expanded", false).toBool()) {
squawk->expand(roster.getGroupIndex(account, name)); squawk->expand(roster.getGroupIndex(account, name));
}
} }
settings.endGroup(); settings.endGroup();
settings.endGroup(); settings.endGroup();

View File

@ -60,7 +60,6 @@ signals:
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
void subscribeContact(const QString& account, const QString& jid, const QString& reason); void subscribeContact(const QString& account, const QString& jid, const QString& reason);
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
void setEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value);
void quitting(); void quitting();
void readyToQuit(); void readyToQuit();
@ -102,7 +101,6 @@ private slots:
void onItemExpanded(const QModelIndex& index); void onItemExpanded(const QModelIndex& index);
void onItemCollapsed(const QModelIndex& index); void onItemCollapsed(const QModelIndex& index);
void onAddedElement(const std::list<QString>& path); void onAddedElement(const std::list<QString>& path);
void onConversationSetEncryption(Shared::EncryptionProtocol value);
private: private:
void createMainWindow(); void createMainWindow();

View File

@ -16,43 +16,125 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "root.h" #include "shared/global.h"
#include "shared/messageinfo.h" #include "shared/messageinfo.h"
#include "shared/identity.h" #include "shared/pathcheck.h"
#include "shared/info.h" #include "main/application.h"
#include "core/signalcatcher.h"
#include "core/squawk.h"
#include <QObject> #include <QLibraryInfo>
#include <QSettings>
#include <QStandardPaths>
#include <QTranslator>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtWidgets/QApplication>
#include <QDir>
#ifdef WITH_OMEMO int main(int argc, char *argv[])
#include <QXmppOmemoStorage.h> {
#endif
int main(int argc, char *argv[]) {
qRegisterMetaType<Shared::Message>("Shared::Message"); qRegisterMetaType<Shared::Message>("Shared::Message");
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo"); qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
qRegisterMetaType<Shared::VCard>("Shared::VCard"); qRegisterMetaType<Shared::VCard>("Shared::VCard");
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>"); qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>"); qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
qRegisterMetaType<std::list<QString>>("std::list<QString>");
qRegisterMetaType<std::set<QString>>("std::set<QString>");
qRegisterMetaType<std::list<Shared::Identity>>("std::list<Shared::Identity>");
qRegisterMetaType<QSet<QString>>("QSet<QString>"); qRegisterMetaType<QSet<QString>>("QSet<QString>");
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState"); qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
qRegisterMetaType<Shared::Availability>("Shared::Availability"); qRegisterMetaType<Shared::Availability>("Shared::Availability");
qRegisterMetaType<Shared::EncryptionProtocol>("Shared::EncryptionProtocol");
qRegisterMetaType<Shared::KeyInfo>("Shared::KeyInfo");
qRegisterMetaType<Shared::Info>("Shared::Info");
qRegisterMetaType<Shared::TrustLevel>("Shared::TrustLevel");
#ifdef WITH_OMEMO
qRegisterMetaType<QXmppOmemoStorage::OwnDevice>("QXmppOmemoStorage::OwnDevice");
qRegisterMetaTypeStreamOperators<QXmppOmemoStorage::OwnDevice>("QXmppOmemoStorage::OwnDevice");
qRegisterMetaType<QXmppOmemoStorage::Device>("QXmppOmemoStorage::Device");
#endif
Root app(argc, argv); QApplication app(argc, argv);
if (!app.initializeSettings()) SignalCatcher sc(&app);
QApplication::setApplicationName("squawk");
QApplication::setOrganizationName("macaw.me");
QApplication::setApplicationDisplayName("Squawk");
QApplication::setApplicationVersion("0.2.3");
app.setDesktopFileName("squawk");
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&qtTranslator);
QTranslator myappTranslator;
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
bool found = false;
for (QString share : shares) {
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
if (found) {
break;
}
}
if (!found) {
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
}
app.installTranslator(&myappTranslator);
QIcon icon;
icon.addFile(":images/logo.svg", QSize(16, 16));
icon.addFile(":images/logo.svg", QSize(24, 24));
icon.addFile(":images/logo.svg", QSize(32, 32));
icon.addFile(":images/logo.svg", QSize(48, 48));
icon.addFile(":images/logo.svg", QSize(64, 64));
icon.addFile(":images/logo.svg", QSize(96, 96));
icon.addFile(":images/logo.svg", QSize(128, 128));
icon.addFile(":images/logo.svg", QSize(256, 256));
icon.addFile(":images/logo.svg", QSize(512, 512));
QApplication::setWindowIcon(icon);
new Shared::Global(); //translates enums
QSettings settings;
QVariant vs = settings.value("style");
if (vs.isValid()) {
QString style = vs.toString().toLower();
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") {
Shared::Global::setTheme(theme);
}
}
}
QString path = Shared::downloadsPathCheck();
if (path.size() > 0) {
settings.setValue("downloadsPath", path);
} else {
qDebug() << "couldn't initialize directory for downloads, quitting";
return -1; return -1;
}
return app.run(); Core::Squawk* squawk = new Core::Squawk();
QThread* coreThread = new QThread();
squawk->moveToThread(coreThread);
Application application(squawk);
QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit);
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop);
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
coreThread->start();
int result = app.exec();
if (coreThread->isRunning()) {
//coreThread->wait();
//todo if I uncomment that, the app will not quit if it has reconnected at least once
//it feels like a symptom of something badly desinged in the core thread
//need to investigate;
}
return result;
} }

View File

@ -1,190 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "root.h"
#include <QDebug>
#include <QThread>
#include <QLibraryInfo>
#include <QStandardPaths>
#include <string>
#include <shared/pathcheck.h>
const std::vector<unsigned int> Root::appIconSizes({
16, 24, 32, 48, 64, 96, 128, 256, 512
});
Root::Root(int& argc, char *argv[]) :
QApplication(argc, argv),
signalCatcher(this),
defaultTranslator(),
currentTranslator(),
appIcon(),
settings(),
componentsInitialized(false),
global(nullptr),
coreThread(nullptr),
core(nullptr),
gui(nullptr)
{
setApplicationName("squawk");
setOrganizationName("macaw.me");
setApplicationDisplayName("Squawk");
setApplicationVersion("0.2.3");
setDesktopFileName("squawk");
initializeTranslation();
initializeAppIcon();
global = new Shared::Global(); //important to instantiate after initialization of translations;
}
Root::~Root() {
if (componentsInitialized) {
delete gui;
if (core != nullptr)
delete core;
delete coreThread;
}
delete global;
}
void Root::initializeTranslation() {
defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
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) {
break;
}
}
if (!found) {
currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
}
installTranslator(&currentTranslator);
}
void Root::initializeAppIcon() {
for (std::vector<unsigned int>::size_type i = 0; i < appIconSizes.size(); ++i)
appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i]));
Root::setWindowIcon(appIcon);
}
bool Root::initializeSettings() {
QVariant vs = settings.value("style");
if (vs.isValid()) {
QString style = vs.toString().toLower();
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") {
Shared::Global::setTheme(theme);
}
}
}
QString path = Shared::downloadsPathCheck();
if (path.size() > 0) {
settings.setValue("downloadsPath", path);
} else {
qDebug() << "couldn't initialize directory for downloads, quitting";
return false;
}
return true;
}
int Root::run() {
if (!componentsInitialized)
initializeComponents();
coreThread->start();
int result = exec();
qDebug("Event loop stopped");
if (result == 0) {
processEvents(); //I dont like all of this mess
if (coreThread->isRunning()) { //but it's the best solution for now
if (core != nullptr) { //Ideally, following line should never appear in the log
qDebug() << "Core is still seems to be running, killing manually";
core->deleteLater();
coreThread->quit();
processEvents();
core = nullptr;
}
coreThread->wait();
}
}
return result;
}
void Root::initializeComponents() {
core = new Core::Squawk();
coreThread = new QThread();
core->moveToThread(coreThread);
gui = new Application(core);
QObject::connect(&signalCatcher, &SignalCatcher::interrupt, gui, &Application::quit);
QObject::connect(coreThread, &QThread::started, core, &Core::Squawk::start);
QObject::connect(gui, &Application::quitting, core, &Core::Squawk::stop);
QObject::connect(core, &Core::Squawk::quit, core, &Core::Squawk::deleteLater);
QObject::connect(core, &Core::Squawk::destroyed, this, &Root::onCoreDestroyed);
QObject::connect(core, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
QObject::connect(coreThread, &QThread::finished, this, &Root::quit, Qt::QueuedConnection);
componentsInitialized = true;
}
bool Root::notify(QObject* receiver, QEvent* e) {
try {
return QApplication::notify(receiver, e);
} catch(const std::runtime_error& e) {
qDebug() << "std::runtime_error in thread:" << QThread::currentThreadId();
qDebug() << "error message:" << e.what();
} catch(const std::exception& e) {
qDebug() << "std::exception in thread:" << QThread::currentThreadId();
qDebug() << "error message:" << e.what();
} catch(const int& e) {
qDebug() << "integer exception in thread:" << QThread::currentThreadId();
qDebug() << "thrown integer:" << std::to_string(e).c_str();
} catch(...) {
qDebug() << "unhandled exception thread:" << QThread::currentThreadId();
}
qDebug() << "Squawk is crashing...";
exit(1);
return false;
}
void Root::onCoreDestroyed() {
core = nullptr;
}

View File

@ -1,70 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef ROOT_H
#define ROOT_H
#include <QApplication>
#include <QTranslator>
#include <QIcon>
#include <QSettings>
#include <QThread>
#include <vector>
#include <core/squawk.h>
#include <core/signalcatcher.h>
#include <shared/global.h>
#include "application.h"
class Root : public QApplication {
Q_OBJECT
public:
Root(int& argc, char* argv[]);
~Root();
bool notify(QObject* receiver, QEvent* e) override;
int run();
bool initializeSettings();
private slots:
void onCoreDestroyed();
private:
void initializeTranslation();
void initializeAppIcon();
void initializeComponents();
private:
static const std::vector<unsigned int> appIconSizes;
SignalCatcher signalCatcher;
QTranslator defaultTranslator;
QTranslator currentTranslator;
QIcon appIcon;
QSettings settings;
bool componentsInitialized;
Shared::Global* global;
QThread* coreThread;
Core::Squawk* core;
Application* gui;
};
#endif // ROOT_H

View File

@ -1,12 +1,12 @@
# Maintainer: Yury Gubich <blue@macaw.me> # Maintainer: Yury Gubich <blue@macaw.me>
pkgname=squawk pkgname=squawk
pkgver=0.2.3 pkgver=0.2.2
pkgrel=1 pkgrel=1
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
url="https://git.macaw.me/blue/squawk" url="https://git.macaw.me/blue/squawk"
license=('GPL3') license=('GPL3')
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp>=1.1.0') depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost')
optdepends=('kwallet: secure password storage (requires rebuild)' optdepends=('kwallet: secure password storage (requires rebuild)'
'kconfig: system themes support (requires rebuild)' 'kconfig: system themes support (requires rebuild)'
@ -18,9 +18,9 @@ sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e')
build() { build() {
cd "$srcdir/squawk" cd "$srcdir/squawk"
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
cmake --build . cmake --build . -j $nproc
} }
package() { package() {
cd "$srcdir/squawk" cd "$srcdir/squawk"
DESTDIR="$pkgdir/" cmake --install . DESTDIR="$pkgdir/" cmake --build . --target install
} }

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11,3 C 8.784,3 7,4.784 7,7 l 0,4 -2,0 c 0,2.666667 0,5.333333 0,8 4,0 8,0 12,0 l 0,-8 c -0.666667,0 -1.333333,0 -2,0 L 15,7 C 15,4.784 13.216,3 11,3 m 0,1 c 1.662,0 3,1.561 3,3.5 L 14,11 8,11 8,7.5 C 8,5.561 9.338,4 11,4"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 558 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
class="ColorScheme-Text"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,14 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="m11 3c-2.216 0-4 1.784-4 4v1h1v-.5c0-1.939 1.338-3.5 3-3.5 1.662 0 3 1.561 3 3.5v3.5h-5-1-1-1-1v1 7h1 10 1v-8h-1-1v-4c0-2.216-1.784-4-4-4m-5 9h10v6h-10v-6"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 491 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 6.3431375,2 5,3.3431372 5,5 l 0,3 -2,0 0,6 10,0 0,-6 -2,0 0,-3 C 11,3.3431372 9.6568625,2 8,2 Z m 0,1 c 1.1045695,0 2,0.8954305 2,2 L 10,8 6,8 6,5 C 6,3.8954305 6.8954305,3 8,3 Z"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 522 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
class="ColorScheme-Text"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#232629;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 2 C 6.3431375 2 5 3.3431371 5 5 L 5 6 L 6 6 L 6 5 C 6 3.8954305 6.8954305 3 8 3 C 9.1045695 3 10 3.8954305 10 5 L 10 8 L 7 8 L 6 8 L 5 8 L 3 8 L 3 14 L 13 14 L 13 8 L 11 8 L 11 5 C 11 3.3431371 9.6568625 2 8 2 z M 4 9 L 12 9 L 12 13 L 4 13 L 4 9 z "
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 11,3 C 8.784,3 7,4.784 7,7 l 0,4 -2,0 c 0,2.666667 0,5.333333 0,8 4,0 8,0 12,0 l 0,-8 c -0.666667,0 -1.333333,0 -2,0 L 15,7 C 15,4.784 13.216,3 11,3 m 0,1 c 1.662,0 3,1.561 3,3.5 L 14,11 8,11 8,7.5 C 8,5.561 9.338,4 11,4"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 558 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
class="ColorScheme-Text"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,14 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="m11 3c-2.216 0-4 1.784-4 4v1h1v-.5c0-1.939 1.338-3.5 3-3.5 1.662 0 3 1.561 3 3.5v3.5h-5-1-1-1-1v1 7h1 10 1v-8h-1-1v-4c0-2.216-1.784-4-4-4m-5 9h10v6h-10v-6"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 491 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 6.3431375,2 5,3.3431372 5,5 l 0,3 -2,0 0,6 10,0 0,-6 -2,0 0,-3 C 11,3.3431372 9.6568625,2 8,2 Z m 0,1 c 1.1045695,0 2,0.8954305 2,2 L 10,8 6,8 6,5 C 6,3.8954305 6.8954305,3 8,3 Z"
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 522 B

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
class="ColorScheme-Text"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#eff0f1;
}
</style>
</defs>
<path style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 8 2 C 6.3431375 2 5 3.3431371 5 5 L 5 6 L 6 6 L 6 5 C 6 3.8954305 6.8954305 3 8 3 C 9.1045695 3 10 3.8954305 10 5 L 10 8 L 7 8 L 6 8 L 5 8 L 3 8 L 3 14 L 13 14 L 13 8 L 11 8 L 11 5 C 11 3.3431371 9.6568625 2 8 2 z M 4 9 L 12 9 L 12 13 L 4 13 L 4 9 z "
class="ColorScheme-Text"
/>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@ -42,9 +42,6 @@
<file>images/fallback/dark/big/add.svg</file> <file>images/fallback/dark/big/add.svg</file>
<file>images/fallback/dark/big/folder.svg</file> <file>images/fallback/dark/big/folder.svg</file>
<file>images/fallback/dark/big/document-preview.svg</file> <file>images/fallback/dark/big/document-preview.svg</file>
<file>images/fallback/dark/big/shield.svg</file>
<file>images/fallback/dark/big/lock.svg</file>
<file>images/fallback/dark/big/unlock.svg</file>
<file>images/fallback/dark/small/absent.svg</file> <file>images/fallback/dark/small/absent.svg</file>
@ -87,9 +84,6 @@
<file>images/fallback/dark/small/add.svg</file> <file>images/fallback/dark/small/add.svg</file>
<file>images/fallback/dark/small/folder.svg</file> <file>images/fallback/dark/small/folder.svg</file>
<file>images/fallback/dark/small/document-preview.svg</file> <file>images/fallback/dark/small/document-preview.svg</file>
<file>images/fallback/dark/small/shield.svg</file>
<file>images/fallback/dark/small/lock.svg</file>
<file>images/fallback/dark/small/unlock.svg</file>
<file>images/fallback/light/big/absent.svg</file> <file>images/fallback/light/big/absent.svg</file>
@ -132,9 +126,6 @@
<file>images/fallback/light/big/add.svg</file> <file>images/fallback/light/big/add.svg</file>
<file>images/fallback/light/big/folder.svg</file> <file>images/fallback/light/big/folder.svg</file>
<file>images/fallback/light/big/document-preview.svg</file> <file>images/fallback/light/big/document-preview.svg</file>
<file>images/fallback/light/big/shield.svg</file>
<file>images/fallback/light/big/lock.svg</file>
<file>images/fallback/light/big/unlock.svg</file>
<file>images/fallback/light/small/absent.svg</file> <file>images/fallback/light/small/absent.svg</file>
@ -177,8 +168,5 @@
<file>images/fallback/light/small/add.svg</file> <file>images/fallback/light/small/add.svg</file>
<file>images/fallback/light/small/folder.svg</file> <file>images/fallback/light/small/folder.svg</file>
<file>images/fallback/light/small/document-preview.svg</file> <file>images/fallback/light/small/document-preview.svg</file>
<file>images/fallback/light/small/shield.svg</file>
<file>images/fallback/light/small/lock.svg</file>
<file>images/fallback/light/small/unlock.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -1,45 +1,21 @@
set(SOURCE_FILES
global.cpp
exception.cpp
icons.cpp
message.cpp
messageinfo.cpp
utils.cpp
vcard.cpp
pathcheck.cpp
clientinfo.cpp
identity.cpp
form.cpp
field.cpp
keyinfo.cpp
info.cpp
clientid.cpp
trustsummary.cpp
)
set(HEADER_FILES
shared.h
enums.h
global.h
exception.h
icons.h
message.h
messageinfo.h
utils.h
vcard.h
pathcheck.h
clientinfo.h
identity.h
form.h
field.h
keyinfo.h
info.h
clientid.h
trustsummary.h
defines.h
)
target_sources(squawk PRIVATE target_sources(squawk PRIVATE
${SOURCE_FILES} enums.h
${HEADER_FILES} global.cpp
) global.h
exception.cpp
exception.h
icons.cpp
icons.h
message.cpp
message.h
messageinfo.cpp
messageinfo.h
order.h
shared.h
utils.cpp
utils.h
vcard.cpp
vcard.h
pathcheck.cpp
pathcheck.h
)

View File

@ -1,167 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "clientid.h"
Shared::ClientId::ClientId():
node(),
verification(),
hash()
{}
Shared::ClientId::ClientId(const QString& p_node, const QString& p_ver, const QString& p_hash):
node(p_node),
verification(p_ver),
hash(p_hash)
{}
Shared::ClientId::ClientId(const Shared::ClientId& other):
node(other.node),
verification(other.verification),
hash(other.hash)
{}
Shared::ClientId & Shared::ClientId::operator=(const Shared::ClientId& other) {
node = other.node;
verification = other.verification;
hash = other.hash;
return *this;
}
bool Shared::ClientId::operator==(const Shared::ClientId& other) const {
return hash == other.hash && verification == other.verification && node == other.node;
}
bool Shared::ClientId::operator!=(const Shared::ClientId& other) const {
return hash != other.hash && verification != other.verification && node != other.node;
}
bool Shared::ClientId::operator<(const Shared::ClientId& other) const {
if (hash < other.hash)
return true;
if (hash > other.hash)
return false;
if (verification < other.verification)
return true;
if (verification > other.verification)
return false;
if (node < other.node)
return true;
return false;
}
bool Shared::ClientId::operator>(const Shared::ClientId& other) const {
if (hash > other.hash)
return true;
if (hash < other.hash)
return false;
if (verification > other.verification)
return true;
if (verification < other.verification)
return false;
if (node > other.node)
return true;
return false;
}
bool Shared::ClientId::operator<=(const Shared::ClientId& other) const {
if (hash < other.hash)
return true;
if (hash > other.hash)
return false;
if (verification < other.verification)
return true;
if (verification > other.verification)
return false;
if (node < other.node)
return true;
if (node > other.node)
return false;
return true;
}
bool Shared::ClientId::operator>=(const Shared::ClientId& other) const {
if (hash > other.hash)
return true;
if (hash < other.hash)
return false;
if (verification > other.verification)
return true;
if (verification < other.verification)
return false;
if (node > other.node)
return true;
if (node < other.node)
return false;
return true;
}
QString Shared::ClientId::getId() const {
return node + "/" + verification;
}
bool Shared::ClientId::valid() const {
return node.size() > 0 && verification.size() > 0 && hash.size() > 0;
}
QDataStream & Shared::ClientId::operator<<(QDataStream& stream) {
stream >> node;
stream >> verification;
stream >> hash;
return stream;
}
QDataStream & Shared::ClientId::operator>>(QDataStream& stream) const {
stream << node;
stream << verification;
stream << hash;
return stream;
}
QDataStream & operator<<(QDataStream& stream, const Shared::ClientId& info) {
info >> stream;
return stream;
}
QDataStream & operator>>(QDataStream& stream, Shared::ClientId& info) {
info << stream;
return stream;
}

View File

@ -1,58 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SHARED_CLIENTID_H
#define SHARED_CLIENTID_H
#include <QString>
#include <QDataStream>
namespace Shared {
class ClientId {
public:
ClientId();
ClientId(const QString& node, const QString& verification, const QString& hash);
ClientId(const ClientId& other);
ClientId& operator = (const ClientId& other);
bool operator == (const ClientId& other) const;
bool operator != (const ClientId& other) const;
bool operator < (const ClientId& other) const;
bool operator > (const ClientId& other) const;
bool operator <= (const ClientId& other) const;
bool operator >= (const ClientId& other) const;
bool valid() const;
QString getId() const;
QDataStream& operator << (QDataStream& stream);
QDataStream& operator >> (QDataStream& stream) const;
public:
QString node;
QString verification;
QString hash;
};
}
Q_DECLARE_METATYPE(Shared::ClientId)
QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info);
QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info);
#endif // SHARED_CLIENTID_H

View File

@ -1,116 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "clientinfo.h"
const std::map<QString, QCryptographicHash::Algorithm> Shared::ClientInfo::hashes = {
//md2 is missing
{"md5", QCryptographicHash::Md5},
{"sha-1", QCryptographicHash::Sha1},
{"sha-224", QCryptographicHash::Sha224},
{"sha-256", QCryptographicHash::Sha256},
{"sha-384", QCryptographicHash::Sha384},
{"sha-512", QCryptographicHash::Sha512},
//shake128 is missing
//shake256 is missing
};
Shared::ClientInfo::ClientInfo():
identities(),
extensions(),
id(),
specificPresence() {}
Shared::ClientInfo::ClientInfo(const QString& p_node, const QString& p_ver, const QString& p_hash) :
identities(),
extensions(),
id(p_node, p_ver, p_hash),
specificPresence() {}
Shared::ClientInfo::ClientInfo(const Shared::ClientId& p_id) :
identities(),
extensions(),
id(p_id),
specificPresence() {}
QString Shared::ClientInfo::getId() const {
return id.getId();
}
QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const {
stream << id;
stream << (quint8)identities.size();
for (const Shared::Identity& identity : identities) {
stream << identity;
}
stream << (quint8)extensions.size();
for (const QString& ext : extensions) {
stream << ext;
}
return stream;
}
QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) {
stream >> id;
quint8 size;
stream >> size;
for (quint8 i = 0; i < size; ++i) {
Shared::Identity identity;
stream >> identity;
identities.insert(identity);
}
stream >> size;
for (quint8 i = 0; i < size; ++i) {
QString ext;
stream >> ext;
extensions.insert(ext);
}
return stream;
}
bool Shared::ClientInfo::valid() const {
std::map<QString, QCryptographicHash::Algorithm>::const_iterator itr = hashes.find(id.hash);
if (itr == hashes.end()) {
return false;
}
QCryptographicHash calc(itr->second);
QString validationString = "";
for (const Identity& identity : identities) {
calc.addData((identity.category + "/" + identity.type + "/" + identity.language + "/" + identity.name + "<").toUtf8());
}
for (const QString& ext : extensions) {
calc.addData((ext + "<").toUtf8());
}
QString result = calc.result().toBase64();
return result == id.verification;
}
QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info) {
info >> stream;
return stream;
}
QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info) {
info << stream;
return stream;
}

View File

@ -1,58 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SHARED_CLIENTINFO_H
#define SHARED_CLIENTINFO_H
#include <set>
#include <QDataStream>
#include <QString>
#include <QCryptographicHash>
#include <shared/identity.h>
#include <shared/clientid.h>
namespace Shared {
class ClientInfo {
public:
ClientInfo();
ClientInfo(const ClientId& id);
ClientInfo(const QString& node, const QString& verification, const QString& hash);
QString getId() const;
bool valid() const;
QDataStream& operator << (QDataStream& stream);
QDataStream& operator >> (QDataStream& stream) const;
public:
std::set<Identity> identities;
std::set<QString> extensions;
ClientId id;
QString specificPresence;
private:
static const std::map<QString, QCryptographicHash::Algorithm> hashes;
};
}
QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info);
QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info);
#endif // SHARED_CLIENTINFO_H

View File

@ -1,24 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHARED_DEFINES_H
#define SHARED_DEFINES_H
#define SHARED_UNUSED(x) (void)(x)
#endif

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef SHARED_ENUMS_H
#define SHARED_ENUMS_H
#include <deque> #include <deque>
@ -28,13 +29,12 @@ Q_NAMESPACE
enum class ConnectionState { enum class ConnectionState {
disconnected, disconnected,
scheduled,
connecting, connecting,
connected, connected,
error error
}; };
Q_ENUM_NS(ConnectionState) Q_ENUM_NS(ConnectionState)
static const std::deque<QString> connectionStateThemeIcons = {"state-offline", "state-sync", "state-sync", "state-ok", "state-error"}; static const std::deque<QString> connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"};
static const ConnectionState ConnectionStateHighest = ConnectionState::error; static const ConnectionState ConnectionStateHighest = ConnectionState::error;
static const ConnectionState ConnectionStateLowest = ConnectionState::disconnected; static const ConnectionState ConnectionStateLowest = ConnectionState::disconnected;
@ -117,63 +117,5 @@ Q_ENUM_NS(AccountPassword)
static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet;
static const AccountPassword AccountPasswordLowest = AccountPassword::plain; static const AccountPassword AccountPasswordLowest = AccountPassword::plain;
enum class EntryType {
none,
ownAccount,
contact,
conference,
presence,
participant
};
Q_ENUM_NS(EntryType)
static const EntryType EntryTypeHighest = EntryType::participant;
static const EntryType EntryTypeLowest = EntryType::none;
enum class Support {
unknown,
supported,
unsupported
};
Q_ENUM_NS(Support)
enum class Possible {
unknown,
discovering,
present,
abscent
};
Q_ENUM_NS(Possible)
enum class TrustLevel {
/// The key's trust is not decided.
undecided,
/// The key is automatically distrusted (e.g., by the security policy TOAKAFA).
/// \see SecurityPolicy
automaticallyDistrusted,
/// The key is manually distrusted (e.g., by clicking a button or \xep{0450, Automatic Trust
/// Management (ATM)}).
manuallyDistrusted,
/// The key is automatically trusted (e.g., by the client for all keys of a bare JID until one
/// of it is authenticated).
automaticallyTrusted,
/// The key is manually trusted (e.g., by clicking a button).
manuallyTrusted,
/// The key is authenticated (e.g., by QR code scanning or \xep{0450, Automatic Trust
/// Management (ATM)}).
authenticated
};
Q_ENUM_NS(TrustLevel)
static const TrustLevel TrustLevelHighest = TrustLevel::undecided;
static const TrustLevel TrustLevelLowest = TrustLevel::authenticated;
enum class EncryptionProtocol {
none,
omemo,
omemo1,
omemo2
};
Q_ENUM_NS(EncryptionProtocol)
static const EncryptionProtocol EncryptionProtocolHighest = EncryptionProtocol::none;
static const EncryptionProtocol EncryptionProtocolLowest = EncryptionProtocol::omemo2;
} }
#endif // SHARED_ENUMS_H

View File

@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #ifndef EXCEPTION_H
#define EXCEPTION_H
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@ -35,3 +36,5 @@ namespace Utils
const char* what() const noexcept( true ); const char* what() const noexcept( true );
}; };
} }
#endif // EXCEPTION_H

View File

@ -1,28 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "field.h"
Shared::Field::Field(Shared::Field::Type fieldTtype):
type(fieldTtype),
key(),
label(),
description(),
required(false),
options(),
value()
{
}

View File

@ -1,61 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SHARED_FIELD_H
#define SHARED_FIELD_H
#include <list>
#include <QString>
#include <QVariant>
namespace Shared {
/**
* @todo write docs
*/
class Field
{
public:
enum class Type {
boolean,
fixed,
hidden,
jidMultiple,
jidSingle,
listMultiple,
listSingle,
textMultiple,
textPrivate,
textSingle
};
Field(Type fieldType);
public:
const Type type;
QString key;
QString label;
QString description;
bool required;
std::list<std::pair<QString, QString>> options;
QVariant value;
};
}
#endif // SHARED_FIELD_H

View File

@ -1,23 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "form.h"
Shared::Form::Form(Shared::Form::Type formType):
type(formType),
title(),
instructions(),
fields() {}

View File

@ -1,48 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SHARED_FORM_H
#define SHARED_FORM_H
#include <list>
#include <shared/field.h>
namespace Shared {
class Form
{
public:
enum class Type {
none,
form,
submit,
cancel,
result
};
Form(Type formType);
public:
const Type type;
QString title;
QString instructions;
std::list<Field> fields;
};
}
#endif // SHARED_FORM_H

Some files were not shown because too many files have changed in this diff Show More