Compare commits
59 Commits
master
...
maybe-omem
Author | SHA1 | Date | |
---|---|---|---|
6c9f1ab964 | |||
442ad37300 | |||
08fe37bfb2 | |||
bb2ce750c8 | |||
bbeeee4c8a | |||
574210f5d9 | |||
2654e38665 | |||
12ffe8e8e6 | |||
006752b31c | |||
b1a8f162ce | |||
6721b62629 | |||
b22a4c8ca3 | |||
a6254d88b3 | |||
2fbbe1ec22 | |||
8e99cc2969 | |||
140b0fa6b4 | |||
cb7e2ede75 | |||
bc66ab7e52 | |||
f94c3dac14 | |||
a184ecafa3 | |||
7d2688151c | |||
0038aca1f6 | |||
6e06a1d5bc | |||
7d648ab081 | |||
7c1ae4737e | |||
04e745fad4 | |||
b7b70bc198 | |||
9fbbe0c120 | |||
ce047db787 | |||
f45319de25 | |||
ebf0c64ffb | |||
d514db9c4a | |||
f34289399e | |||
05d6761baa | |||
216dcd29e9 | |||
0973cb2991 | |||
50190f3eac | |||
b44873d587 | |||
4c5efad9dc | |||
8310708c92 | |||
d936c0302d | |||
0e937199b0 | |||
48e498be25 | |||
3a7735b192 | |||
8f914c02a7 | |||
50bb3f5fd7 | |||
a0348b8fd2 | |||
85555da81f | |||
ebe5addfb5 | |||
b3c6860e25 | |||
00ffbac6b0 | |||
ff4124d1f0 | |||
15342f3c53 | |||
270a32db9e | |||
e0ef1ef797 | |||
e1eea2f3a2 | |||
e54cff0f0c | |||
4e6bd04b02 | |||
38159eafeb |
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
|||||||
[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/signal-protocol-c"]
|
||||||
|
path = external/signal-protocol-c
|
||||||
|
url = https://github.com/signalapp/libsignal-protocol-c.git
|
||||||
|
@ -6,10 +6,14 @@
|
|||||||
- requesting the history of the current chat after reconnection
|
- requesting the history of the current chat after reconnection
|
||||||
- global availability (in drop down list) gets restored after reconnection
|
- global availability (in drop down list) gets restored after reconnection
|
||||||
- status icon in active chat changes when presence of the pen pal changes
|
- status icon in active chat changes when presence of the pen pal changes
|
||||||
|
- infinite progress when open the dialogue with something that has no history to show
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
||||||
|
- completely rewritten message feed, now it works way faster
|
||||||
|
- OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager
|
||||||
|
- show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed
|
||||||
|
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
||||||
|
|
||||||
## Squawk 0.1.5 (Jul 29, 2020)
|
## Squawk 0.1.5 (Jul 29, 2020)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
164
CMakeLists.txt
@ -1,117 +1,119 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
project(squawk)
|
project(squawk VERSION 0.1.6 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include_directories(.)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
add_executable(squawk)
|
||||||
find_package(Qt5LinguistTools)
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
|
||||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
|
||||||
|
|
||||||
|
|
||||||
set(squawk_SRC
|
|
||||||
main.cpp
|
|
||||||
exception.cpp
|
|
||||||
signalcatcher.cpp
|
|
||||||
shared/global.cpp
|
|
||||||
shared/utils.cpp
|
|
||||||
shared/message.cpp
|
|
||||||
shared/vcard.cpp
|
|
||||||
shared/icons.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(squawk_HEAD
|
|
||||||
exception.h
|
|
||||||
signalcatcher.h
|
|
||||||
shared.h
|
|
||||||
shared/enums.h
|
|
||||||
shared/message.h
|
|
||||||
shared/global.h
|
|
||||||
shared/utils.h
|
|
||||||
shared/vcard.h
|
|
||||||
shared/icons.h
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
|
||||||
execute_process(COMMAND convert -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
configure_file(packaging/squawk.desktop squawk.desktop COPYONLY)
|
|
||||||
|
|
||||||
set(TS_FILES
|
|
||||||
translations/squawk.ru.ts
|
|
||||||
)
|
|
||||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
|
||||||
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
|
||||||
|
|
||||||
qt5_add_resources(RCC resources/resources.qrc)
|
|
||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
|
option(SYSTEM_SIGNAL "Use system signal-protocol-c 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)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
## Qt
|
||||||
|
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
||||||
|
|
||||||
|
## QXmpp
|
||||||
if (SYSTEM_QXMPP)
|
if (SYSTEM_QXMPP)
|
||||||
find_package(QXmpp CONFIG)
|
find_package(QXmpp CONFIG)
|
||||||
|
|
||||||
if (NOT QXmpp_FOUND)
|
if (NOT QXmpp_FOUND)
|
||||||
set(SYSTEM_QXMPP OFF)
|
set(SYSTEM_QXMPP OFF)
|
||||||
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
||||||
else()
|
else ()
|
||||||
message("Building with system QXmpp")
|
message("Building with system QXmpp")
|
||||||
endif()
|
endif ()
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
if(NOT SYSTEM_QXMPP)
|
if (NOT SYSTEM_QXMPP)
|
||||||
|
target_link_libraries(squawk PRIVATE qxmpp)
|
||||||
add_subdirectory(external/qxmpp)
|
add_subdirectory(external/qxmpp)
|
||||||
endif()
|
else ()
|
||||||
|
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Signal
|
||||||
|
if (NOT SYSTEM_SIGNAL)
|
||||||
|
add_subdirectory(external/signal-protocol-c)
|
||||||
|
add_dependencies(squawk signal-protocol-c)
|
||||||
|
target_link_libraries(squawk PRIVATE signal-protocol-c)
|
||||||
|
else ()
|
||||||
|
find_package(Signal REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE Signal::Signal)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
## KIO
|
||||||
|
if (WITH_KIO)
|
||||||
|
find_package(KF5KIO CONFIG)
|
||||||
|
|
||||||
|
if (NOT KF5KIO_FOUND)
|
||||||
|
set(WITH_KIO OFF)
|
||||||
|
message("KIO package wasn't found, KIO support modules wouldn't be built")
|
||||||
|
else ()
|
||||||
|
target_compile_definitions(squawk PRIVATE WITH_KIO)
|
||||||
|
message("Building with support of KIO")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
## KWallet
|
||||||
if (WITH_KWALLET)
|
if (WITH_KWALLET)
|
||||||
find_package(KF5Wallet CONFIG)
|
find_package(KF5Wallet CONFIG)
|
||||||
|
|
||||||
if (NOT KF5Wallet_FOUND)
|
if (NOT KF5Wallet_FOUND)
|
||||||
set(WITH_KWALLET OFF)
|
set(WITH_KWALLET OFF)
|
||||||
message("KWallet package wasn't found, KWallet support module wouldn't be built")
|
message("KWallet package wasn't found, KWallet support module wouldn't be built")
|
||||||
else()
|
else ()
|
||||||
add_definitions(-DWITH_KWALLET)
|
target_compile_definitions(squawk PRIVATE WITH_KWALLET)
|
||||||
message("Building with support of KWallet")
|
message("Building with support of KWallet")
|
||||||
endif()
|
endif ()
|
||||||
endif()
|
endif ()
|
||||||
|
|
||||||
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
|
## LMDB
|
||||||
target_link_libraries(squawk Qt5::Widgets)
|
find_package(LMDB REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE lmdb)
|
||||||
|
|
||||||
|
# OpenSSL
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE OpenSSL::Crypto)
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
target_link_libraries(squawk PRIVATE simpleCrypt)
|
||||||
|
target_link_libraries(squawk PRIVATE uuid)
|
||||||
|
|
||||||
|
# Build type
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
target_compile_options(squawk PRIVATE
|
||||||
|
"-Wall;-Wextra"
|
||||||
|
"$<$<CONFIG:DEBUG>:-g>"
|
||||||
|
"$<$<CONFIG:RELEASE>:-O3>"
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(ui)
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
|
||||||
add_subdirectory(external/simpleCrypt)
|
add_subdirectory(external/simpleCrypt)
|
||||||
|
add_subdirectory(packaging)
|
||||||
target_link_libraries(squawk squawkUI)
|
add_subdirectory(plugins)
|
||||||
target_link_libraries(squawk squawkCORE)
|
add_subdirectory(resources)
|
||||||
target_link_libraries(squawk uuid)
|
add_subdirectory(shared)
|
||||||
|
add_subdirectory(translations)
|
||||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
add_subdirectory(ui)
|
||||||
|
add_subdirectory(qomemo)
|
||||||
|
|
||||||
# Install the executable
|
# Install the executable
|
||||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.0 or higher
|
- CMake 3.0 or higher
|
||||||
- qxmpp 1.1.0 or higher
|
- qxmpp 1.1.0 or higher
|
||||||
- kwallet (optional)
|
- KDE Frameworks: kwallet (optional)
|
||||||
|
- KDE Frameworks: KIO (optional)
|
||||||
|
|
||||||
### Getting
|
### Getting
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ 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`)
|
||||||
- `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`)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
47
cmake/FindLMDB.cmake
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#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 lmdb
|
||||||
|
HINTS ${LMDB_ROOT_DIR}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
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
@ -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 ()
|
@ -1,46 +1,27 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
target_sources(squawk PRIVATE
|
||||||
project(squawkCORE)
|
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Gui CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Network CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Xml CONFIG REQUIRED)
|
|
||||||
|
|
||||||
set(squawkCORE_SRC
|
|
||||||
squawk.cpp
|
|
||||||
account.cpp
|
account.cpp
|
||||||
archive.cpp
|
account.h
|
||||||
rosteritem.cpp
|
|
||||||
contact.cpp
|
|
||||||
conference.cpp
|
|
||||||
storage.cpp
|
|
||||||
networkaccess.cpp
|
|
||||||
adapterFuctions.cpp
|
adapterFuctions.cpp
|
||||||
handlers/messagehandler.cpp
|
archive.cpp
|
||||||
handlers/rosterhandler.cpp
|
archive.h
|
||||||
)
|
conference.cpp
|
||||||
|
conference.h
|
||||||
|
contact.cpp
|
||||||
|
contact.h
|
||||||
|
main.cpp
|
||||||
|
networkaccess.cpp
|
||||||
|
networkaccess.h
|
||||||
|
rosteritem.cpp
|
||||||
|
rosteritem.h
|
||||||
|
signalcatcher.cpp
|
||||||
|
signalcatcher.h
|
||||||
|
squawk.cpp
|
||||||
|
squawk.h
|
||||||
|
storage.cpp
|
||||||
|
storage.h
|
||||||
|
urlstorage.cpp
|
||||||
|
urlstorage.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(handlers)
|
||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
|
||||||
add_library(squawkCORE ${squawkCORE_SRC})
|
|
||||||
|
|
||||||
|
|
||||||
if(SYSTEM_QXMPP)
|
|
||||||
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
|
||||||
target_link_libraries(squawkCORE Qt5::Core)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Network)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Gui)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Xml)
|
|
||||||
target_link_libraries(squawkCORE qxmpp)
|
|
||||||
target_link_libraries(squawkCORE lmdb)
|
|
||||||
target_link_libraries(squawkCORE simpleCrypt)
|
|
||||||
if (WITH_KWALLET)
|
|
||||||
target_link_libraries(squawkCORE kwalletPSE)
|
|
||||||
endif()
|
|
||||||
|
@ -47,7 +47,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
network(p_net),
|
network(p_net),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain),
|
||||||
mh(new MessageHandler(this)),
|
mh(new MessageHandler(this)),
|
||||||
rh(new RosterHandler(this))
|
rh(new RosterHandler(this)),
|
||||||
|
omemo(new QXmpp::Omemo::Manager())
|
||||||
{
|
{
|
||||||
config.setUser(p_login);
|
config.setUser(p_login);
|
||||||
config.setDomain(p_server);
|
config.setDomain(p_server);
|
||||||
@ -84,12 +85,14 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
|
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
|
||||||
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
|
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
|
||||||
|
|
||||||
QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded);
|
QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete);
|
||||||
QObject::connect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError);
|
QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
||||||
|
QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
||||||
|
|
||||||
client.addExtension(rcpm);
|
client.addExtension(rcpm);
|
||||||
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
||||||
|
|
||||||
|
client.addExtension(omemo.get());
|
||||||
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
path += "/" + name;
|
path += "/" + name;
|
||||||
@ -155,8 +158,9 @@ Account::~Account()
|
|||||||
reconnectTimer->stop();
|
reconnectTimer->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::disconnect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onFileUploaded);
|
QObject::disconnect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete);
|
||||||
QObject::disconnect(network, &NetworkAccess::uploadFileError, mh, &MessageHandler::onFileUploadError);
|
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
||||||
|
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
||||||
|
|
||||||
delete mh;
|
delete mh;
|
||||||
delete rh;
|
delete rh;
|
||||||
@ -402,9 +406,6 @@ QString Core::Account::getFullJid() const {
|
|||||||
void Core::Account::sendMessage(const Shared::Message& data) {
|
void Core::Account::sendMessage(const Shared::Message& data) {
|
||||||
mh->sendMessage(data);}
|
mh->sendMessage(data);}
|
||||||
|
|
||||||
void Core::Account::sendMessage(const Shared::Message& data, const QString& path) {
|
|
||||||
mh->sendMessage(data, path);}
|
|
||||||
|
|
||||||
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)) {
|
||||||
@ -434,13 +435,13 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString&
|
|||||||
|
|
||||||
if (contact == 0) {
|
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>());
|
emit responseArchive(jid, std::list<Shared::Message>(), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(contact->jid, std::list<Shared::Message>());
|
emit responseArchive(contact->jid, std::list<Shared::Message>(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
contact->requestHistory(count, before);
|
contact->requestHistory(count, before);
|
||||||
@ -552,9 +553,11 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
case QXmppStanza::Error::NotAuthorized:
|
case QXmppStanza::Error::NotAuthorized:
|
||||||
errorText = "Authentication error";
|
errorText = "Authentication error";
|
||||||
break;
|
break;
|
||||||
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
||||||
case QXmppStanza::Error::PaymentRequired:
|
case QXmppStanza::Error::PaymentRequired:
|
||||||
errorText = "Payment is required";
|
errorText = "Payment is required";
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case QXmppStanza::Error::RecipientUnavailable:
|
case QXmppStanza::Error::RecipientUnavailable:
|
||||||
errorText = "Recipient is unavailable";
|
errorText = "Recipient is unavailable";
|
||||||
break;
|
break;
|
||||||
@ -909,3 +912,16 @@ void Core::Account::handleDisconnection()
|
|||||||
ownVCardRequestInProgress = false;
|
ownVCardRequestInProgress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
|
||||||
|
{
|
||||||
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
|
|
||||||
|
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
||||||
|
if (last) {
|
||||||
|
qDebug() << "The response contains the first accounted message";
|
||||||
|
}
|
||||||
|
emit responseArchive(contact->jid, list, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
|
||||||
|
mh->requestChangeMessage(jid, messageId, data);}
|
||||||
|
@ -30,23 +30,24 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <QXmppRosterManager.h>
|
|
||||||
#include <QXmppCarbonManager.h>
|
|
||||||
#include <QXmppDiscoveryManager.h>
|
|
||||||
#include <QXmppMamManager.h>
|
|
||||||
#include <QXmppMucManager.h>
|
|
||||||
#include <QXmppClient.h>
|
|
||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
|
#include <QXmppCarbonManager.h>
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
#include <QXmppDiscoveryManager.h>
|
||||||
|
#include <QXmppMamManager.h>
|
||||||
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
#include <QXmppMucManager.h>
|
||||||
|
#include <QXmppRosterManager.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
#include <QXmppVCardIq.h>
|
#include <QXmppVCardIq.h>
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <qomemo/qxmpp_omemo_manager.h>
|
||||||
|
|
||||||
#include "shared.h"
|
|
||||||
#include "contact.h"
|
|
||||||
#include "conference.h"
|
#include "conference.h"
|
||||||
|
#include "contact.h"
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
#include "shared/shared.h"
|
||||||
|
|
||||||
#include "handlers/messagehandler.h"
|
#include "handlers/messagehandler.h"
|
||||||
#include "handlers/rosterhandler.h"
|
#include "handlers/rosterhandler.h"
|
||||||
@ -88,7 +89,6 @@ public:
|
|||||||
void setPasswordType(Shared::AccountPassword pt);
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
QString getFullJid() const;
|
QString getFullJid() const;
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(const Shared::Message& data);
|
||||||
void sendMessage(const Shared::Message& data, const QString& path);
|
|
||||||
void requestArchive(const QString& jid, int count, const QString& before);
|
void requestArchive(const QString& jid, int count, const QString& before);
|
||||||
void subscribeToContact(const QString& jid, const QString& reason);
|
void subscribeToContact(const QString& jid, const QString& reason);
|
||||||
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
||||||
@ -97,6 +97,7 @@ public:
|
|||||||
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||||
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 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);
|
||||||
@ -127,14 +128,14 @@ signals:
|
|||||||
void removePresence(const QString& jid, const QString& name);
|
void removePresence(const QString& jid, const QString& name);
|
||||||
void message(const Shared::Message& data);
|
void message(const Shared::Message& data);
|
||||||
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void responseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
void responseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
void error(const QString& text);
|
void error(const QString& text);
|
||||||
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 receivedVCard(const QString& jid, const Shared::VCard& card);
|
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& messageId, const QString& error);
|
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@ -166,6 +167,8 @@ private:
|
|||||||
MessageHandler* mh;
|
MessageHandler* mh;
|
||||||
RosterHandler* rh;
|
RosterHandler* rh;
|
||||||
|
|
||||||
|
QScopedPointer<QXmpp::Omemo::Manager> omemo;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChange(QXmppClient::State state);
|
void onClientStateChange(QXmppClient::State state);
|
||||||
void onClientError(QXmppClient::Error err);
|
void onClientError(QXmppClient::Error err);
|
||||||
@ -183,6 +186,7 @@ private slots:
|
|||||||
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
|
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleDisconnection();
|
void handleDisconnection();
|
||||||
|
@ -271,6 +271,8 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
||||||
QDateTime oTime = msg.getTime();
|
QDateTime oTime = msg.getTime();
|
||||||
bool idChange = msg.change(data);
|
bool idChange = msg.change(data);
|
||||||
|
QDateTime nTime = msg.getTime();
|
||||||
|
bool orderChange = oTime != nTime;
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
MDB_val lmdbKey, lmdbData;
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
@ -280,15 +282,21 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
int rc;
|
int rc;
|
||||||
|
if (idChange || orderChange) {
|
||||||
if (idChange) {
|
if (idChange) {
|
||||||
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
|
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
|
||||||
|
} else {
|
||||||
|
quint64 ostamp = oTime.toMSecsSinceEpoch();
|
||||||
|
lmdbData.mv_data = (quint8*)&ostamp;
|
||||||
|
lmdbData.mv_size = 8;
|
||||||
|
rc = mdb_del(txn, order, &lmdbData, &lmdbKey);
|
||||||
|
}
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
strId = msg.getId().toStdString();
|
strId = msg.getId().toStdString();
|
||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
|
|
||||||
|
quint64 stamp = nTime.toMSecsSinceEpoch();
|
||||||
quint64 stamp = oTime.toMSecsSinceEpoch();
|
|
||||||
lmdbData.mv_data = (quint8*)&stamp;
|
lmdbData.mv_data = (quint8*)&stamp;
|
||||||
lmdbData.mv_size = 8;
|
lmdbData.mv_size = 8;
|
||||||
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
||||||
@ -502,8 +510,9 @@ long unsigned int Core::Archive::size() const
|
|||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
mdb_stat(txn, order, &stat);
|
mdb_stat(txn, order, &stat);
|
||||||
|
size_t amount = stat.ms_entries;
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return stat.ms_entries;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
||||||
@ -603,10 +612,10 @@ void Core::Archive::setFromTheBeginning(bool is)
|
|||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
bool success = setStatValue("beginning", is, txn);
|
bool success = setStatValue("beginning", is, txn);
|
||||||
if (success != 0) {
|
if (success) {
|
||||||
mdb_txn_abort(txn);
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
|
} else {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <QMimeType>
|
#include <QMimeType>
|
||||||
|
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "exception.h"
|
#include "shared/exception.h"
|
||||||
#include <lmdb.h>
|
#include <lmdb.h>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
6
core/handlers/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
messagehandler.cpp
|
||||||
|
messagehandler.h
|
||||||
|
rosterhandler.cpp
|
||||||
|
rosterhandler.h
|
||||||
|
)
|
@ -23,7 +23,6 @@ Core::MessageHandler::MessageHandler(Core::Account* account):
|
|||||||
QObject(),
|
QObject(),
|
||||||
acc(account),
|
acc(account),
|
||||||
pendingStateMessages(),
|
pendingStateMessages(),
|
||||||
pendingMessages(),
|
|
||||||
uploadingSlotsQueue()
|
uploadingSlotsQueue()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -168,14 +167,16 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
id = source.id();
|
id = source.id();
|
||||||
#endif
|
#endif
|
||||||
target.setId(id);
|
target.setId(id);
|
||||||
if (target.getId().size() == 0) {
|
QString messageId = target.getId();
|
||||||
|
if (messageId.size() == 0) {
|
||||||
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
|
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
|
||||||
|
messageId = target.getId();
|
||||||
}
|
}
|
||||||
target.setFrom(source.from());
|
target.setFrom(source.from());
|
||||||
target.setTo(source.to());
|
target.setTo(source.to());
|
||||||
target.setBody(source.body());
|
target.setBody(source.body());
|
||||||
target.setForwarded(forwarded);
|
target.setForwarded(forwarded);
|
||||||
target.setOutOfBandUrl(source.outOfBandUrl());
|
|
||||||
if (guessing) {
|
if (guessing) {
|
||||||
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
||||||
outgoing = true;
|
outgoing = true;
|
||||||
@ -189,6 +190,12 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
} else {
|
} else {
|
||||||
target.setCurrentTime();
|
target.setCurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString oob = source.outOfBandUrl();
|
||||||
|
if (oob.size() > 0) {
|
||||||
|
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
|
||||||
|
}
|
||||||
|
target.setOutOfBandUrl(oob);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
||||||
@ -232,11 +239,23 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessage(Shared::Message data)
|
void Core::MessageHandler::sendMessage(const Shared::Message& data)
|
||||||
|
{
|
||||||
|
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
||||||
|
prepareUpload(data);
|
||||||
|
} else {
|
||||||
|
performSending(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
|
||||||
{
|
{
|
||||||
QString jid = data.getPenPalJid();
|
QString jid = data.getPenPalJid();
|
||||||
QString id = data.getId();
|
QString id = data.getId();
|
||||||
|
QString oob = data.getOutOfBandUrl();
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
|
bool sent = false;
|
||||||
|
QMap<QString, QVariant> changes;
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
||||||
|
|
||||||
@ -245,23 +264,16 @@ void Core::MessageHandler::sendMessage(Shared::Message data)
|
|||||||
#endif
|
#endif
|
||||||
msg.setId(id);
|
msg.setId(id);
|
||||||
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
||||||
msg.setOutOfBandUrl(data.getOutOfBandUrl());
|
msg.setOutOfBandUrl(oob);
|
||||||
msg.setReceiptRequested(true);
|
msg.setReceiptRequested(true);
|
||||||
|
|
||||||
bool sent = acc->client.sendPacket(msg);
|
sent = acc->client.sendPacket(msg);
|
||||||
|
|
||||||
if (sent) {
|
if (sent) {
|
||||||
data.setState(Shared::Message::State::sent);
|
data.setState(Shared::Message::State::sent);
|
||||||
} else {
|
} else {
|
||||||
data.setState(Shared::Message::State::error);
|
data.setState(Shared::Message::State::error);
|
||||||
data.setErrorText("Couldn't send message via QXMPP library check out logs");
|
data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
|
||||||
}
|
|
||||||
|
|
||||||
if (ri != 0) {
|
|
||||||
ri->appendMessageToArchive(data);
|
|
||||||
if (sent) {
|
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -269,41 +281,74 @@ void Core::MessageHandler::sendMessage(Shared::Message data)
|
|||||||
data.setErrorText("You are is offline or reconnecting");
|
data.setErrorText("You are is offline or reconnecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
emit acc->changeMessage(jid, id, {
|
Shared::Message::State mstate = data.getState();
|
||||||
{"state", static_cast<uint>(data.getState())},
|
changes.insert("state", static_cast<uint>(mstate));
|
||||||
{"errorText", data.getErrorText()}
|
if (mstate == Shared::Message::State::error) {
|
||||||
});
|
changes.insert("errorText", data.getErrorText());
|
||||||
|
}
|
||||||
|
if (oob.size() > 0) {
|
||||||
|
changes.insert("outOfBandUrl", oob);
|
||||||
|
}
|
||||||
|
if (!newMessage) {
|
||||||
|
changes.insert("stamp", data.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ri != 0) {
|
||||||
|
if (newMessage) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
|
} else {
|
||||||
|
ri->changeMessage(id, changes);
|
||||||
|
}
|
||||||
|
if (sent) {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
} else {
|
||||||
|
pendingStateMessages.erase(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit acc->changeMessage(jid, id, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessage(const Shared::Message& data, const QString& path)
|
void Core::MessageHandler::prepareUpload(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
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);
|
QString url = acc->network->getFileRemoteUrl(path);
|
||||||
if (url.size() != 0) {
|
if (url.size() != 0) {
|
||||||
sendMessageWithLocalUploadedFile(data, url);
|
sendMessageWithLocalUploadedFile(data, url);
|
||||||
} else {
|
} else {
|
||||||
if (acc->network->isUploading(path, data.getId())) {
|
if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
||||||
pendingMessages.emplace(data.getId(), data);
|
ri->appendMessageToArchive(data);
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
} else {
|
} else {
|
||||||
if (acc->um->serviceFound()) {
|
if (acc->um->serviceFound()) {
|
||||||
QFileInfo file(path);
|
QFileInfo file(path);
|
||||||
if (file.exists() && file.isReadable()) {
|
if (file.exists() && file.isReadable()) {
|
||||||
uploadingSlotsQueue.emplace_back(path, data);
|
ri->appendMessageToArchive(data);
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
uploadingSlotsQueue.emplace_back(path, id);
|
||||||
if (uploadingSlotsQueue.size() == 1) {
|
if (uploadingSlotsQueue.size() == 1) {
|
||||||
acc->um->requestUploadSlot(file);
|
acc->um->requestUploadSlot(file);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onFileUploadError(data.getId(), "Uploading file no longer exists or your system user has no permission to read it");
|
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";
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account");
|
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";
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onFileUploadError(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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,12 +359,12 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
|
|||||||
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 {
|
||||||
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
|
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
||||||
const QString& mId = pair.second.getId();
|
const QString& mId = pair.second;
|
||||||
acc->network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
QString palJid = pendingStateMessages.at(mId);
|
||||||
pendingMessages.emplace(mId, pair.second);
|
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);
|
||||||
}
|
}
|
||||||
@ -328,44 +373,111 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
|
|||||||
|
|
||||||
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
||||||
{
|
{
|
||||||
|
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";
|
||||||
qDebug() << request.error().text();
|
qDebug() << err;
|
||||||
} else {
|
} else {
|
||||||
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
|
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
||||||
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << request.error().text();
|
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err;
|
||||||
emit acc->uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text());
|
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
|
||||||
|
|
||||||
|
uploadingSlotsQueue.pop_front();
|
||||||
if (uploadingSlotsQueue.size() > 0) {
|
if (uploadingSlotsQueue.size() > 0) {
|
||||||
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
}
|
}
|
||||||
uploadingSlotsQueue.pop_front();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onFileUploaded(const QString& messageId, const QString& url)
|
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
||||||
{
|
{
|
||||||
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
|
QMap<QString, QVariant> cData = {
|
||||||
if (itr != pendingMessages.end()) {
|
{"attachPath", path}
|
||||||
sendMessageWithLocalUploadedFile(itr->second, url);
|
};
|
||||||
pendingMessages.erase(itr);
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
|
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::onFileUploadError(const QString& messageId, const QString& errMsg)
|
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up)
|
||||||
{
|
{
|
||||||
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
|
if (up) {
|
||||||
if (itr != pendingMessages.end()) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
pendingMessages.erase(itr);
|
if (info.account == acc->getName()) {
|
||||||
|
handleUploadError(info.jid, info.messageId, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url)
|
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);
|
||||||
|
pendingStateMessages.erase(jid);
|
||||||
|
requestChangeMessage(jid, messageId, {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
|
{"errorText", errorText}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
||||||
|
{
|
||||||
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
|
if (info.account == acc->getName()) {
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
||||||
|
if (ri != 0) {
|
||||||
|
Shared::Message msg = ri->getMessage(info.messageId);
|
||||||
|
sendMessageWithLocalUploadedFile(msg, path, false);
|
||||||
|
} 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)
|
||||||
{
|
{
|
||||||
msg.setOutOfBandUrl(url);
|
msg.setOutOfBandUrl(url);
|
||||||
if (msg.getBody().size() == 0) {
|
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
||||||
msg.setBody(url);
|
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
||||||
}
|
}
|
||||||
sendMessage(msg);
|
performSending(msg, newMessage);
|
||||||
//TODO removal/progress update
|
//TODO removal/progress update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::set<QString> allowerToChangeKeys({
|
||||||
|
"attachPath",
|
||||||
|
"outOfBandUrl",
|
||||||
|
"state",
|
||||||
|
"errorText"
|
||||||
|
});
|
||||||
|
|
||||||
|
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
|
if (cnt != 0) {
|
||||||
|
bool allSupported = true;
|
||||||
|
QString unsupportedString;
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
||||||
|
if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
||||||
|
allSupported = false; //to make a message to look like if it was edited
|
||||||
|
unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method
|
||||||
|
break; //because the underlying tech assumes that the change is initiated by user
|
||||||
|
} //not by system
|
||||||
|
}
|
||||||
|
if (allSupported) {
|
||||||
|
cnt->changeMessage(messageId, data);
|
||||||
|
emit acc->changeMessage(jid, messageId, data);
|
||||||
|
} else {
|
||||||
|
qDebug() << "A request to change message" << messageId << "of conversation" << jid << "with following data" << data;
|
||||||
|
qDebug() << "only limited set of dataFields are supported yet here, and" << unsupportedString << "isn't one of them, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <QXmppHttpUploadIq.h>
|
#include <QXmppHttpUploadIq.h>
|
||||||
|
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
|
#include <shared/messageinfo.h>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -44,8 +45,7 @@ public:
|
|||||||
MessageHandler(Account* account);
|
MessageHandler(Account* account);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void sendMessage(Shared::Message data);
|
void sendMessage(const Shared::Message& data);
|
||||||
void sendMessage(const Shared::Message& data, const QString& path);
|
|
||||||
void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const;
|
void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@ -55,20 +55,24 @@ public slots:
|
|||||||
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);
|
||||||
void onFileUploaded(const QString& messageId, const QString& url);
|
void onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void onFileUploadError(const QString& messageId, const QString& errMsg);
|
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
|
void onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& path, bool up);
|
||||||
|
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
||||||
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
||||||
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
||||||
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url);
|
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
|
||||||
|
void performSending(Shared::Message data, bool newMessage = true);
|
||||||
|
void prepareUpload(const Shared::Message& data);
|
||||||
|
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Account* acc;
|
Account* acc;
|
||||||
std::map<QString, QString> pendingStateMessages;
|
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
||||||
std::map<QString, Shared::Message> pendingMessages;
|
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
||||||
std::deque<std::pair<QString, Shared::Message>> uploadingSlotsQueue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ 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, &RosterHandler::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::requestVCard, this->acc, &Account::requestVCard);
|
connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
|
||||||
@ -315,14 +315,6 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactHistoryResponse(const std::list<Shared::Message>& list)
|
|
||||||
{
|
|
||||||
RosterItem* contact = static_cast<RosterItem*>(sender());
|
|
||||||
|
|
||||||
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
|
||||||
emit acc->responseArchive(contact->jid, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
||||||
{
|
{
|
||||||
RosterItem* item = 0;
|
RosterItem* item = 0;
|
||||||
|
@ -86,7 +86,6 @@ private slots:
|
|||||||
void onContactGroupRemoved(const QString& group);
|
void onContactGroupRemoved(const QString& group);
|
||||||
void onContactNameChanged(const QString& name);
|
void onContactNameChanged(const QString& name);
|
||||||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
void onContactHistoryResponse(const std::list<Shared::Message>& list);
|
|
||||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -16,23 +16,26 @@
|
|||||||
* 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 "ui/squawk.h"
|
#include "../shared/global.h"
|
||||||
#include "core/squawk.h"
|
#include "../shared/messageinfo.h"
|
||||||
|
#include "../ui/squawk.h"
|
||||||
#include "signalcatcher.h"
|
#include "signalcatcher.h"
|
||||||
#include "shared/global.h"
|
#include "squawk.h"
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
|
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<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");
|
||||||
@ -96,10 +99,7 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
||||||
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
||||||
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
||||||
QObject::connect(&w, qOverload<const QString&, const Shared::Message&>(&Squawk::sendMessage),
|
QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage);
|
||||||
squawk, qOverload<const QString&, const Shared::Message&>(&Core::Squawk::sendMessage));
|
|
||||||
QObject::connect(&w, qOverload<const QString&, const Shared::Message&, const QString&>(&Squawk::sendMessage),
|
|
||||||
squawk, qOverload<const QString&, const Shared::Message&, const QString&>(&Core::Squawk::sendMessage));
|
|
||||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
||||||
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
||||||
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
||||||
@ -109,14 +109,14 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
||||||
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
||||||
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
||||||
QObject::connect(&w, &Squawk::fileLocalPathRequest, squawk, &Core::Squawk::fileLocalPathRequest);
|
QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest);
|
||||||
QObject::connect(&w, &Squawk::downloadFileRequest, squawk, &Core::Squawk::downloadFileRequest);
|
|
||||||
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
||||||
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
||||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
||||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
||||||
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
||||||
|
QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid);
|
||||||
|
|
||||||
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
||||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
||||||
@ -141,11 +141,10 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
||||||
QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse);
|
QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete);
|
||||||
QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::fileProgress);
|
QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete);
|
||||||
QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::fileError);
|
QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress);
|
||||||
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
|
QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError);
|
||||||
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
||||||
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
||||||
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
@ -16,13 +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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
|
||||||
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
running(false),
|
running(false),
|
||||||
manager(0),
|
manager(0),
|
||||||
files("files"),
|
storage("fileURLStorage"),
|
||||||
downloads(),
|
downloads(),
|
||||||
uploads()
|
uploads()
|
||||||
{
|
{
|
||||||
@ -33,60 +37,31 @@ Core::NetworkAccess::~NetworkAccess()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, 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()) {
|
||||||
Transfer* dwn = itr->second;
|
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
|
||||||
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
|
||||||
if (mItr == dwn->messages.end()) {
|
|
||||||
dwn->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit downloadFileProgress(messageId, dwn->progress);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
QString path = files.getRecord(url);
|
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
|
||||||
QFileInfo info(path);
|
if (p.first.size() > 0) {
|
||||||
|
QFileInfo info(p.first);
|
||||||
if (info.exists() && info.isFile()) {
|
if (info.exists() && info.isFile()) {
|
||||||
emit fileLocalPathResponse(messageId, path);
|
emit downloadFileComplete(p.second, p.first);
|
||||||
} else {
|
} else {
|
||||||
files.removeRecord(url);
|
startDownload(p.second, url);
|
||||||
emit fileLocalPathResponse(messageId, "");
|
}
|
||||||
|
} else {
|
||||||
|
startDownload(p.second, url);
|
||||||
}
|
}
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const Archive::NotFound& e) {
|
||||||
emit fileLocalPathResponse(messageId, "");
|
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);
|
||||||
|
startDownload(std::list<Shared::MessageInfo>(), url);
|
||||||
} catch (const Archive::Unknown& e) {
|
} catch (const Archive::Unknown& e) {
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
qDebug() << "Error requesting file path:" << e.what();
|
||||||
emit fileLocalPathResponse(messageId, "");
|
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url)
|
|
||||||
{
|
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
|
||||||
if (itr != downloads.end()) {
|
|
||||||
Transfer* dwn = itr->second;
|
|
||||||
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
|
||||||
if (mItr == dwn->messages.end()) {
|
|
||||||
dwn->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit downloadFileProgress(messageId, dwn->progress);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
QString path = files.getRecord(url);
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startDownload(messageId, url);
|
|
||||||
}
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
startDownload(messageId, url);
|
|
||||||
} catch (const Archive::Unknown& e) {
|
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
|
||||||
emit downloadFileError(messageId, QString("Database error: ") + e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +70,7 @@ void Core::NetworkAccess::start()
|
|||||||
{
|
{
|
||||||
if (!running) {
|
if (!running) {
|
||||||
manager = new QNetworkAccessManager();
|
manager = new QNetworkAccessManager();
|
||||||
files.open();
|
storage.open();
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +78,7 @@ void Core::NetworkAccess::start()
|
|||||||
void Core::NetworkAccess::stop()
|
void Core::NetworkAccess::stop()
|
||||||
{
|
{
|
||||||
if (running) {
|
if (running) {
|
||||||
files.close();
|
storage.close();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
manager = 0;
|
manager = 0;
|
||||||
running = false;
|
running = false;
|
||||||
@ -128,9 +103,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||||||
qreal total = bytesTotal;
|
qreal total = bytesTotal;
|
||||||
qreal progress = received/total;
|
qreal progress = received/total;
|
||||||
dwn->progress = progress;
|
dwn->progress = progress;
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
emit loadFileProgress(dwn->messages, progress, false);
|
||||||
emit downloadFileProgress(*mItr, progress);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,9 +119,7 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
|||||||
if (errorText.size() > 0) {
|
if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
emit loadFileError(dwn->messages, errorText, false);
|
||||||
emit downloadFileError(*mItr, errorText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,50 +247,43 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
|
|
||||||
void Core::NetworkAccess::onDownloadFinished()
|
void Core::NetworkAccess::onDownloadFinished()
|
||||||
{
|
{
|
||||||
QString path("");
|
|
||||||
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);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request is done but there is no record of it being downloaded, ignoring";
|
||||||
} else {
|
} else {
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
if (dwn->success) {
|
if (dwn->success) {
|
||||||
qDebug() << "download success for" << url;
|
qDebug() << "download success for" << url;
|
||||||
QStringList hops = url.split("/");
|
QStringList hops = url.split("/");
|
||||||
QString fileName = hops.back();
|
QString fileName = hops.back();
|
||||||
QStringList parts = fileName.split(".");
|
QString jid;
|
||||||
path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/";
|
if (dwn->messages.size() > 0) {
|
||||||
QString suffix("");
|
jid = dwn->messages.front().jid;
|
||||||
QStringList::const_iterator sItr = parts.begin();
|
|
||||||
QString realName = *sItr;
|
|
||||||
++sItr;
|
|
||||||
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
|
||||||
suffix += "." + (*sItr);
|
|
||||||
}
|
|
||||||
QString postfix("");
|
|
||||||
QFileInfo proposedName(path + realName + postfix + suffix);
|
|
||||||
int counter = 0;
|
|
||||||
while (proposedName.exists()) {
|
|
||||||
postfix = QString("(") + std::to_string(++counter).c_str() + ")";
|
|
||||||
proposedName = QFileInfo(path + realName + postfix + suffix);
|
|
||||||
}
|
}
|
||||||
|
QString path = prepareDirectory(jid);
|
||||||
|
if (path.size() > 0) {
|
||||||
|
path = checkFileName(fileName, path);
|
||||||
|
|
||||||
path = proposedName.absoluteFilePath();
|
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
if (file.open(QIODevice::WriteOnly)) {
|
||||||
file.write(dwn->reply->readAll());
|
file.write(dwn->reply->readAll());
|
||||||
file.close();
|
file.close();
|
||||||
files.addRecord(url, path);
|
storage.setPath(url, path);
|
||||||
qDebug() << "file" << path << "was successfully downloaded";
|
qDebug() << "file" << path << "was successfully downloaded";
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't save file" << path;
|
qDebug() << "couldn't save file" << path;
|
||||||
path = "";
|
path = QString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
if (path.size() > 0) {
|
||||||
emit fileLocalPathResponse(*mItr, path);
|
emit downloadFileComplete(dwn->messages, path);
|
||||||
|
} else {
|
||||||
|
//TODO do I need to handle the failure here or it's already being handled in error?
|
||||||
|
//emit loadFileError(dwn->messages, path, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dwn->reply->deleteLater();
|
dwn->reply->deleteLater();
|
||||||
@ -328,9 +292,9 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url)
|
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url)
|
||||||
{
|
{
|
||||||
Transfer* dwn = new Transfer({{messageId}, 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);
|
||||||
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
||||||
@ -341,7 +305,7 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString&
|
|||||||
#endif
|
#endif
|
||||||
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
||||||
downloads.insert(std::make_pair(url, dwn));
|
downloads.insert(std::make_pair(url, dwn));
|
||||||
emit downloadFileProgress(messageId, 0);
|
emit loadFileProgress(dwn->messages, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
||||||
@ -350,16 +314,16 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
|||||||
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);
|
||||||
if (itr == uploads.end()) {
|
if (itr == uploads.end()) {
|
||||||
qDebug() << "an error uploading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping";
|
qDebug() << "an error uploading" << url << ": the request is reporting an error but there is no record of it being uploading, ignoring";
|
||||||
} else {
|
} else {
|
||||||
QString errorText = getErrorText(code);
|
QString errorText = getErrorText(code);
|
||||||
if (errorText.size() > 0) {
|
if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
emit loadFileError(upl->messages, errorText, true);
|
||||||
emit uploadFileError(*mItr, errorText);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO deletion?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,17 +333,14 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
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);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping";
|
qDebug() << "an error uploading" << url << ": the request is done there is no record of it being uploading, ignoring";
|
||||||
} else {
|
} else {
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
if (upl->success) {
|
if (upl->success) {
|
||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
files.addRecord(upl->url, upl->path);
|
|
||||||
|
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
storage.addFile(upl->messages, upl->url, upl->path);
|
||||||
emit fileLocalPathResponse(*mItr, upl->path);
|
emit uploadFileComplete(upl->messages, upl->url);
|
||||||
emit uploadFileComplete(*mItr, upl->url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upl->reply->deleteLater();
|
upl->reply->deleteLater();
|
||||||
@ -403,94 +364,29 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
|
|||||||
qreal total = bytesTotal;
|
qreal total = bytesTotal;
|
||||||
qreal progress = received/total;
|
qreal progress = received/total;
|
||||||
upl->progress = progress;
|
upl->progress = progress;
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
emit loadFileProgress(upl->messages, progress, true);
|
||||||
emit uploadFileProgress(*mItr, progress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0});
|
|
||||||
QNetworkRequest req(url);
|
|
||||||
QFile* file = new QFile(path);
|
|
||||||
if (file->open(QIODevice::ReadOnly)) {
|
|
||||||
upl->reply = manager->put(req, file);
|
|
||||||
|
|
||||||
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
||||||
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onUploadError);
|
|
||||||
#else
|
|
||||||
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
|
||||||
uploads.insert(std::make_pair(url, upl));
|
|
||||||
emit downloadFileProgress(messageId, 0);
|
|
||||||
} else {
|
|
||||||
qDebug() << "couldn't upload file" << path;
|
|
||||||
emit uploadFileError(messageId, "Error opening file");
|
|
||||||
delete file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
std::map<QString, Transfer*>::iterator itr = uploads.find(url);
|
|
||||||
if (itr != uploads.end()) {
|
|
||||||
Transfer* upl = itr->second;
|
|
||||||
std::set<QString>::const_iterator mItr = upl->messages.find(messageId);
|
|
||||||
if (mItr == upl->messages.end()) {
|
|
||||||
upl->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit uploadFileProgress(messageId, upl->progress);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
QString ePath = files.getRecord(url);
|
|
||||||
if (ePath == path) {
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
files.changeRecord(url, path);
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
} catch (const Archive::Unknown& e) {
|
|
||||||
qDebug() << "Error requesting file path on upload:" << e.what();
|
|
||||||
emit uploadFileError(messageId, QString("Database error: ") + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
||||||
{
|
{
|
||||||
return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url
|
QString p;
|
||||||
|
|
||||||
|
try {
|
||||||
|
p = storage.getUrl(path);
|
||||||
|
} catch (const Archive::NotFound& err) {
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId)
|
void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
||||||
{
|
|
||||||
return false; //TODO this is a way to avoid parallel uploading of the same files by different chats
|
|
||||||
// message is is supposed to be added to the uploading messageids list
|
|
||||||
// the result should be true if there was an uploading file with this path
|
|
||||||
// message id can be empty, then it's just to check and not to add
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFile(const QString& messageId, 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({{messageId}, 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);
|
||||||
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
||||||
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
||||||
@ -506,10 +402,99 @@ void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& pa
|
|||||||
#endif
|
#endif
|
||||||
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
||||||
uploads.insert(std::make_pair(put.toString(), upl));
|
uploads.insert(std::make_pair(put.toString(), upl));
|
||||||
emit downloadFileProgress(messageId, 0);
|
emit loadFileProgress(upl->messages, 0, true);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't upload file" << path;
|
qDebug() << "couldn't upload file" << path;
|
||||||
emit uploadFileError(messageId, "Error opening file");
|
emit loadFileError(upl->messages, "Error opening file", true);
|
||||||
delete file;
|
delete file;
|
||||||
|
delete upl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
storage.addFile(url, account, jid, id);
|
||||||
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
|
if (itr != downloads.end()) {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
storage.addFile(url, path, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Transfer* info = pair.second;
|
||||||
|
if (pair.second->path == path) {
|
||||||
|
std::list<Shared::MessageInfo>& messages = info->messages;
|
||||||
|
bool dup = false;
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
if (info.account == acc && info.jid == jid && info.messageId == id) {
|
||||||
|
dup = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dup) {
|
||||||
|
info->messages.emplace_back(acc, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
||||||
|
{
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
|
path += "/" + QApplication::applicationName();
|
||||||
|
if (jid.size() > 0) {
|
||||||
|
path += "/" + jid;
|
||||||
|
}
|
||||||
|
QDir location(path);
|
||||||
|
|
||||||
|
if (!location.exists()) {
|
||||||
|
bool res = location.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
||||||
|
{
|
||||||
|
QStringList parts = name.split(".");
|
||||||
|
QString suffix("");
|
||||||
|
QStringList::const_iterator sItr = parts.begin();
|
||||||
|
QString realName = *sItr;
|
||||||
|
++sItr;
|
||||||
|
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
||||||
|
suffix += "." + (*sItr);
|
||||||
|
}
|
||||||
|
QString postfix("");
|
||||||
|
QFileInfo proposedName(path + "/" + realName + suffix);
|
||||||
|
int counter = 0;
|
||||||
|
while (proposedName.exists()) {
|
||||||
|
QString count = QString("(") + std::to_string(++counter).c_str() + ")";
|
||||||
|
proposedName = QFileInfo(path + "/" + realName + count + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return proposedName.absoluteFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
return storage.addMessageAndCheckForPath(url, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path)
|
||||||
|
{
|
||||||
|
return storage.deletedFile(path);
|
||||||
|
}
|
||||||
|
@ -29,13 +29,15 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "storage.h"
|
#include "urlstorage.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//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
|
||||||
@ -48,26 +50,27 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
QString getFileRemoteUrl(const QString& path);
|
QString getFileRemoteUrl(const QString& path);
|
||||||
bool isUploading(const QString& path, const QString& messageId = "");
|
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
void uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
||||||
|
bool checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path);
|
||||||
|
std::list<Shared::MessageInfo> reportPathInvalid(const QString& path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
void downloadFileProgress(const QString& messageId, qreal value);
|
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
||||||
void downloadFileError(const QString& messageId, const QString& path);
|
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
void uploadFileProgress(const QString& messageId, qreal value);
|
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void uploadFileError(const QString& messageId, const QString& path);
|
|
||||||
void uploadFileComplete(const QString& messageId, const QString& url);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
void downladFile(const QString& url);
|
||||||
void downladFileRequest(const QString& messageId, const QString& url);
|
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void uploadFileRequest(const QString& messageId, const QString& url, const QString& path);
|
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startDownload(const QString& messageId, const QString& url);
|
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
void startUpload(const QString& messageId, const QString& url, const QString& path);
|
|
||||||
QString getErrorText(QNetworkReply::NetworkError code);
|
QString getErrorText(QNetworkReply::NetworkError code);
|
||||||
|
QString prepareDirectory(const QString& jid);
|
||||||
|
QString checkFileName(const QString& name, const QString& path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
@ -80,12 +83,12 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
bool running;
|
bool running;
|
||||||
QNetworkAccessManager* manager;
|
QNetworkAccessManager* manager;
|
||||||
Storage files;
|
UrlStorage storage;
|
||||||
std::map<QString, Transfer*> downloads;
|
std::map<QString, Transfer*> downloads;
|
||||||
std::map<QString, Transfer*> uploads;
|
std::map<QString, Transfer*> uploads;
|
||||||
|
|
||||||
struct Transfer {
|
struct Transfer {
|
||||||
std::set<QString> messages;
|
std::list<Shared::MessageInfo> messages;
|
||||||
qreal progress;
|
qreal progress;
|
||||||
QNetworkReply* reply;
|
QNetworkReply* reply;
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -1,37 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
project(pse)
|
|
||||||
|
|
||||||
if (WITH_KWALLET)
|
if (WITH_KWALLET)
|
||||||
set(CMAKE_AUTOMOC ON)
|
target_sources(squawk PRIVATE
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Gui CONFIG REQUIRED)
|
|
||||||
|
|
||||||
get_target_property(KWALLET_INTERFACE_INCLUDE_DIRECTORIES KF5::Wallet INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
get_target_property(Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES Qt5::Gui INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
|
|
||||||
set(kwalletPSE_SRC
|
|
||||||
kwallet.cpp
|
kwallet.cpp
|
||||||
|
kwallet.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(kwalletPSE ${kwalletPSE_SRC})
|
add_subdirectory(wrappers)
|
||||||
|
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||||
target_include_directories(kwalletPSE PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
endif ()
|
||||||
target_include_directories(kwalletPSE PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
|
||||||
|
|
||||||
target_link_libraries(kwalletPSE Qt5::Core)
|
|
||||||
|
|
||||||
set(kwalletW_SRC
|
|
||||||
wrappers/kwallet.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(kwalletWrapper SHARED ${kwalletW_SRC})
|
|
||||||
|
|
||||||
target_include_directories(kwalletWrapper PUBLIC ${KWALLET_INTERFACE_INCLUDE_DIRECTORIES})
|
|
||||||
target_include_directories(kwalletWrapper PUBLIC ${Qt5GUI_INTERFACE_INCLUDE_DIRECTORIES})
|
|
||||||
|
|
||||||
target_link_libraries(kwalletWrapper KF5::Wallet)
|
|
||||||
target_link_libraries(kwalletWrapper Qt5::Core)
|
|
||||||
|
|
||||||
install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
endif()
|
|
||||||
|
2
core/passwordStorageEngines/wrappers/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
add_library(kwalletWrapper SHARED kwallet.cpp)
|
||||||
|
target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet)
|
@ -122,7 +122,22 @@ void Core::RosterItem::nextRequest()
|
|||||||
{
|
{
|
||||||
if (syncronizing) {
|
if (syncronizing) {
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
emit historyResponse(responseCache);
|
bool last = false;
|
||||||
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
|
QString firstId = archive->oldestId();
|
||||||
|
if (responseCache.size() == 0) {
|
||||||
|
if (requestedBefore == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (responseCache.front().getId() == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
emit historyResponse(responseCache, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requestCache.size() > 0) {
|
if (requestCache.size() > 0) {
|
||||||
@ -360,6 +375,11 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
archiveState = complete;
|
archiveState = complete;
|
||||||
archive->setFromTheBeginning(true);
|
archive->setFromTheBeginning(true);
|
||||||
}
|
}
|
||||||
|
if (added == 0 && wasEmpty) {
|
||||||
|
archiveState = empty;
|
||||||
|
nextRequest();
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
QString before;
|
QString before;
|
||||||
if (responseCache.size() > 0) {
|
if (responseCache.size() > 0) {
|
||||||
@ -378,7 +398,7 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
} catch (const Archive::Empty& e) {
|
} catch (const Archive::Empty& e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!found || requestedCount > responseCache.size()) {
|
if (!found || requestedCount > int(responseCache.size())) {
|
||||||
if (archiveState == complete) {
|
if (archiveState == complete) {
|
||||||
nextRequest();
|
nextRequest();
|
||||||
} else {
|
} else {
|
||||||
@ -529,7 +549,7 @@ void Core::RosterItem::clearArchiveRequests()
|
|||||||
requestedBefore = "";
|
requestedBefore = "";
|
||||||
for (const std::pair<int, QString>& pair : requestCache) {
|
for (const std::pair<int, QString>& pair : requestCache) {
|
||||||
if (pair.first != -1) {
|
if (pair.first != -1) {
|
||||||
emit historyResponse(responseCache); //just to notify those who still waits with whatever happened to be left in caches yet
|
emit historyResponse(responseCache, false); //just to notify those who still waits with whatever happened to be left in caches yet
|
||||||
}
|
}
|
||||||
responseCache.clear();
|
responseCache.clear();
|
||||||
}
|
}
|
||||||
@ -549,3 +569,20 @@ void Core::RosterItem::downgradeDatabaseState()
|
|||||||
archiveState = ArchiveState::chunk;
|
archiveState = ArchiveState::chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shared::Message Core::RosterItem::getMessage(const QString& id)
|
||||||
|
{
|
||||||
|
for (const Shared::Message& msg : appendCache) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Shared::Message& msg : hisoryCache) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return archive->getElement(id);
|
||||||
|
}
|
||||||
|
@ -78,10 +78,12 @@ public:
|
|||||||
void clearArchiveRequests();
|
void clearArchiveRequests();
|
||||||
void downgradeDatabaseState();
|
void downgradeDatabaseState();
|
||||||
|
|
||||||
|
Shared::Message getMessage(const QString& id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nameChanged(const QString& name);
|
void nameChanged(const QString& name);
|
||||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
void subscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
void historyResponse(const std::list<Shared::Message>& messages);
|
void historyResponse(const std::list<Shared::Message>& messages, bool last);
|
||||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||||
void avatarChanged(Shared::Avatar, const QString& path);
|
void avatarChanged(Shared::Avatar, const QString& path);
|
||||||
void requestVCard(const QString& jid);
|
void requestVCard(const QString& jid);
|
||||||
|
@ -32,11 +32,10 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
,kwallet()
|
,kwallet()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
|
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
|
||||||
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
|
connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError);
|
||||||
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
|
connect(&network, &NetworkAccess::downloadFileComplete, this, &Squawk::fileDownloadComplete);
|
||||||
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
|
connect(&network, &NetworkAccess::uploadFileComplete, this, &Squawk::fileUploadComplete);
|
||||||
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
|
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
@ -168,7 +167,7 @@ void Core::Squawk::addAccount(
|
|||||||
|
|
||||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
||||||
|
|
||||||
connect(acc, &Account::uploadFileError, this, &Squawk::uploadFileError);
|
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
|
||||||
|
|
||||||
QMap<QString, QVariant> map = {
|
QMap<QString, QVariant> map = {
|
||||||
{"login", login},
|
{"login", login},
|
||||||
@ -336,17 +335,6 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
|
|||||||
itr->second->sendMessage(data);
|
itr->second->sendMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path)
|
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
|
||||||
if (itr == amap.end()) {
|
|
||||||
qDebug("An attempt to send a message with non existing account, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
itr->second->sendMessage(data, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
@ -357,10 +345,10 @@ 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)
|
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);
|
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)
|
||||||
@ -604,14 +592,9 @@ 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::fileLocalPathRequest(const QString& messageId, const QString& url)
|
void Core::Squawk::fileDownloadRequest(const QString& url)
|
||||||
{
|
{
|
||||||
network.fileLocalPathRequest(messageId, url);
|
network.downladFile(url);
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::downloadFileRequest(const QString& messageId, const QString& url)
|
|
||||||
{
|
|
||||||
network.downladFileRequest(messageId, 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)
|
||||||
@ -762,3 +745,26 @@ void Core::Squawk::onWalletResponsePassword(const QString& login, const QString&
|
|||||||
emit changeAccount(login, {{"password", password}});
|
emit changeAccount(login, {{"password", password}});
|
||||||
accountReady();
|
accountReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
||||||
|
{
|
||||||
|
Account* acc = static_cast<Account*>(sender());
|
||||||
|
emit fileError({{acc->getName(), jid, id}}, errorText, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onLocalPathInvalid(const QString& path)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
|
||||||
|
|
||||||
|
QMap<QString, QVariant> data({
|
||||||
|
{"attachPath", ""}
|
||||||
|
});
|
||||||
|
for (const Shared::MessageInfo& info : list) {
|
||||||
|
AccountsMap::const_iterator itr = amap.find(info.account);
|
||||||
|
if (itr != amap.end()) {
|
||||||
|
itr->second->requestChangeMessage(info.jid, info.messageId, data);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Reacting on failure to reach file" << path << "there was an attempt to change message in account" << info.account << "which doesn't exist, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -51,31 +51,39 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void quit();
|
void quit();
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
void newAccount(const QMap<QString, QVariant>&);
|
void newAccount(const QMap<QString, QVariant>&);
|
||||||
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
|
|
||||||
void addGroup(const QString& account, const QString& name);
|
void addGroup(const QString& account, const QString& name);
|
||||||
void removeGroup(const QString& account, const QString& name);
|
void removeGroup(const QString& account, const QString& name);
|
||||||
|
|
||||||
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
void removeContact(const QString& account, const QString& jid);
|
void removeContact(const QString& account, const QString& jid);
|
||||||
void removeContact(const QString& account, const QString& jid, const QString& group);
|
void removeContact(const QString& account, const QString& jid, const QString& group);
|
||||||
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removePresence(const QString& account, const QString& jid, const QString& name);
|
void removePresence(const QString& account, const QString& jid, const QString& name);
|
||||||
|
|
||||||
void stateChanged(Shared::Availability state);
|
void stateChanged(Shared::Availability state);
|
||||||
|
|
||||||
void accountMessage(const QString& account, const Shared::Message& data);
|
void accountMessage(const QString& account, const Shared::Message& data);
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list);
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void removeRoom(const QString& account, const QString jid);
|
void removeRoom(const QString& account, const QString jid);
|
||||||
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
||||||
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
|
||||||
void downloadFileError(const QString& messageId, const QString& error);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void downloadFileProgress(const QString& messageId, qreal value);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void uploadFileError(const QString& messageId, const QString& error);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void uploadFileProgress(const QString& messageId, qreal value);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
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);
|
void requestPassword(const QString& account);
|
||||||
@ -83,15 +91,18 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void newAccountRequest(const QMap<QString, QVariant>& map);
|
void newAccountRequest(const QMap<QString, QVariant>& map);
|
||||||
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
||||||
void removeAccountRequest(const QString& name);
|
void removeAccountRequest(const QString& name);
|
||||||
void connectAccount(const QString& account);
|
void connectAccount(const QString& account);
|
||||||
void disconnectAccount(const QString& account);
|
void disconnectAccount(const QString& account);
|
||||||
|
|
||||||
void changeState(Shared::Availability state);
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
void sendMessage(const QString& account, const Shared::Message& data);
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
void sendMessage(const QString& account, const Shared::Message& data, const QString& path);
|
|
||||||
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
||||||
|
|
||||||
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 addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
@ -99,15 +110,18 @@ 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 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);
|
||||||
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void removeRoomRequest(const QString& account, const QString& jid);
|
void removeRoomRequest(const QString& account, const QString& jid);
|
||||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
|
||||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
|
|
||||||
void requestVCard(const QString& account, const QString& jid);
|
void requestVCard(const QString& account, const QString& jid);
|
||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::deque<Account*> Accounts;
|
typedef std::deque<Account*> Accounts;
|
||||||
@ -146,7 +160,7 @@ private slots:
|
|||||||
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemovePresence(const QString& jid, const QString& name);
|
void onAccountRemovePresence(const QString& jid, const QString& name);
|
||||||
void onAccountMessage(const Shared::Message& data);
|
void onAccountMessage(const Shared::Message& data);
|
||||||
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemoveRoom(const QString jid);
|
void onAccountRemoveRoom(const QString jid);
|
||||||
@ -155,6 +169,8 @@ private slots:
|
|||||||
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
||||||
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
||||||
|
|
||||||
void onWalletOpened(bool success);
|
void onWalletOpened(bool success);
|
||||||
void onWalletResponsePassword(const QString& login, const QString& password);
|
void onWalletResponsePassword(const QString& login, const QString& password);
|
||||||
void onWalletRejectPassword(const QString& login);
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
491
core/urlstorage.cpp
Normal 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;
|
||||||
|
}
|
99
core/urlstorage.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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_URLSTORAGE_H
|
||||||
|
#define CORE_URLSTORAGE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <lmdb.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "archive.h"
|
||||||
|
#include <shared/messageinfo.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class UrlStorage
|
||||||
|
{
|
||||||
|
class UrlInfo;
|
||||||
|
public:
|
||||||
|
UrlStorage(const QString& name);
|
||||||
|
~UrlStorage();
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void addFile(const QString& url);
|
||||||
|
void addFile(const QString& url, const QString& path);
|
||||||
|
void addFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path); //this one overwrites all that was
|
||||||
|
std::list<Shared::MessageInfo> removeFile(const QString& url); //removes entry like it never was in the database, returns affected message infos
|
||||||
|
std::list<Shared::MessageInfo> deletedFile(const QString& path); //empties the localPath of the entry, returns affected message infos
|
||||||
|
std::list<Shared::MessageInfo> setPath(const QString& url, const QString& path);
|
||||||
|
QString getUrl(const QString& path);
|
||||||
|
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
|
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString name;
|
||||||
|
bool opened;
|
||||||
|
MDB_env* environment;
|
||||||
|
MDB_dbi base;
|
||||||
|
MDB_dbi map;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeInfo(const QString& key, const UrlInfo& info, 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");
|
||||||
|
|
||||||
|
private:
|
||||||
|
class UrlInfo {
|
||||||
|
public:
|
||||||
|
UrlInfo(const QString& path);
|
||||||
|
UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs);
|
||||||
|
UrlInfo();
|
||||||
|
~UrlInfo();
|
||||||
|
|
||||||
|
void serialize(QDataStream& data) const;
|
||||||
|
void deserialize(QDataStream& data);
|
||||||
|
|
||||||
|
QString getPath() const;
|
||||||
|
bool hasPath() const;
|
||||||
|
void setPath(const QString& path);
|
||||||
|
|
||||||
|
bool addMessage(const QString& acc, const QString& jid, const QString& id);
|
||||||
|
void getMessages(std::list<Shared::MessageInfo>& container) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString localPath;
|
||||||
|
std::list<Shared::MessageInfo> messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_URLSTORAGE_H
|
1
external/signal-protocol-c
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28
|
12
external/simpleCrypt/CMakeLists.txt
vendored
@ -1,16 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(simplecrypt)
|
project(simplecrypt LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
find_package(Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
|
||||||
set(simplecrypt_SRC
|
add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h)
|
||||||
simplecrypt.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
|
||||||
add_library(simpleCrypt ${simplecrypt_SRC})
|
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
|
||||||
target_link_libraries(simpleCrypt Qt5::Core)
|
target_link_libraries(simpleCrypt Qt5::Core)
|
||||||
|
3
packaging/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
configure_file(squawk.desktop squawk.desktop COPYONLY)
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
4
plugins/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
if (WITH_KIO)
|
||||||
|
add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp)
|
||||||
|
target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets)
|
||||||
|
endif ()
|
8
plugins/openfilemanagerwindowjob.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <QUrl>
|
||||||
|
#include <QObject>
|
||||||
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
|
||||||
|
extern "C" void highlightInFileManager(const QUrl& url) {
|
||||||
|
KIO::OpenFileManagerWindowJob* job = KIO::highlightInFileManager({url});
|
||||||
|
QObject::connect(job, &KIO::OpenFileManagerWindowJob::result, job, &KIO::OpenFileManagerWindowJob::deleteLater);
|
||||||
|
}
|
25
qomemo/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
bundle.cpp
|
||||||
|
bundle.h
|
||||||
|
database.cpp
|
||||||
|
database.h
|
||||||
|
device.cpp
|
||||||
|
device.h
|
||||||
|
device_key_storage.cpp
|
||||||
|
device_key_storage.h
|
||||||
|
device_service.cpp
|
||||||
|
device_service.h
|
||||||
|
key.cpp
|
||||||
|
key.h
|
||||||
|
qomemo.cpp
|
||||||
|
qomemo.h
|
||||||
|
sce.cpp
|
||||||
|
sce.h
|
||||||
|
user_device_list.cpp
|
||||||
|
user_device_list.h
|
||||||
|
qxmpp_omemo_manager.cpp
|
||||||
|
qxmpp_omemo_manager.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(signal)
|
||||||
|
add_subdirectory(variant)
|
5
qomemo/bundle.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bundle.h"
|
34
qomemo/bundle.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
class QXmppPubSubIq;
|
||||||
|
|
||||||
|
class QXmppElement;
|
||||||
|
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class PreKey {
|
||||||
|
public:
|
||||||
|
int id;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Bundle {
|
||||||
|
public:
|
||||||
|
QByteArray spk;
|
||||||
|
int spkId;
|
||||||
|
QByteArray spks;
|
||||||
|
QByteArray ik;
|
||||||
|
QList<PreKey> prekeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
180
qomemo/database.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QException>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
Database::Database(QString jid) : jid(std::move(jid)) {
|
||||||
|
auto cacheLocation =
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||||
|
auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid);
|
||||||
|
QDir cache(path);
|
||||||
|
|
||||||
|
if (!cache.exists() && !cache.mkpath(path)) {
|
||||||
|
qWarning() << "Could not create:" << path;
|
||||||
|
throw QException();
|
||||||
|
}
|
||||||
|
|
||||||
|
mdb_env_create(&env);
|
||||||
|
|
||||||
|
mdb_env_set_maxdbs(env, 5);
|
||||||
|
mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL);
|
||||||
|
mdb_env_open(env, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys);
|
||||||
|
mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices);
|
||||||
|
mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::~Database() {
|
||||||
|
mdb_dbi_close(env, dbiKeys);
|
||||||
|
mdb_dbi_close(env, dbiDevices);
|
||||||
|
mdb_dbi_close(env, dbiIdentityKeys);
|
||||||
|
mdb_env_close(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QByteArray> Database::loadIdentityKeySecret(int deviceId) { return std::nullopt; }
|
||||||
|
|
||||||
|
bool Database::saveIdentityKeySecret(int deviceId,
|
||||||
|
const QByteArray &identityKeySecret) {
|
||||||
|
MDB_val mdbKey, mdbValue;
|
||||||
|
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
|
||||||
|
|
||||||
|
mdbKey.mv_data = key.data();
|
||||||
|
mdbKey.mv_size = key.size();
|
||||||
|
|
||||||
|
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data());
|
||||||
|
mdbValue.mv_size = identityKeySecret.size();
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE);
|
||||||
|
if (!err) {
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "could not save identity key secret:" << mdb_strerror(err);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> Database::loadActiveDeviceId() {
|
||||||
|
MDB_val key, value;
|
||||||
|
|
||||||
|
key.mv_data = (void *) "active";
|
||||||
|
key.mv_size = sizeof("active");
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
|
||||||
|
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not load active device id:" << mdb_strerror(err);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.mv_size != sizeof(int)) {
|
||||||
|
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = *reinterpret_cast<int *>(value.mv_data);
|
||||||
|
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveActiveDeviceId(int deviceId) {
|
||||||
|
MDB_val key, value;
|
||||||
|
|
||||||
|
key.mv_data = (void *) "active";
|
||||||
|
key.mv_size = sizeof("active");
|
||||||
|
|
||||||
|
value.mv_data = &deviceId;
|
||||||
|
value.mv_size = sizeof(deviceId);
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(env, nullptr, 0, &txn);
|
||||||
|
|
||||||
|
auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not save active device id" << mdb_strerror(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mdb_txn_commit(txn);
|
||||||
|
if (err) {
|
||||||
|
qWarning() << "could not save active device id" << mdb_strerror(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveIdentityKey(int deviceId, const QByteArray &identityKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QByteArray> Database::loadIdentityKey(int deviceId) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::containsPreKey() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<KeyPair> Database::loadPreKey(int deviceId, int id) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::savePreKey(int deviceId, int id, const KeyPair &preKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Bundle> Database::loadBundle(int deviceId) {
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
auto ik = loadIdentityKey(deviceId);
|
||||||
|
|
||||||
|
result.ik = ik.value();
|
||||||
|
|
||||||
|
auto spk = loadSignedPreKey(deviceId);
|
||||||
|
|
||||||
|
result.spk = spk->key.publicKey;
|
||||||
|
result.spks = spk->signature;
|
||||||
|
result.spkId = spk->id;
|
||||||
|
|
||||||
|
// PreKeys
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveBundle(int deviceId, const Bundle &bundle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<SignedPreKey> Database::loadSignedPreKey(int deviceId) {
|
||||||
|
return std::optional<SignedPreKey>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::saveSignedPreKey(int deviceId, const SignedPreKey &signedPreKey) {
|
||||||
|
return false;
|
||||||
|
}
|
56
qomemo/database.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QString>
|
||||||
|
#include <lmdb.h>
|
||||||
|
|
||||||
|
#include "key.h"
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Bundle;
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
public:
|
||||||
|
explicit Database(QString jid);
|
||||||
|
~Database();
|
||||||
|
Database(const Database &) = delete;
|
||||||
|
Database(Database &&) = delete;
|
||||||
|
Database &operator=(const Database &) = delete;
|
||||||
|
|
||||||
|
// For local user
|
||||||
|
std::optional<int> loadActiveDeviceId();
|
||||||
|
bool saveActiveDeviceId(int deviceId);
|
||||||
|
|
||||||
|
std::optional<QByteArray> loadIdentityKeySecret(int deviceId);
|
||||||
|
bool saveIdentityKeySecret(int deviceId, const QByteArray &identityKeySecret);
|
||||||
|
|
||||||
|
std::optional<Bundle> loadBundle(int deviceId);
|
||||||
|
bool saveBundle(int deviceId, const Bundle& bundle);
|
||||||
|
|
||||||
|
// For any user
|
||||||
|
std::optional<QByteArray> loadIdentityKey(int deviceId);
|
||||||
|
bool saveIdentityKey(int deviceId, const QByteArray &identityKey);
|
||||||
|
|
||||||
|
bool containsPreKey();
|
||||||
|
std::optional<KeyPair> loadPreKey(int deviceId, int id);
|
||||||
|
bool savePreKey(int deviceId, int id, const KeyPair &preKey);
|
||||||
|
|
||||||
|
std::optional<SignedPreKey> loadSignedPreKey(int deviceId);
|
||||||
|
bool saveSignedPreKey(int deviceId, const SignedPreKey& signedPreKey);
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MDB_env *env{};
|
||||||
|
MDB_dbi dbiDevices{};
|
||||||
|
MDB_dbi dbiKeys{};
|
||||||
|
MDB_dbi dbiPreKeys{};
|
||||||
|
MDB_dbi dbiIdentityKeys{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
5
qomemo/device.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device.h"
|
25
qomemo/device.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
class QXmppElement;
|
||||||
|
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Device {
|
||||||
|
public:
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceList {
|
||||||
|
public:
|
||||||
|
QList<Device> devices;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
14
qomemo/device_key_storage.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device_key_storage.h"
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() {
|
||||||
|
QRandomGenerator random{};
|
||||||
|
|
||||||
|
return 1 + random.bounded(INT32_MAX - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) : deviceId(deviceId) {}
|
18
qomemo/device_key_storage.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class DeviceKeyStorage {
|
||||||
|
public:
|
||||||
|
static int generateDeviceId();
|
||||||
|
|
||||||
|
explicit DeviceKeyStorage(int deviceId);
|
||||||
|
|
||||||
|
const int deviceId;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
35
qomemo/device_service.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "device_service.h"
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
void DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
||||||
|
|
||||||
|
for (const auto &device : list.devices) {
|
||||||
|
qInfo() << "Got device for" << jid << ":" << device.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Database> DeviceService::getDatabase(const QString &jid) {
|
||||||
|
if (!databases.contains(jid)) {
|
||||||
|
databases.insert(jid, QSharedPointer<Database>::create(jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return databases[jid];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceService::addIdentity(const QString &jid, int deviceId, const QByteArray& publicKey) {
|
||||||
|
auto db = getDatabase(jid);
|
||||||
|
|
||||||
|
db->saveIdentityKey(deviceId, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceService::onDeviceListNotFound(const QString &jid) {
|
||||||
|
qInfo() << "Device list not found:" << jid;
|
||||||
|
}
|
38
qomemo/device_service.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "user_device_list.h"
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class DeviceList;
|
||||||
|
|
||||||
|
class DeviceService : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DeviceService(QObject *parent);
|
||||||
|
|
||||||
|
QSharedPointer<Database> getDatabase(const QString& jid);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addIdentity(const QString& jid, int deviceId, const QByteArray& publicKey);
|
||||||
|
|
||||||
|
void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void onDeviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, QSharedPointer<Database>> databases{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
6
qomemo/key.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "key.h"
|
||||||
|
|
27
qomemo/key.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class KeyPair {
|
||||||
|
public:
|
||||||
|
QByteArray publicKey{};
|
||||||
|
std::optional<QByteArray> secretKey{ std::nullopt };
|
||||||
|
};
|
||||||
|
|
||||||
|
class SignedPreKey {
|
||||||
|
public:
|
||||||
|
int id{ 0 };
|
||||||
|
|
||||||
|
KeyPair key{};
|
||||||
|
QByteArray signature{};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
94
qomemo/qomemo.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "sce.h"
|
||||||
|
#include <shared/qxmppfactories.h>
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDomDocument>
|
||||||
|
|
||||||
|
using namespace QXmpp::Factories;
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::header() const {
|
||||||
|
auto result = createElement("header");
|
||||||
|
result.setAttribute("sid", QString::number(fromDeviceId));
|
||||||
|
|
||||||
|
auto ivNode = createElement("iv");
|
||||||
|
ivNode.setValue(iv);
|
||||||
|
|
||||||
|
for (const auto &key : keys) {
|
||||||
|
result.appendChild(key.toXml());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const {
|
||||||
|
auto result = createElement("encrypted", "eu.siacs.conversations.axolotl");
|
||||||
|
|
||||||
|
result.appendChild(header());
|
||||||
|
// TODO: Payload is optional
|
||||||
|
result.appendChild(payload());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const {
|
||||||
|
QBuffer buffer;
|
||||||
|
buffer.open(QIODevice::ReadWrite);
|
||||||
|
QXmlStreamWriter writer(&buffer);
|
||||||
|
message.toXml(&writer);
|
||||||
|
|
||||||
|
QDomDocument doc;
|
||||||
|
doc.setContent(buffer.data(), true);
|
||||||
|
|
||||||
|
QXmppElement root(doc.documentElement());
|
||||||
|
root.setTagName("payload");
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::EncryptedMessage::content() const {
|
||||||
|
auto envelope = createElement("content", "urn:xmpp:sce:0");
|
||||||
|
|
||||||
|
envelope.appendChild(payload());
|
||||||
|
|
||||||
|
if (!from.isEmpty()) {
|
||||||
|
auto fromNode = createElement("from");
|
||||||
|
fromNode.setAttribute("jid", from);
|
||||||
|
envelope.appendChild(fromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!to.isEmpty()) {
|
||||||
|
auto toNode = createElement("to");
|
||||||
|
toNode.setAttribute("jid", to);
|
||||||
|
envelope.appendChild(toNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timestamp.isNull()) {
|
||||||
|
auto timeNode = createElement("time");
|
||||||
|
timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate));
|
||||||
|
envelope.appendChild(timeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rpad = createElement("rpad");
|
||||||
|
rpad.setValue(QXmpp::Sce::generatePadding());
|
||||||
|
envelope.appendChild(rpad);
|
||||||
|
|
||||||
|
return envelope;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement QXmpp::Omemo::MessageKey::toXml() const {
|
||||||
|
auto result = createElement("key");
|
||||||
|
|
||||||
|
result.setAttribute("rid", QString::number(receivingDeviceId));
|
||||||
|
if (prekey)
|
||||||
|
result.setAttribute("prekey", "true");
|
||||||
|
|
||||||
|
result.setValue(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
42
qomemo/qomemo.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QXmppMessage.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class MessageKey {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] QXmppElement toXml() const;
|
||||||
|
|
||||||
|
int receivingDeviceId{};
|
||||||
|
bool prekey{};
|
||||||
|
QString key{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class EncryptedMessage {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] QXmppElement header() const;
|
||||||
|
[[nodiscard]] QXmppElement content() const;
|
||||||
|
[[nodiscard]] QXmppElement toXml() const;
|
||||||
|
[[nodiscard]] QXmppElement payload() const;
|
||||||
|
|
||||||
|
int fromDeviceId{};
|
||||||
|
|
||||||
|
QList<MessageKey> keys{};
|
||||||
|
QString from{};
|
||||||
|
QString to{};
|
||||||
|
QDateTime timestamp{};
|
||||||
|
|
||||||
|
QString iv{};
|
||||||
|
|
||||||
|
QXmppMessage message{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
292
qomemo/qxmpp_omemo_manager.cpp
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qxmpp_omemo_manager.h"
|
||||||
|
|
||||||
|
#include "qomemo/signal/context.h"
|
||||||
|
|
||||||
|
#include "bundle.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "variant/conversations.h"
|
||||||
|
|
||||||
|
#include <QDomElement>
|
||||||
|
|
||||||
|
#include <QXmppClient.h>
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <QRandomGenerator64>
|
||||||
|
#include <external/signal-protocol-c/src/signal_protocol_internal.h>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
|
||||||
|
Manager::Manager()
|
||||||
|
: deviceService{new DeviceService(this)},
|
||||||
|
omemoVariant(new Variant::Conversations),
|
||||||
|
signalContext(new Signal::Context) {
|
||||||
|
connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived);
|
||||||
|
connect(this, &Manager::deviceListNotFound, deviceService.get(), &DeviceService::onDeviceListNotFound);
|
||||||
|
connect(this, &Manager::deviceListNotFound, this, [this](const QString &jid) {
|
||||||
|
if (jid == client()->configuration().jidBare())
|
||||||
|
generateDeviceListForSelf();
|
||||||
|
});
|
||||||
|
connect(this, &Manager::deviceListReceived, this, [this](const QString &jid, const DeviceList ¤tList) {
|
||||||
|
if (jid == client()->configuration().jidBare())
|
||||||
|
generateDeviceForSelfIfNeeded(currentList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
||||||
|
QString str{};
|
||||||
|
QTextStream info(&str);
|
||||||
|
stanza.save(info, 4);
|
||||||
|
|
||||||
|
std::cout << str.toStdString();
|
||||||
|
|
||||||
|
if (handleDeviceList(stanza) || handleMissingDeviceList(stanza) || handleEncryptedMessage(stanza))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::handleDeviceList(const QDomElement &stanza) {
|
||||||
|
if (!(stanza.tagName() == "iq"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(stanza.attribute("type") == "result"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pubsub = stanza.firstChildElement("pubsub");
|
||||||
|
if (pubsub.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto items = pubsub.firstChildElement("items");
|
||||||
|
if (!(items.attribute("node") == omemoVariant->getDeviceListNode()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto deviceList = omemoVariant->latestDeviceListFromPubSubNode(items);
|
||||||
|
if (!deviceList.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
emit deviceListReceived(stanza.attribute("from"), deviceList.value());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::handleMissingDeviceList(const QDomElement &stanza) {
|
||||||
|
if (stanza.tagName() != "iq")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stanza.attribute("type") != "error")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pubsub = stanza.firstChildElement("pubsub");
|
||||||
|
if (pubsub.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto items = pubsub.firstChildElement("items");
|
||||||
|
if (items.attribute("node") != omemoVariant->getDeviceListNode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto error = stanza.firstChildElement("error");
|
||||||
|
|
||||||
|
if (error.namespaceURI() != "jabber:client" || error.attribute("code") != "404")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qDebug() << "Got 404 deviceList for" << stanza.attribute("from");
|
||||||
|
emit deviceListNotFound(stanza.attribute("from"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::handleEncryptedMessage(const QDomElement &stanza) {
|
||||||
|
if (stanza.tagName() != "message")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto encrypted = stanza.firstChildElement("encrypted");
|
||||||
|
if (encrypted.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qDebug() << "!!!! Got encrypted message!!";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
|
||||||
|
QXmppClientExtension::setClient(client);
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QObject::connect(client, &QXmppClient::connected, this,
|
||||||
|
&Manager::fetchOwnDevices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QXmpp::Omemo::Manager::fetchOwnDevices() {
|
||||||
|
QXmppPubSubIq iq{};
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
iq.setTo(client()->configuration().jidBare());
|
||||||
|
iq.setType(QXmppIq::Get);
|
||||||
|
iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||||
|
|
||||||
|
client()->sendPacket(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<DeviceService> Manager::getDeviceService() {
|
||||||
|
return deviceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::publishDeviceList(const DeviceList &deviceList) {
|
||||||
|
// QXmppPubSubIq iq{};
|
||||||
|
// iq.setFrom(client()->configuration().jid());
|
||||||
|
// iq.setType(QXmppIq::Set);
|
||||||
|
// iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||||
|
// iq.setItems()
|
||||||
|
|
||||||
|
auto iq = omemoVariant->deviceListSetIq(deviceList);
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
|
||||||
|
QString str{};
|
||||||
|
QXmlStreamWriter info(&str);
|
||||||
|
iq.toXml(&info);
|
||||||
|
qInfo() << str;
|
||||||
|
|
||||||
|
client()->sendPacket(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::generateDeviceListForSelf() {
|
||||||
|
qInfo() << "Generate device for self...";
|
||||||
|
|
||||||
|
generateDeviceForSelfIfNeeded(DeviceList());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::publishBundle(int deviceId, const Bundle &bundle) {
|
||||||
|
auto iq = omemoVariant->bundleSetIq(deviceId, bundle);
|
||||||
|
iq.setFrom(client()->configuration().jid());
|
||||||
|
|
||||||
|
QString str{};
|
||||||
|
QXmlStreamWriter info(&str);
|
||||||
|
iq.toXml(&info);
|
||||||
|
qInfo() << str;
|
||||||
|
|
||||||
|
client()->sendPacket(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) {
|
||||||
|
auto db = deviceService->getDatabase(client()->configuration().jidBare());
|
||||||
|
auto activeId = db->loadActiveDeviceId();
|
||||||
|
|
||||||
|
if (activeId.has_value()) {
|
||||||
|
qInfo() << "Current device:" << *activeId;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (const auto &d : currentList.devices)
|
||||||
|
if (d.id == *activeId)
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qInfo() << "Could not find device" << *activeId << ", generating new one";
|
||||||
|
}
|
||||||
|
|
||||||
|
qInfo() << "Generating device";
|
||||||
|
|
||||||
|
auto deviceId = QRandomGenerator64::system()->bounded(INT32_MAX);
|
||||||
|
db->saveActiveDeviceId(deviceId);
|
||||||
|
|
||||||
|
Device device{};
|
||||||
|
device.id = deviceId;
|
||||||
|
|
||||||
|
auto updatedList = currentList;
|
||||||
|
|
||||||
|
|
||||||
|
updatedList.devices.push_back(device);
|
||||||
|
publishDeviceList(updatedList);
|
||||||
|
|
||||||
|
auto bundle = generateAndSaveBundle(deviceId);
|
||||||
|
publishBundle(deviceId, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle Manager::generateAndSaveBundle(int deviceId) {
|
||||||
|
auto database = deviceService->getDatabase(client()->configuration().jidBare());
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
ec_key_pair *ecKeyPair;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecKeyPair);
|
||||||
|
|
||||||
|
signal_buffer *ecPublic;
|
||||||
|
ec_public_key_serialize(&ecPublic, ec_key_pair_get_public(ecKeyPair));
|
||||||
|
|
||||||
|
signal_buffer *ecSecret;
|
||||||
|
ec_private_key_serialize(&ecSecret, ec_key_pair_get_private(ecKeyPair));
|
||||||
|
|
||||||
|
QByteArray identityKey((const char *) signal_buffer_const_data(ecPublic), (int) signal_buffer_len(ecPublic));
|
||||||
|
QByteArray identityKeySecret((const char *) signal_buffer_const_data(ecSecret), (int) signal_buffer_len(ecSecret));
|
||||||
|
|
||||||
|
database->saveIdentityKey(deviceId, identityKey);
|
||||||
|
database->saveIdentityKeySecret(deviceId, identityKeySecret);
|
||||||
|
|
||||||
|
result.ik = identityKey;
|
||||||
|
|
||||||
|
// Generate SPK
|
||||||
|
ec_key_pair *ecSpk;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecSpk);
|
||||||
|
signal_buffer *spkPublic, *spkSecret;
|
||||||
|
ec_public_key_serialize(&spkPublic, ec_key_pair_get_public(ecSpk));
|
||||||
|
ec_private_key_serialize(&spkSecret, ec_key_pair_get_private(ecSpk));
|
||||||
|
|
||||||
|
// Generate SPKs
|
||||||
|
signal_buffer *signature;
|
||||||
|
curve_calculate_signature(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &signature,
|
||||||
|
ec_key_pair_get_private(ecKeyPair), signal_buffer_const_data(spkPublic),
|
||||||
|
signal_buffer_len(spkPublic));
|
||||||
|
|
||||||
|
SignedPreKey spk{};
|
||||||
|
spk.id = 1;
|
||||||
|
spk.key.publicKey = QByteArray((const char *) signal_buffer_const_data(spkPublic), (int) signal_buffer_len(spkPublic));
|
||||||
|
spk.key.secretKey = QByteArray((const char *) signal_buffer_const_data(spkSecret), (int) signal_buffer_len(spkSecret));
|
||||||
|
spk.signature = QByteArray((const char *) signal_buffer_const_data(signature), (int) signal_buffer_len(signature));
|
||||||
|
|
||||||
|
result.spk = spk.key.publicKey;
|
||||||
|
result.spks = spk.signature;
|
||||||
|
result.spkId = 1;
|
||||||
|
|
||||||
|
// Generate 100 PK
|
||||||
|
for (auto i = 1; i <= 100; ++i) {
|
||||||
|
ec_key_pair *currentPreKey;
|
||||||
|
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), ¤tPreKey);
|
||||||
|
|
||||||
|
signal_buffer *pkPublic, *pkSecret;
|
||||||
|
ec_public_key_serialize(&pkPublic, ec_key_pair_get_public(currentPreKey));
|
||||||
|
ec_private_key_serialize(&pkSecret, ec_key_pair_get_private(currentPreKey));
|
||||||
|
|
||||||
|
KeyPair preKey{};
|
||||||
|
preKey.publicKey = QByteArray((const char *) signal_buffer_const_data(pkPublic), (int) signal_buffer_len(pkPublic));
|
||||||
|
preKey.secretKey = QByteArray((const char *) signal_buffer_const_data(pkSecret), (int) signal_buffer_len(pkSecret));
|
||||||
|
|
||||||
|
database->savePreKey(deviceId, i, preKey);
|
||||||
|
|
||||||
|
PreKey pk{};
|
||||||
|
pk.data = preKey.publicKey;
|
||||||
|
pk.id = i;
|
||||||
|
|
||||||
|
result.prekeys.append(pk);
|
||||||
|
|
||||||
|
signal_buffer_free(pkPublic);
|
||||||
|
signal_buffer_free(pkSecret);
|
||||||
|
SIGNAL_UNREF(currentPreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer_free(signature);
|
||||||
|
|
||||||
|
signal_buffer_free(ecPublic);
|
||||||
|
signal_buffer_free(ecSecret);
|
||||||
|
SIGNAL_UNREF(ecKeyPair);
|
||||||
|
|
||||||
|
signal_buffer_free(spkPublic);
|
||||||
|
signal_buffer_free(spkSecret);
|
||||||
|
SIGNAL_UNREF(ecSpk);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
64
qomemo/qxmpp_omemo_manager.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "device_service.h"
|
||||||
|
#include "qomemo.h"
|
||||||
|
#include "variant/omemo_base.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QXmppClientExtension.h>
|
||||||
|
|
||||||
|
namespace Signal {
|
||||||
|
class Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class Manager : public QXmppClientExtension {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Manager();
|
||||||
|
~Manager() override = default;
|
||||||
|
|
||||||
|
bool handleStanza(const QDomElement &stanza) override;
|
||||||
|
|
||||||
|
bool handleDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
bool handleMissingDeviceList(const QDomElement& stanza);
|
||||||
|
|
||||||
|
bool handleEncryptedMessage(const QDomElement& stanza);
|
||||||
|
|
||||||
|
QSharedPointer<DeviceService> getDeviceService();
|
||||||
|
|
||||||
|
Bundle generateAndSaveBundle(int deviceId);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void fetchOwnDevices();
|
||||||
|
|
||||||
|
void publishDeviceList(const QXmpp::Omemo::DeviceList& deviceList);
|
||||||
|
|
||||||
|
void generateDeviceListForSelf();
|
||||||
|
|
||||||
|
void generateDeviceForSelfIfNeeded(const QXmpp::Omemo::DeviceList ¤tList);
|
||||||
|
|
||||||
|
void publishBundle(int deviceId, const QXmpp::Omemo::Bundle& bundle);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||||
|
|
||||||
|
void deviceListNotFound(const QString &jid);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setClient(QXmppClient *client) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<DeviceService> deviceService;
|
||||||
|
QScopedPointer<Variant::Base> omemoVariant;
|
||||||
|
std::shared_ptr<Signal::Context> signalContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
26
qomemo/sce.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sce.h"
|
||||||
|
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
#define RPAD_ALPHABET "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||||
|
|
||||||
|
constexpr int RPAD_MAX_LENGTH = 200;
|
||||||
|
|
||||||
|
QString QXmpp::Sce::generatePadding() {
|
||||||
|
QRandomGenerator random{};
|
||||||
|
QString result{};
|
||||||
|
QString alphabet{QStringLiteral(RPAD_ALPHABET)};
|
||||||
|
|
||||||
|
auto length = random.bounded(RPAD_MAX_LENGTH);
|
||||||
|
result.resize(length);
|
||||||
|
|
||||||
|
for (auto i = 0; i < length; ++i) {
|
||||||
|
result[i] = alphabet[random.bounded(alphabet.length())];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
14
qomemo/sce.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QXmppElement.h>
|
||||||
|
|
||||||
|
namespace QXmpp::Sce {
|
||||||
|
|
||||||
|
QString generatePadding();
|
||||||
|
|
||||||
|
}
|
9
qomemo/signal/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
context.cpp
|
||||||
|
context.h
|
||||||
|
util.cpp
|
||||||
|
util.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(crypto)
|
||||||
|
add_subdirectory(stores)
|
28
qomemo/signal/context.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
|
using namespace Signal;
|
||||||
|
|
||||||
|
Context::Context() : cryptoProvider{ Signal::Crypto::createProvider() } {
|
||||||
|
signal_context_create(&ctx, nullptr);
|
||||||
|
signal_context_set_crypto_provider(ctx, &cryptoProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::~Context() {
|
||||||
|
signal_context_destroy(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() {
|
||||||
|
auto result = std::unique_ptr<Crypto::ECKeyPair>();
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_context *Context::temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped() {
|
||||||
|
return ctx;
|
||||||
|
}
|
32
qomemo/signal/context.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "crypto/ec.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal {
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
Context();
|
||||||
|
~Context();
|
||||||
|
Context(const Context &) = delete;
|
||||||
|
Context(Context &&) = delete;
|
||||||
|
Context &operator=(const Context &) = delete;
|
||||||
|
|
||||||
|
std::unique_ptr<Crypto::ECKeyPair> generateCurveKeyPair();
|
||||||
|
|
||||||
|
signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped();
|
||||||
|
|
||||||
|
private:
|
||||||
|
signal_crypto_provider cryptoProvider{};
|
||||||
|
signal_context *ctx{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal
|
12
qomemo/signal/crypto/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
aes_openssl.cpp
|
||||||
|
aes_openssl.h
|
||||||
|
crypto.cpp
|
||||||
|
crypto.h
|
||||||
|
hmac_sha256_openssl.cpp
|
||||||
|
hmac_sha256_openssl.h
|
||||||
|
sha512_digest_openssl.cpp
|
||||||
|
sha512_digest_openssl.h
|
||||||
|
ec.cpp
|
||||||
|
ec.h
|
||||||
|
)
|
187
qomemo/signal/crypto/aes_openssl.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "aes_openssl.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
class EVPCipherCtxWrapper {
|
||||||
|
public:
|
||||||
|
EVPCipherCtxWrapper() {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
ctx = EVP_CIPHER_CTX_new();
|
||||||
|
#else
|
||||||
|
ctx = new EVP_CIPHER_CTX;
|
||||||
|
EVP_CIPHER_CTX_init(ctx);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~EVPCipherCtxWrapper() {
|
||||||
|
if (good()) {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
#else
|
||||||
|
EVP_CIPHER_CTX_cleanup(ctx);
|
||||||
|
delete ctx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper(const EVPCipherCtxWrapper &) = delete;
|
||||||
|
EVPCipherCtxWrapper(EVPCipherCtxWrapper &&) = delete;
|
||||||
|
EVPCipherCtxWrapper &operator=(const EVPCipherCtxWrapper &) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]] bool good() const { return ctx != nullptr; }
|
||||||
|
|
||||||
|
[[nodiscard]] EVP_CIPHER_CTX *operator*() const { return ctx; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EVP_CIPHER_CTX *ctx{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
static const EVP_CIPHER *aes_cipher(int cipher, size_t key_len) {
|
||||||
|
if (cipher == SG_CIPHER_AES_CBC_PKCS5) {
|
||||||
|
if (key_len == 16) {
|
||||||
|
return EVP_aes_128_cbc();
|
||||||
|
} else if (key_len == 24) {
|
||||||
|
return EVP_aes_192_cbc();
|
||||||
|
} else if (key_len == 32) {
|
||||||
|
return EVP_aes_256_cbc();
|
||||||
|
}
|
||||||
|
} else if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
if (key_len == 16) {
|
||||||
|
return EVP_aes_128_ctr();
|
||||||
|
} else if (key_len == 24) {
|
||||||
|
return EVP_aes_192_ctr();
|
||||||
|
} else if (key_len == 32) {
|
||||||
|
return EVP_aes_256_ctr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Aes::encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *) {
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv_len != 16) {
|
||||||
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plaintext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
fprintf(stderr, "invalid plaintext length: %zu\n", plaintext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper ctx{};
|
||||||
|
if (!ctx.good()) {
|
||||||
|
fprintf(stderr, "could not create context\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_EncryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot set padding\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto out_buf = std::make_unique<uint8_t>(plaintext_len + EVP_CIPHER_block_size(evp_cipher));
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
result = EVP_EncryptUpdate(*ctx, out_buf.get(), &out_len, plaintext,
|
||||||
|
plaintext_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot encrypt plaintext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
result = EVP_EncryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot finish encrypting plaintext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Aes::decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *) {
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv_len != 16) {
|
||||||
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ciphertext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
fprintf(stderr, "invalid ciphertext length: %zu\n", ciphertext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPCipherCtxWrapper ctx{};
|
||||||
|
if (!ctx.good()) {
|
||||||
|
fprintf(stderr, "could not create context\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_DecryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
||||||
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot set padding\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto out_buf = std::make_unique<uint8_t>(ciphertext_len + EVP_CIPHER_block_size(evp_cipher));
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
result = EVP_DecryptUpdate(*ctx, out_buf.get(), &out_len, ciphertext, ciphertext_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot decrypt ciphertext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
result = EVP_DecryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "cannot finish decrypting ciphertext\n");
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
17
qomemo/signal/crypto/aes_openssl.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::Aes {
|
||||||
|
|
||||||
|
int encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data);
|
||||||
|
|
||||||
|
int decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
||||||
|
size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::Aes
|
40
qomemo/signal/crypto/crypto.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "aes_openssl.h"
|
||||||
|
#include "hmac_sha256_openssl.h"
|
||||||
|
#include "sha512_digest_openssl.h"
|
||||||
|
|
||||||
|
int random_func(uint8_t *data, size_t len, void *) {
|
||||||
|
if (RAND_bytes(data, len)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_crypto_provider Signal::Crypto::createProvider() {
|
||||||
|
signal_crypto_provider result{};
|
||||||
|
|
||||||
|
result.random_func = random_func;
|
||||||
|
result.hmac_sha256_init_func = HmacSha256::init;
|
||||||
|
result.hmac_sha256_update_func = HmacSha256::update;
|
||||||
|
result.hmac_sha256_final_func = HmacSha256::final;
|
||||||
|
result.hmac_sha256_cleanup_func = HmacSha256::cleanup;
|
||||||
|
result.sha512_digest_init_func = Sha512::init;
|
||||||
|
result.sha512_digest_update_func = Sha512::update;
|
||||||
|
result.sha512_digest_final_func = Sha512::final;
|
||||||
|
result.sha512_digest_cleanup_func = Sha512::cleanup;
|
||||||
|
result.encrypt_func = Aes::encrypt;
|
||||||
|
result.decrypt_func = Aes::decrypt;
|
||||||
|
result.user_data = nullptr;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
13
qomemo/signal/crypto/crypto.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto {
|
||||||
|
|
||||||
|
signal_crypto_provider createProvider();
|
||||||
|
|
||||||
|
}
|
5
qomemo/signal/crypto/ec.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-06-17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ec.h"
|
20
qomemo/signal/crypto/ec.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-06-17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto {
|
||||||
|
|
||||||
|
class ECKeyPair {
|
||||||
|
public:
|
||||||
|
ECKeyPair();
|
||||||
|
~ECKeyPair();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ec_key_pair *ec;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
71
qomemo/signal/crypto/hmac_sha256_openssl.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hmac_sha256_openssl.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
int HmacSha256::init(void **hmac_context, const uint8_t *key, size_t key_len, void *) {
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
HMAC_CTX *ctx = HMAC_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
auto ctx = new HMAC_CTX;
|
||||||
|
HMAC_CTX_init(ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*hmac_context = ctx;
|
||||||
|
|
||||||
|
if (HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), nullptr) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HmacSha256::update(void *hmac_context, const uint8_t *data, size_t data_len, void *) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
int result = HMAC_Update(ctx, data, data_len);
|
||||||
|
|
||||||
|
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HmacSha256::final(void *hmac_context, signal_buffer **output, void *) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
if (HMAC_Final(ctx, md, &len) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer *output_buffer = signal_buffer_create(md, len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = output_buffer;
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmacSha256::cleanup(void *hmac_context, void *) {
|
||||||
|
if (hmac_context) {
|
||||||
|
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
HMAC_CTX_free(ctx);
|
||||||
|
#else
|
||||||
|
HMAC_CTX_cleanup(ctx);
|
||||||
|
delete ctx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
16
qomemo/signal/crypto/hmac_sha256_openssl.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::HmacSha256 {
|
||||||
|
|
||||||
|
int init(void **hmac_context, const uint8_t *key, size_t key_len, void *);
|
||||||
|
int update(void *hmac_context, const uint8_t *data, size_t data_len, void *);
|
||||||
|
int final(void *hmac_context, signal_buffer **output, void *);
|
||||||
|
void cleanup(void *hmac_context, void *);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::HmacSha256
|
67
qomemo/signal/crypto/sha512_digest_openssl.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha512_digest_openssl.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Signal::Crypto;
|
||||||
|
|
||||||
|
int Sha512::init(void **digest_context, void *) {
|
||||||
|
auto ctx = EVP_MD_CTX_create();
|
||||||
|
if (!ctx) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
|
||||||
|
|
||||||
|
if (result == 1) {
|
||||||
|
*digest_context = ctx;
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_MD_CTX_destroy(ctx);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sha512::update(void *digest_context, const uint8_t *data, size_t data_len, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
auto result = EVP_DigestUpdate(ctx, data, data_len);
|
||||||
|
|
||||||
|
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sha512::final(void *digest_context, signal_buffer **output, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
auto result = EVP_DigestFinal_ex(ctx, md, &len);
|
||||||
|
if (result != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
|
||||||
|
if (result != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_buffer *output_buffer = signal_buffer_create(md, len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = output_buffer;
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sha512::cleanup(void *digest_context, void *) {
|
||||||
|
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
|
||||||
|
EVP_MD_CTX_destroy(ctx);
|
||||||
|
}
|
16
qomemo/signal/crypto/sha512_digest_openssl.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Crypto::Sha512 {
|
||||||
|
|
||||||
|
int init(void **digest_context, void *);
|
||||||
|
int update(void *digest_context, const uint8_t *data, size_t data_len, void *);
|
||||||
|
int final(void *digest_context, signal_buffer **output, void *);
|
||||||
|
void cleanup(void *digest_context, void *);
|
||||||
|
|
||||||
|
} // namespace Signal::Crypto::Sha512
|
14
qomemo/signal/stores/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
identity_key_store.cpp
|
||||||
|
identity_key_store.h
|
||||||
|
pre_key_store.cpp
|
||||||
|
pre_key_store.h
|
||||||
|
sender_key_store.cpp
|
||||||
|
sender_key_store.h
|
||||||
|
session_store.cpp
|
||||||
|
session_store.h
|
||||||
|
signed_pre_key_store.cpp
|
||||||
|
signed_pre_key_store.h
|
||||||
|
store_context.cpp
|
||||||
|
store_context.h
|
||||||
|
)
|
70
qomemo/signal/stores/identity_key_store.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "identity_key_store.h"
|
||||||
|
|
||||||
|
#include "qomemo/signal/util.h"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
Signal::Store::IdentityKeyStore::IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId)
|
||||||
|
: jid(std::move(jid)), deviceId(deviceId), deviceService(deviceService) {
|
||||||
|
database = deviceService.getDatabase(jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) {
|
||||||
|
auto pk = database->loadIdentityKey(deviceId);
|
||||||
|
auto sk = database->loadIdentityKeySecret(deviceId);
|
||||||
|
|
||||||
|
*public_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(pk->data()), pk->size());
|
||||||
|
*private_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(sk->data()), sk->size());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) {
|
||||||
|
// TODO: Figure out what registration id is used for
|
||||||
|
*registration_id = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||||
|
size_t key_len) {
|
||||||
|
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||||
|
auto identityKey = Signal::Util::byteArray(key_data, key_len);
|
||||||
|
|
||||||
|
deviceService.getDatabase(identityJid)->saveIdentityKey(address->device_id, identityKey);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||||
|
size_t key_len) {
|
||||||
|
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||||
|
auto actualIdentityKey = deviceService.getDatabase(identityJid)->loadIdentityKey(address->device_id);
|
||||||
|
|
||||||
|
if (!actualIdentityKey.has_value()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto givenIdentityKey = Signal::Util::byteArray(key_data, key_len);
|
||||||
|
|
||||||
|
return givenIdentityKey == actualIdentityKey ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::IdentityKeyStore::fillCallbacks(signal_protocol_identity_key_store &store) {
|
||||||
|
store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data);
|
||||||
|
};
|
||||||
|
store.get_local_registration_id = [](void *ptr, uint32_t *registrationId) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->getLocalRegistrationId(registrationId);
|
||||||
|
};
|
||||||
|
store.is_trusted_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len,
|
||||||
|
void *ptr) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->isTrustedIdentity(address, key_data, key_len);
|
||||||
|
};
|
||||||
|
store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) {
|
||||||
|
return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len);
|
||||||
|
};
|
||||||
|
}
|
32
qomemo/signal/stores/identity_key_store.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class IdentityKeyStore {
|
||||||
|
public:
|
||||||
|
IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId);
|
||||||
|
|
||||||
|
int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data);
|
||||||
|
int getLocalRegistrationId(uint32_t *registration_id);
|
||||||
|
int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||||
|
int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_identity_key_store &store);
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
const int deviceId;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QXmpp::Omemo::DeviceService &deviceService;
|
||||||
|
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
38
qomemo/signal/stores/pre_key_store.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pre_key_store.h"
|
||||||
|
|
||||||
|
Signal::Store::PreKeyStore::PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database) : database(
|
||||||
|
std::move(database)) {}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::loadPreKey(signal_buffer **record, uint32_t pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; }
|
||||||
|
|
||||||
|
void Signal::Store::PreKeyStore::fillCallbacks(signal_protocol_pre_key_store &store) {
|
||||||
|
store.contains_pre_key = [](uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->containsPreKey(id);
|
||||||
|
};
|
||||||
|
store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id);
|
||||||
|
};
|
||||||
|
store.remove_pre_key = [](uint32_t id, void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->removePreKey(id);
|
||||||
|
};
|
||||||
|
store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size,
|
||||||
|
void *ptr) {
|
||||||
|
return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size);
|
||||||
|
};
|
||||||
|
}
|
28
qomemo/signal/stores/pre_key_store.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "qomemo/device_service.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class PreKeyStore {
|
||||||
|
public:
|
||||||
|
explicit PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database);
|
||||||
|
|
||||||
|
int containsPreKey(uint32_t pre_key_id);
|
||||||
|
int loadPreKey(signal_buffer **record, uint32_t pre_key_id);
|
||||||
|
int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len);
|
||||||
|
int removePreKey(uint32_t pre_key_id);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_pre_key_store &store);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
30
qomemo/signal/stores/sender_key_store.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sender_key_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SenderKeyStore::loadSenderKey(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SenderKeyStore::storeSenderKey(const signal_protocol_sender_key_name *sender_key_name,
|
||||||
|
uint8_t *record, size_t record_len, uint8_t *user_record,
|
||||||
|
size_t user_record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SenderKeyStore::fillCallbacks(signal_protocol_sender_key_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_sender_key = [](signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name, void *ptr) {
|
||||||
|
return static_cast<SenderKeyStore *>(ptr)->loadSenderKey(record, user_record, sender_key_name);
|
||||||
|
};
|
||||||
|
store.store_sender_key = [](const signal_protocol_sender_key_name *sender_key_name, uint8_t *record,
|
||||||
|
size_t record_len, uint8_t *user_record, size_t user_record_len, void *ptr) {
|
||||||
|
return static_cast<SenderKeyStore *>(ptr)->storeSenderKey(sender_key_name, record, record_len, user_record,
|
||||||
|
user_record_len);
|
||||||
|
};
|
||||||
|
}
|
21
qomemo/signal/stores/sender_key_store.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SenderKeyStore {
|
||||||
|
public:
|
||||||
|
int storeSenderKey(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len);
|
||||||
|
int loadSenderKey(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_sender_key_name *sender_key_name);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_sender_key_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
57
qomemo/signal/stores/session_store.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "session_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::loadSession(signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::storeSession(const signal_protocol_address *address, uint8_t *record,
|
||||||
|
size_t record_len, uint8_t *user_record, size_t user_record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::containsSession(const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::deleteSession(const signal_protocol_address *address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SessionStore::deleteAllSessions(const char *name, size_t name_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SessionStore::fillCallbacks(signal_protocol_session_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_session_func = [](signal_buffer **record, signal_buffer **user_record,
|
||||||
|
const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->loadSession(record, user_record, address);
|
||||||
|
};
|
||||||
|
store.get_sub_device_sessions_func = [](signal_int_list **sessions, const char *name, size_t name_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->getSubDeviceSessions(sessions, name, name_len);
|
||||||
|
};
|
||||||
|
store.store_session_func = [](const signal_protocol_address *address, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->storeSession(address, record, record_len, user_record,
|
||||||
|
user_record_len);
|
||||||
|
};
|
||||||
|
store.contains_session_func = [](const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->containsSession(address);
|
||||||
|
};
|
||||||
|
store.delete_session_func = [](const signal_protocol_address *address, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->deleteSession(address);
|
||||||
|
};
|
||||||
|
store.delete_all_sessions_func = [](const char *name, size_t name_len, void *ptr) {
|
||||||
|
return static_cast<SessionStore *>(ptr)->deleteAllSessions(name, name_len);
|
||||||
|
};
|
||||||
|
}
|
24
qomemo/signal/stores/session_store.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SessionStore {
|
||||||
|
public:
|
||||||
|
int loadSession(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address);
|
||||||
|
int getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len);
|
||||||
|
int storeSession(const signal_protocol_address *address, uint8_t *record, size_t record_len,
|
||||||
|
uint8_t *user_record, size_t user_record_len);
|
||||||
|
int containsSession(const signal_protocol_address *address);
|
||||||
|
int deleteSession(const signal_protocol_address *address);
|
||||||
|
int deleteAllSessions(const char *name, size_t name_len);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_session_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
43
qomemo/signal/stores/signed_pre_key_store.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "signed_pre_key_store.h"
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::containsSignedPreKey(uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Signal::Store::SignedPreKeyStore::removeSignedPreKey(uint32_t signed_pre_key_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::Store::SignedPreKeyStore::fillCallbacks(signal_protocol_signed_pre_key_store &store) {
|
||||||
|
store.user_data = nullptr;
|
||||||
|
store.destroy_func = nullptr;
|
||||||
|
store.load_signed_pre_key = [](signal_buffer **record, uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->loadSignedPreKey(
|
||||||
|
record, signed_pre_key_id);
|
||||||
|
};
|
||||||
|
store.store_signed_pre_key = [](uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->storeSignedPreKey(
|
||||||
|
signed_pre_key_id, record, record_len);
|
||||||
|
};
|
||||||
|
store.contains_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->containsSignedPreKey(
|
||||||
|
signed_pre_key_id);
|
||||||
|
};
|
||||||
|
store.remove_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) {
|
||||||
|
return static_cast<SignedPreKeyStore *>(ptr)->removeSignedPreKey(
|
||||||
|
signed_pre_key_id);
|
||||||
|
};
|
||||||
|
}
|
21
qomemo/signal/stores/signed_pre_key_store.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class SignedPreKeyStore {
|
||||||
|
public:
|
||||||
|
int loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id);
|
||||||
|
int storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len);
|
||||||
|
int containsSignedPreKey(uint32_t signed_pre_key_id);
|
||||||
|
int removeSignedPreKey(uint32_t signed_pre_key_id);
|
||||||
|
|
||||||
|
void fillCallbacks(signal_protocol_signed_pre_key_store &store);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
25
qomemo/signal/stores/store_context.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "store_context.h"
|
||||||
|
|
||||||
|
Signal::Store::Context::Context(signal_context *global) : identityKeyStore(), preKeyStore(), senderKeyStore(), sessionStore(), signedPreKeyStore() {
|
||||||
|
signal_protocol_store_context_create(&ctx, global);
|
||||||
|
|
||||||
|
identityKeyStore->fillCallbacks(iks);
|
||||||
|
preKeyStore->fillCallbacks(pks);
|
||||||
|
senderKeyStore->fillCallbacks(sks);
|
||||||
|
sessionStore->fillCallbacks(ss);
|
||||||
|
signedPreKeyStore->fillCallbacks(spks);
|
||||||
|
|
||||||
|
signal_protocol_store_context_set_identity_key_store(ctx, &iks);
|
||||||
|
signal_protocol_store_context_set_pre_key_store(ctx, &pks);
|
||||||
|
signal_protocol_store_context_set_sender_key_store(ctx, &sks);
|
||||||
|
signal_protocol_store_context_set_session_store(ctx, &ss);
|
||||||
|
signal_protocol_store_context_set_signed_pre_key_store(ctx, &spks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Signal::Store::Context::~Context() {
|
||||||
|
signal_protocol_store_context_destroy(ctx);
|
||||||
|
}
|
42
qomemo/signal/stores/store_context.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include "identity_key_store.h"
|
||||||
|
#include "pre_key_store.h"
|
||||||
|
#include "sender_key_store.h"
|
||||||
|
#include "session_store.h"
|
||||||
|
#include "signed_pre_key_store.h"
|
||||||
|
|
||||||
|
namespace Signal::Store {
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
explicit Context(signal_context *global);
|
||||||
|
~Context();
|
||||||
|
|
||||||
|
Context(const Context &) = delete;
|
||||||
|
Context(Context &&) = delete;
|
||||||
|
Context &operator=(const Context &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
signal_protocol_store_context *ctx{nullptr};
|
||||||
|
|
||||||
|
signal_protocol_identity_key_store iks{};
|
||||||
|
signal_protocol_pre_key_store pks{};
|
||||||
|
signal_protocol_sender_key_store sks{};
|
||||||
|
signal_protocol_session_store ss{};
|
||||||
|
signal_protocol_signed_pre_key_store spks{};
|
||||||
|
|
||||||
|
std::unique_ptr<IdentityKeyStore> identityKeyStore;
|
||||||
|
std::unique_ptr<PreKeyStore> preKeyStore;
|
||||||
|
std::unique_ptr<SenderKeyStore> senderKeyStore;
|
||||||
|
std::unique_ptr<SessionStore> sessionStore;
|
||||||
|
std::unique_ptr<SignedPreKeyStore> signedPreKeyStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Signal::Store
|
14
qomemo/signal/util.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
QString Signal::Util::jidFromAddress(const signal_protocol_address *address) {
|
||||||
|
// TODO: Validate this
|
||||||
|
return QString::fromRawData(reinterpret_cast<const QChar *>(address->name), static_cast<int>(address->name_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Signal::Util::byteArray(const uint8_t *data, size_t len) {
|
||||||
|
return QByteArray::fromRawData(reinterpret_cast<const char *>(data), static_cast<int>(len));
|
||||||
|
}
|
18
qomemo/signal/util.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal/signal_protocol.h>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
namespace Signal::Util {
|
||||||
|
|
||||||
|
QString jidFromAddress(const signal_protocol_address *address);
|
||||||
|
|
||||||
|
QByteArray byteArray(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
}
|
9
qomemo/user_device_list.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "user_device_list.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) : jid(std::move(jid)) {}
|
18
qomemo/user_device_list.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class UserDeviceList {
|
||||||
|
public:
|
||||||
|
explicit UserDeviceList(QString jid);
|
||||||
|
|
||||||
|
const QString jid;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
1
qomemo/variant/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
target_sources(squawk PRIVATE xep0384.cpp xep0384.h conversations.cpp conversations.h omemo_base.cpp omemo_base.h)
|
205
qomemo/variant/conversations.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "conversations.h"
|
||||||
|
|
||||||
|
#include "qomemo/bundle.h"
|
||||||
|
#include "qomemo/device.h"
|
||||||
|
#include "shared/qxmppfactories.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QXmppIq.h>
|
||||||
|
#include <QXmppPubSubIq.h>
|
||||||
|
|
||||||
|
using namespace QXmpp::Omemo;
|
||||||
|
using namespace QXmpp::Factories;
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::deviceToXml(const Device &device) {
|
||||||
|
auto result = createElement("device");
|
||||||
|
result.setAttribute("id", QString::number(device.id));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) {
|
||||||
|
Device result{};
|
||||||
|
|
||||||
|
if (!elementMatches(xml, "device"))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.id = xml.attribute("id").toInt();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement
|
||||||
|
Variant::Conversations::deviceListToXml(const DeviceList &deviceList) {
|
||||||
|
auto element = createElement("list", "eu.siacs.conversations.axolotl");
|
||||||
|
|
||||||
|
for (const auto &device : deviceList.devices) {
|
||||||
|
element.appendChild(deviceToXml(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DeviceList> Variant::Conversations::latestDeviceListFromPubSubNode(const QXmppElement &items) {
|
||||||
|
auto item = items.firstChildElement("item");
|
||||||
|
if (item.isNull())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto list = item.firstChildElement("list");
|
||||||
|
if (list.isNull())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (!elementMatches(list, "list", "eu.siacs.conversations.axolotl"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
DeviceList result{};
|
||||||
|
|
||||||
|
auto deviceElement = list.firstChildElement("device");
|
||||||
|
while (!deviceElement.isNull()) {
|
||||||
|
result.devices.push_back(deviceFromXml(deviceElement));
|
||||||
|
deviceElement = deviceElement.nextSiblingElement("device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
||||||
|
QXmppIq iq{};
|
||||||
|
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
|
||||||
|
auto item = createElement("item");
|
||||||
|
item.appendChild(deviceListToXml(deviceList));
|
||||||
|
|
||||||
|
auto publish = createElement("publish");
|
||||||
|
publish.setAttribute("node", getDeviceListNode());
|
||||||
|
publish.appendChild(item);
|
||||||
|
|
||||||
|
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||||
|
pubSub.appendChild(publish);
|
||||||
|
pubSub.appendChild(createOpenPublishOptions());
|
||||||
|
|
||||||
|
iq.setExtensions({ pubSub });
|
||||||
|
|
||||||
|
return iq;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Variant::Conversations::getDeviceListNode() const {
|
||||||
|
return "eu.siacs.conversations.axolotl.devicelist";
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) {
|
||||||
|
QXmppIq iq{};
|
||||||
|
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
|
||||||
|
auto item = createElement("item");
|
||||||
|
item.appendChild(bundleToXml(bundle));
|
||||||
|
|
||||||
|
auto publish = createElement("publish");
|
||||||
|
publish.setAttribute(
|
||||||
|
"node",
|
||||||
|
QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId));
|
||||||
|
publish.appendChild(item);
|
||||||
|
|
||||||
|
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||||
|
pubSub.appendChild(publish);
|
||||||
|
pubSub.appendChild(createOpenPublishOptions());
|
||||||
|
|
||||||
|
iq.setExtensions({ pubSub });
|
||||||
|
|
||||||
|
return iq;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::bundleToXml(const Bundle &bundle) {
|
||||||
|
auto spkNode = createElement("signedPreKeyPublic");
|
||||||
|
spkNode.setAttribute("signedPreKeyId", QString::number(bundle.spkId));
|
||||||
|
spkNode.setValue(bundle.spk.toBase64());
|
||||||
|
|
||||||
|
auto spksNode = createElement("signedPreKeySignature");
|
||||||
|
spksNode.setValue(bundle.spks.toBase64());
|
||||||
|
|
||||||
|
auto ikNode = createElement("identityKey");
|
||||||
|
ikNode.setValue(bundle.ik.toBase64());
|
||||||
|
|
||||||
|
auto prekeysNode = createElement("prekeys");
|
||||||
|
for (const auto &pk : bundle.prekeys) {
|
||||||
|
prekeysNode.appendChild(preKeyToXml(pk));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
|
||||||
|
result.appendChild(spkNode);
|
||||||
|
result.appendChild(spksNode);
|
||||||
|
result.appendChild(ikNode);
|
||||||
|
result.appendChild(prekeysNode);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppElement Variant::Conversations::preKeyToXml(const PreKey &pk) {
|
||||||
|
auto x = createElement("preKeyPublic");
|
||||||
|
x.setAttribute("preKeyId", QString::number(pk.id));
|
||||||
|
x.setValue(pk.data.toBase64());
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PreKey> Variant::Conversations::preKeyFromXml(const QXmppElement &xml) {
|
||||||
|
if (!elementMatches(xml, "preKeyPublic"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto pk = PreKey();
|
||||||
|
pk.id = xml.attribute("preKeyId").toInt();
|
||||||
|
pk.data = QByteArray::fromBase64Encoding(xml.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Bundle> Variant::Conversations::bundleFromXml(const QXmppElement &xml) {
|
||||||
|
if (!elementMatches(xml, "bundle", "eu.siacs.conversations.axolotl"))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto spkNode = xml.firstChildElement("signedPreKeyPublic");
|
||||||
|
if (spkNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'signedPreKeyPublic'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle result{};
|
||||||
|
|
||||||
|
result.spk = QByteArray::fromBase64Encoding(spkNode.value().toUtf8()).decoded;
|
||||||
|
result.spkId = spkNode.attribute("signedPreKeyId").toInt();
|
||||||
|
|
||||||
|
auto spksNode = xml.firstChildElement("signedPreKeySignature");
|
||||||
|
if (spksNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'signedPreKeySignature'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.spks = QByteArray::fromBase64Encoding(spksNode.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
auto ikNode = xml.firstChildElement("identityKey");
|
||||||
|
if (ikNode.isNull()) {
|
||||||
|
qWarning() << "'bundle': missing 'identityKey'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.ik = QByteArray::fromBase64Encoding(ikNode.value().toUtf8()).decoded;
|
||||||
|
|
||||||
|
auto prekeysNode = xml.firstChildElement("prekeys");
|
||||||
|
auto pkNode = prekeysNode.firstChildElement("preKeyPublic");
|
||||||
|
|
||||||
|
result.prekeys.clear();
|
||||||
|
while (!pkNode.isNull()) {
|
||||||
|
auto maybePk = preKeyFromXml(pkNode);
|
||||||
|
if (maybePk)
|
||||||
|
result.prekeys.push_back(*maybePk);
|
||||||
|
pkNode = pkNode.nextSiblingElement("preKeyPublic");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
32
qomemo/variant/conversations.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "omemo_base.h"
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo::Variant {
|
||||||
|
|
||||||
|
class Conversations : public Base {
|
||||||
|
public:
|
||||||
|
~Conversations() override = default;
|
||||||
|
|
||||||
|
QXmppElement deviceToXml(const Device &device) override;
|
||||||
|
Device deviceFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
[[nodiscard]] QString getDeviceListNode() const override;
|
||||||
|
QXmppElement deviceListToXml(const DeviceList &deviceList) override;
|
||||||
|
std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &items) override;
|
||||||
|
QXmppIq deviceListSetIq(const DeviceList &deviceList) override;
|
||||||
|
|
||||||
|
QXmppElement preKeyToXml(const PreKey &pk) override;
|
||||||
|
std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
QXmppElement bundleToXml(const Bundle &bundle) override;
|
||||||
|
std::optional<Bundle> bundleFromXml(const QXmppElement &xml) override;
|
||||||
|
|
||||||
|
QXmppIq bundleSetIq(int deviceId, const Bundle &bundle) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo::Variant
|
5
qomemo/variant/omemo_base.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "omemo_base.h"
|
45
qomemo/variant/omemo_base.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QXmppElement;
|
||||||
|
class QXmppIq;
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {
|
||||||
|
|
||||||
|
class PreKey;
|
||||||
|
class Bundle;
|
||||||
|
class Device;
|
||||||
|
class DeviceList;
|
||||||
|
|
||||||
|
namespace Variant {
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
virtual ~Base() = default;
|
||||||
|
|
||||||
|
virtual QXmppElement deviceToXml(const Device &device) = 0;
|
||||||
|
virtual Device deviceFromXml(const QXmppElement &xml) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual QString getDeviceListNode() const = 0;
|
||||||
|
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0;
|
||||||
|
virtual std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &xml) = 0;
|
||||||
|
virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0;
|
||||||
|
|
||||||
|
virtual QXmppElement preKeyToXml(const PreKey &pk) = 0;
|
||||||
|
virtual std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) = 0;
|
||||||
|
|
||||||
|
virtual QXmppElement bundleToXml(const Bundle& bundle) = 0;
|
||||||
|
virtual std::optional<Bundle> bundleFromXml(const QXmppElement& xml) = 0;
|
||||||
|
|
||||||
|
virtual QXmppIq bundleSetIq(int deviceId, const Bundle& bundle) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Variant
|
||||||
|
|
||||||
|
} // namespace QXmpp::Omemo
|
5
qomemo/variant/xep0384.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xep0384.h"
|
7
qomemo/variant/xep0384.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
* Created by victoria on 2021-05-13.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace QXmpp::Omemo {}
|
14
resources/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
target_sources(squawk PRIVATE resources.qrc)
|
||||||
|
|
||||||
|
configure_file(images/logo.svg squawk.svg COPYONLY)
|
||||||
|
|
||||||
|
execute_process(COMMAND convert -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
11
resources/images/fallback/dark/big/document-preview.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 807 B |
21
resources/images/fallback/dark/big/folder.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
||||||
|
<defs id="defs5455">
|
||||||
|
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
||||||
|
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
||||||
|
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
||||||
|
</defs>
|
||||||
|
<metadata id="metadata5458"/>
|
||||||
|
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
||||||
|
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
||||||
|
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
12
resources/images/fallback/dark/small/document-preview.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1010 B |
13
resources/images/fallback/dark/small/folder.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<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 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 609 B |
11
resources/images/fallback/light/big/document-preview.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 807 B |
21
resources/images/fallback/light/big/folder.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
||||||
|
<defs id="defs5455">
|
||||||
|
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
||||||
|
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
||||||
|
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
||||||
|
</defs>
|
||||||
|
<metadata id="metadata5458"/>
|
||||||
|
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
||||||
|
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
||||||
|
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
12
resources/images/fallback/light/small/document-preview.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1010 B |
13
resources/images/fallback/light/small/folder.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<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 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 609 B |