Compare commits
144 Commits
feat/adapt
...
master
Author | SHA1 | Date | |
---|---|---|---|
a04693e39d | |||
3cce057545 | |||
9a44ae1fa5 | |||
85ff6c25ba | |||
3cc7db8eff | |||
|
ff9a591d6d | ||
8e3f10caff | |||
8bfe88929f | |||
9927bdc38b | |||
|
8083859541 | ||
|
030c374139 | ||
|
2c61b82924 | ||
fb843a1346 | |||
8d82d340a4 | |||
acd60eaba2 | |||
829777935f | |||
0be2648849 | |||
93c5be412e | |||
8f5325b291 | |||
75554c7451 | |||
00af582287 | |||
19835af3cf | |||
e31ef78e71 | |||
be466fbad1 | |||
0a530bfa93 | |||
637eb702a8 | |||
a7d1a28f29 | |||
297e08ba41 | |||
9d688e8596 | |||
23ec80ccba | |||
5fbb03fc46 | |||
81cf0f8d34 | |||
69d797fe51 | |||
4f295fee3c | |||
fffef9876a | |||
283e9ebc4d | |||
21b40a9ccb | |||
76a9c5da0c | |||
8ec0af3205 | |||
4b68da458f | |||
927bdf0dab | |||
5ba97ecc25 | |||
9fff409630 | |||
99fd001292 | |||
2d8f32c257 | |||
77dd28b600 | |||
6f32e99593 | |||
ec362cef55 | |||
e4a2728ef8 | |||
bf11d8a74e | |||
edf1ee60cd | |||
4af16b75bf | |||
bb304ce774 | |||
3c6b611a41 | |||
73d83f55af | |||
b72a837754 | |||
d4bf7e599a | |||
15fb4bbd62 | |||
2aed8a1209 | |||
78ef3664f7 | |||
5aa0f4bca9 | |||
b45a73b723 | |||
758a9d95f3 | |||
dfe72ca36c | |||
db3bc358a7 | |||
0b61b6e928 | |||
820dc845ea | |||
87973b3b67 | |||
b6ba022bff | |||
7b2b7ee5d5 | |||
c50cd1140e | |||
037dabbe06 | |||
2ae75a4b91 | |||
d162494ec8 | |||
7e9eed2075 | |||
7192286aeb | |||
645b92ba51 | |||
80c5e2f2b4 | |||
1f065f23e6 | |||
3c48577eee | |||
0340db7f2f | |||
c3a45ec58c | |||
7ba94e9deb | |||
eac87e713f | |||
d86e2c28a0 | |||
2fcc432aef | |||
e58213b294 | |||
3916aec358 | |||
721d3a1a89 | |||
83cb220175 | |||
18859cb960 | |||
4c20a314f0 | |||
51ac1ac709 | |||
8f949277f6 | |||
ce686e121b | |||
f64e5c2df0 | |||
2c26c7e264 | |||
69e0c88d8d | |||
82d54ba4df | |||
1b66fda318 | |||
9f746d203b | |||
27377e0ec5 | |||
4baa3bccbf | |||
4786388822 | |||
62f02c18d7 | |||
1fcd403dba | |||
5f6691067a | |||
788c6ca556 | |||
bf4a27f35d | |||
0823b35148 | |||
73b1b58a96 | |||
d8b5ccb2da | |||
243edff8bd | |||
da19eb86bb | |||
0ff9f12157 | |||
802e2f11a1 | |||
c708c33a92 | |||
a8a7ce2538 | |||
841e526e59 | |||
6bee149e6b | |||
62a59eb7a1 | |||
296328f12d | |||
4d3ba6b11f | |||
8a2658e4fc | |||
9ac0ca10f3 | |||
7130e674c4 | |||
e27ae1a82f | |||
1aa2b5a539 | |||
43bfaf9b7e | |||
b19dafef33 | |||
aeaa6b1b28 | |||
e47ba603e0 | |||
8fece95aa2 | |||
893ff53aa8 | |||
39f2f3d975 | |||
52551c1ce0 | |||
332131796c | |||
3a70df21f8 | |||
a24e8382d1 | |||
d20fd84d39 | |||
5862f1552b | |||
ebeb4089eb | |||
a53126d8bc | |||
7db269acb5 |
45
.gitea/workflows/release.yml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
name: Squawk Release workflow
|
||||||
|
run-name: ${{ gitea.actor }} is running Squawk Release workflow on release ${{ gitea.event.release.tag_name }}
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Archlinux:
|
||||||
|
runs-on: archlinux
|
||||||
|
steps:
|
||||||
|
- name: Download the release tarball
|
||||||
|
run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz
|
||||||
|
|
||||||
|
- name: Calculate SHA256 for the tarball
|
||||||
|
run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Unarchive tarball
|
||||||
|
run: tar -xvzf tarball.tar.gz
|
||||||
|
|
||||||
|
- name: Clone the AUR repository
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key
|
||||||
|
chmod 600 key
|
||||||
|
GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/squawk.git aur
|
||||||
|
chmod 777 -R aur
|
||||||
|
cd aur
|
||||||
|
git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }}
|
||||||
|
git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: Copy PKGBUILD to the directory
|
||||||
|
run: cp squawk/packaging/Archlinux/PKGBUILD aur/
|
||||||
|
|
||||||
|
- name: Put SHA256 sum to PKGBUILD file, and generate .SRCINFO
|
||||||
|
working-directory: aur
|
||||||
|
run: |
|
||||||
|
sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD
|
||||||
|
sudo -u build makepkg --printsrcinfo > .SRCINFO
|
||||||
|
|
||||||
|
- name: Commit package to aur
|
||||||
|
working-directory: aur
|
||||||
|
run: |
|
||||||
|
git add PKGBUILD .SRCINFO
|
||||||
|
git commit -m "${{ gitea.event.release.body//\"/\\\" }}"
|
||||||
|
GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push
|
6
.gitmodules
vendored
@ -1,3 +1,9 @@
|
|||||||
[submodule "external/qxmpp"]
|
[submodule "external/qxmpp"]
|
||||||
path = external/qxmpp
|
path = external/qxmpp
|
||||||
url = https://github.com/qxmpp-project/qxmpp.git
|
url = https://github.com/qxmpp-project/qxmpp.git
|
||||||
|
[submodule "external/storage"]
|
||||||
|
path = external/storage
|
||||||
|
url = https://git.macaw.me/blue/storage
|
||||||
|
[submodule "external/lmdbal"]
|
||||||
|
path = external/lmdbal
|
||||||
|
url = gitea@git.macaw.me:blue/lmdbal.git
|
||||||
|
3640
.uncrustify.cfg
Normal file
74
CHANGELOG.md
@ -1,19 +1,89 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Squawk 0.2.0 (Unreleased)
|
## Squawk 0.2.3 (February 04, 2024)
|
||||||
|
### Bug fixes
|
||||||
|
- "Add contact" and "Join conference" menu are enabled once again (pavavno)!
|
||||||
|
- availability is now read from the same section of config file it was stored
|
||||||
|
- automatic avatars (if a contact doesn't have one) get generated once again
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues
|
||||||
|
- all of the expandable roster items now get saved between launches
|
||||||
|
- settings file on the disk is not rewritten every roster element expansion or collapse
|
||||||
|
- removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar)
|
||||||
|
- vcard window now is Info system and it can display more information
|
||||||
|
- reduced vcard request spam in MUCs
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- now you can enable tray icon from settings!
|
||||||
|
- there is a job queue now, this allowes to spread a bit the spam on the server at connection time
|
||||||
|
- squawk now queries clients of it's peers, you can see what programs other people use
|
||||||
|
|
||||||
|
## Squawk 0.2.2 (May 05, 2022)
|
||||||
|
### Bug fixes
|
||||||
|
- now when you remove an account it actually gets removed
|
||||||
|
- segfault on uninitialized Availability in some rare occasions
|
||||||
|
- fixed crash when you open a dialog with someone that has only error messages in archive
|
||||||
|
- message height is now calculated correctly on Chinese and Japanese paragraphs
|
||||||
|
- the app doesn't crash on SIGINT anymore
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- there is a way to disable an account and it wouldn't connect when you change availability
|
||||||
|
- if you cancel password query an account becomes inactive and doesn't annoy you anymore
|
||||||
|
- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password
|
||||||
|
- if left the password field empty and chose KWallet as a storage Squawk will try to get that password from KWallet before asking you to input it
|
||||||
|
- accounts now connect to the server asynchronously - if one is stopped on password prompt another is connecting
|
||||||
|
- actualized translations, added English localization file
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- new "About" window with links, license, gratitudes
|
||||||
|
- if the authentication failed Squawk will ask again for your password and login
|
||||||
|
- now there is an amount of unread messages showing on top of Squawk launcher icon
|
||||||
|
- notifications now have buttons to open a conversation or to mark that message as read
|
||||||
|
|
||||||
|
## Squawk 0.2.1 (Apr 02, 2022)
|
||||||
|
### Bug fixes
|
||||||
|
- build in release mode now no longer spams warnings
|
||||||
|
- build now correctly installs all build plugin libs
|
||||||
|
- a bug where the correction message was received, the indication was on but the text didn't actually change
|
||||||
|
- message body now doesn't intecept context menu from the whole message
|
||||||
|
- message input now doesn't increase font when you remove everything from it
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- reduced amount of places where platform specific path separator is used
|
||||||
|
- now message input is automatically focused when you open a dialog or a room
|
||||||
|
- what() method on unhandled exception now actually tells what happened
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- the settings are here! You con config different stuff from there
|
||||||
|
- now it's possible to set up different qt styles from settings
|
||||||
|
- if you have KConfig nad KConfigWidgets packages installed - you can choose from global color schemes
|
||||||
|
- it's possible now to choose a folder where squawk is going to store downloaded files
|
||||||
|
- now you can correct your message
|
||||||
|
|
||||||
|
## Squawk 0.2.0 (Jan 10, 2022)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- carbon copies switches on again after reconnection
|
- carbon copies switches on again after reconnection
|
||||||
- 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
|
- infinite progress when open the dialogue with something that has no history to show
|
||||||
|
- fallback icons for buttons, when no supported theme is installed (shunf4)
|
||||||
|
- better handling messages with no id (shunf4)
|
||||||
|
- removed dependency: uuid, now it's on Qt (shunf4)
|
||||||
|
- better requesting latest history (shunf4)
|
||||||
|
|
||||||
### 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
|
- completely rewritten message feed, now it works way faster and looks cooler
|
||||||
- OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager
|
- 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
|
- 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
|
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
||||||
|
- way better compilation time (vae)
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- pasting images from clipboard to attachment (shunf4)
|
||||||
|
- possible compilation for windows and macOS (shunf4)
|
||||||
|
|
||||||
## Squawk 0.1.5 (Jul 29, 2020)
|
## Squawk 0.1.5 (Jul 29, 2020)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
191
CMakeLists.txt
@ -1,10 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(squawk VERSION 0.1.6 LANGUAGES CXX)
|
project(squawk VERSION 0.2.3 LANGUAGES CXX)
|
||||||
|
|
||||||
cmake_policy(SET CMP0076 NEW)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
|
cmake_policy(SET CMP0077 NEW)
|
||||||
cmake_policy(SET CMP0079 NEW)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
cmake_policy(SET CMP0167 NEW)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
set(QT_VERSION_MAJOR 5)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
@ -27,42 +30,43 @@ add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG})
|
|||||||
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
|
option(SYSTEM_LMDBAL "Use system lmdbal lib" ON)
|
||||||
option(WITH_KWALLET "Build KWallet support module" ON)
|
option(WITH_KWALLET "Build KWallet support module" ON)
|
||||||
option(WITH_KIO "Build KIO support module" ON)
|
option(WITH_KIO "Build KIO support module" ON)
|
||||||
|
option(WITH_KCONFIG "Build KConfig support module" ON)
|
||||||
|
option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out
|
||||||
|
option(WITH_SIMPLE_CRYPT "Builds with SimpleCrypt to obfuscate password" ON)
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
## Qt
|
## Qt
|
||||||
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
if (NOT DEFINED QT_VERSION_MAJOR)
|
||||||
|
find_package(QT NAMES Qt6 Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core)
|
||||||
|
else ()
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(Boost COMPONENTS)
|
find_package(Boost COMPONENTS)
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS})
|
target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS})
|
||||||
target_include_directories(squawk PRIVATE ${Qt5_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Widgets_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5DBus_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Gui_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Xml_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Network_INCLUDE_DIRS})
|
|
||||||
target_include_directories(squawk PRIVATE ${Qt5Core_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
## QXmpp
|
## OMEMO
|
||||||
if (SYSTEM_QXMPP)
|
if (WITH_OMEMO)
|
||||||
find_package(QXmpp CONFIG)
|
find_package(PkgConfig)
|
||||||
|
if (PKG_CONFIG_FOUND)
|
||||||
if (NOT QXmpp_FOUND)
|
pkg_check_modules(OMEMO libomemo-c)
|
||||||
set(SYSTEM_QXMPP OFF)
|
if (OMEMO_FOUND)
|
||||||
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
target_compile_definitions(squawk PRIVATE WITH_OMEMO)
|
||||||
|
message("Building with support of OMEMO")
|
||||||
|
else ()
|
||||||
|
message("libomemo-c package wasn't found, trying to build without OMEMO support")
|
||||||
|
set(WITH_OMEMO OFF)
|
||||||
|
endif ()
|
||||||
else ()
|
else ()
|
||||||
message("Building with system QXmpp")
|
message("PKG_CONFIG module wasn't found, can not check libomemo-c support, trying to build without OMEMO support")
|
||||||
|
set(WITH_OMEMO OFF)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (NOT SYSTEM_QXMPP)
|
|
||||||
target_link_libraries(squawk PRIVATE qxmpp)
|
|
||||||
add_subdirectory(external/qxmpp)
|
|
||||||
else ()
|
|
||||||
target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
## KIO
|
## KIO
|
||||||
if (WITH_KIO)
|
if (WITH_KIO)
|
||||||
find_package(KF5KIO CONFIG)
|
find_package(KF5KIO CONFIG)
|
||||||
@ -89,24 +93,110 @@ if (WITH_KWALLET)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
## Signal (TODO)
|
## KConfig
|
||||||
# find_package(Signal REQUIRED)
|
if (WITH_KCONFIG)
|
||||||
|
find_package(KF5Config CONFIG)
|
||||||
|
if (NOT KF5Config_FOUND)
|
||||||
|
set(WITH_KCONFIG OFF)
|
||||||
|
message("KConfig package wasn't found, KConfig support modules wouldn't be built")
|
||||||
|
else()
|
||||||
|
find_package(KF5ConfigWidgets CONFIG)
|
||||||
|
if (NOT KF5ConfigWidgets_FOUND)
|
||||||
|
set(WITH_KCONFIG OFF)
|
||||||
|
message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built")
|
||||||
|
else()
|
||||||
|
target_compile_definitions(squawk PRIVATE WITH_KCONFIG)
|
||||||
|
message("Building with support of KConfig")
|
||||||
|
message("Building with support of KConfigWidgets")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
## LMDB
|
## QXmpp
|
||||||
find_package(LMDB REQUIRED)
|
if (SYSTEM_QXMPP)
|
||||||
|
if (WITH_OMEMO)
|
||||||
|
find_package(QXmpp CONFIG COMPONENTS Omemo)
|
||||||
|
else ()
|
||||||
|
find_package(QXmpp CONFIG)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Linking
|
if (NOT QXmpp_FOUND)
|
||||||
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
set(SYSTEM_QXMPP OFF)
|
||||||
target_link_libraries(squawk PRIVATE lmdb)
|
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
||||||
target_link_libraries(squawk PRIVATE simpleCrypt)
|
else ()
|
||||||
# Link thread libraries on Linux
|
message("Building with system QXmpp")
|
||||||
|
endif ()
|
||||||
|
endif () #it's endif() + if() and not else() because I want it to have a fallback behaviour
|
||||||
|
if (NOT SYSTEM_QXMPP) #we can fail finding system QXmpp and this way we'll check bundled before failing completely
|
||||||
|
message("Building with bundled QXmpp")
|
||||||
|
|
||||||
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base)
|
||||||
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/client)
|
||||||
|
target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src)
|
||||||
|
|
||||||
|
if (WITH_OMEMO)
|
||||||
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo)
|
||||||
|
target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo)
|
||||||
|
set(BUILD_OMEMO ON)
|
||||||
|
set(BUILD_TESTS OFF)
|
||||||
|
else ()
|
||||||
|
set(BUILD_OMEMO OFF)
|
||||||
|
endif ()
|
||||||
|
add_subdirectory(external/qxmpp)
|
||||||
|
add_library(QXmpp::QXmpp ALIAS QXmppQt${QT_VERSION_MAJOR})
|
||||||
|
if (WITH_OMEMO)
|
||||||
|
target_include_directories(QXmppOmemoQt${QT_VERSION_MAJOR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src)
|
||||||
|
add_library(QXmpp::Omemo ALIAS QXmppOmemoQt${QT_VERSION_MAJOR})
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
## LMDBAL
|
||||||
|
if (SYSTEM_LMDBAL)
|
||||||
|
find_package(lmdbal)
|
||||||
|
if (NOT lmdbal_FOUND)
|
||||||
|
set(SYSTEM_LMDBAL OFF)
|
||||||
|
message("LMDBAL package wasn't found, trying to build with bundled LMDBAL")
|
||||||
|
else ()
|
||||||
|
message("Building with system LMDBAL")
|
||||||
|
endif ()
|
||||||
|
else()
|
||||||
|
message("Building with bundled LMDBAL")
|
||||||
|
set(BUILD_STATIC ON)
|
||||||
|
add_subdirectory(external/lmdbal)
|
||||||
|
add_library(LMDBAL::LMDBAL ALIAS LMDBAL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Linking
|
||||||
|
target_link_libraries(squawk
|
||||||
|
PRIVATE
|
||||||
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
Qt${QT_VERSION_MAJOR}::DBus
|
||||||
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Xml
|
||||||
|
LMDBAL::LMDBAL
|
||||||
|
QXmpp::QXmpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if (WITH_OMEMO)
|
||||||
|
target_link_libraries(squawk PRIVATE QXmpp::Omemo)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (WITH_SIMPLE_CRYPT)
|
||||||
|
target_compile_definitions(squawk PRIVATE WITH_SIMPLE_CRYPT)
|
||||||
|
add_subdirectory(external/simpleCrypt)
|
||||||
|
target_link_libraries(squawk PRIVATE simpleCrypt)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
## Link thread libraries on Linux
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
target_link_libraries(squawk PRIVATE Threads::Threads)
|
target_link_libraries(squawk PRIVATE Threads::Threads)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build type
|
## Build type
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif ()
|
endif ()
|
||||||
@ -114,16 +204,27 @@ endif ()
|
|||||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
target_compile_options(squawk PRIVATE
|
set (COMPILE_OPTIONS -fno-sized-deallocation) # for eliminating _ZdlPvm
|
||||||
"-Wall;-Wextra"
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
"$<$<CONFIG:DEBUG>:-g>"
|
list(APPEND COMPILE_OPTIONS -O3)
|
||||||
"$<$<CONFIG:RELEASE>:-O3>"
|
endif()
|
||||||
"-fno-sized-deallocation" # for eliminating _ZdlPvm
|
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
)
|
list(APPEND COMPILE_OPTIONS -g)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wall)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wextra)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("Compilation options: " ${COMPILE_OPTIONS})
|
||||||
|
target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS})
|
||||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
|
||||||
|
# I am not really sure about this solution
|
||||||
|
# This should enable plugins to be found in path like /usr/lib/squawk instead of just /usr/lib
|
||||||
|
set(PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/squawk")
|
||||||
|
add_compile_definitions(PLUGIN_PATH="${PLUGIN_PATH}")
|
||||||
|
|
||||||
|
add_subdirectory(main)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
add_subdirectory(external/simpleCrypt)
|
|
||||||
add_subdirectory(packaging)
|
add_subdirectory(packaging)
|
||||||
add_subdirectory(plugins)
|
add_subdirectory(plugins)
|
||||||
add_subdirectory(resources)
|
add_subdirectory(resources)
|
||||||
@ -131,13 +232,15 @@ add_subdirectory(shared)
|
|||||||
add_subdirectory(translations)
|
add_subdirectory(translations)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
|
|
||||||
# Install the executable
|
## Install the executable
|
||||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
|
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..."
|
add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..."
|
||||||
COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app"
|
COMMAND "${Qt${QT_VERSION_MAJOR}Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app"
|
||||||
)
|
)
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
83
README.md
@ -4,17 +4,20 @@
|
|||||||
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
||||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||||
|
|
||||||
![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png)
|
![Squawk screenshot](https://macaw.me/projects/squawk/0.2.2.png)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- QT 5.12 *(lower versions might work but it wasn't tested)*
|
- QT 5.12 *(lower versions might work but it wasn't tested)*
|
||||||
- lmdb
|
- CMake 3.4 or higher
|
||||||
- CMake 3.0 or higher
|
|
||||||
- qxmpp 1.1.0 or higher
|
- qxmpp 1.1.0 or higher
|
||||||
|
- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) for lmdb)
|
||||||
- KDE Frameworks: kwallet (optional)
|
- KDE Frameworks: kwallet (optional)
|
||||||
- KDE Frameworks: KIO (optional)
|
- KDE Frameworks: KIO (optional)
|
||||||
- Boost
|
- KDE Frameworks: KConfig (optional)
|
||||||
|
- KDE Frameworks: KConfigWidgets (optional)
|
||||||
|
- Boost (just one little hpp from there)
|
||||||
|
- Imagemagick (for compilation, to rasterize an SVG logo)
|
||||||
|
|
||||||
### Getting
|
### Getting
|
||||||
|
|
||||||
@ -30,14 +33,49 @@ $ pacaur -S squawk
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
You can also clone the repo and build it from source
|
You can also the repo and build it from source
|
||||||
|
|
||||||
Squawk requires Qt with SSL enabled. It uses CMake as build system.
|
Squawk requires Qt with SSL enabled. It uses CMake as build system.
|
||||||
|
|
||||||
Please check the prerequisites and install them before installation.
|
Please check the prerequisites and install them before installation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There are several ways to build Squawk. The one you need depends on whether you have `qxmpp` and `lmdbal` installed in your system.
|
||||||
|
|
||||||
|
#### Building with system dependencies
|
||||||
|
|
||||||
|
This is the easiest way but it requires you to have `qxmpp` and `lmdbal` installed as system packages. Here is what you do:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://git.macaw.me/blue/squawk
|
||||||
|
$ cd squawk
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ cmake ..
|
||||||
|
$ cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Building with bundled qxmpp
|
||||||
|
|
||||||
|
If you don't have any of `qxmpp` or `lmdbal` (or both) installed the process is abit mor complicated.
|
||||||
|
On the configuration stage you need to enable one or both entries in the square brackets, depending on what package your system lacks.
|
||||||
|
|
||||||
|
Here is what you do
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone --recurse-submodules https://git.macaw.me/blue/squawk
|
||||||
|
$ cd squawk
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ cmake .. [-D SYSTEM_QXMPP=False] [-D SYSTEM_LMDBAL=False]
|
||||||
|
$ cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
#### For Windows (Mingw-w64) build
|
#### For Windows (Mingw-w64) build
|
||||||
|
|
||||||
|
**Building for windows is not mainteined, but was possible in the past, you can try, but it probably won't work**
|
||||||
|
|
||||||
You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt.
|
You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt.
|
||||||
|
|
||||||
The best way to acquire library `lmdb` and `boost` is through Msys2.
|
The best way to acquire library `lmdb` and `boost` is through Msys2.
|
||||||
@ -46,51 +84,30 @@ First install Msys2, and then install `mingw-w64-x86_64-lmdb` and `mingw-w64-x86
|
|||||||
|
|
||||||
Then you need to provide the cmake cache entry when calling cmake for configuration:
|
Then you need to provide the cmake cache entry when calling cmake for configuration:
|
||||||
|
|
||||||
```
|
|
||||||
cmake .. -D LMDB_ROOT_DIR:PATH=<Msys2 Mingw64 Root Directory> -D BOOST_ROOT:PATH=<Msys2 Mingw64 Root Directory>
|
|
||||||
```
|
|
||||||
|
|
||||||
`<Msys2 Mingw64 Root Directory>`: e.g. `C:/msys64/mingw64`.
|
`<Msys2 Mingw64 Root Directory>`: e.g. `C:/msys64/mingw64`.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
There are two ways to build, it depends whether you have qxmpp installed in your system
|
|
||||||
|
|
||||||
#### Building with system qxmpp
|
|
||||||
|
|
||||||
Here is what you do
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://git.macaw.me/blue/squawk
|
|
||||||
$ cd squawk
|
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
$ cmake .. [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
|
||||||
$ cmake --build .
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Building with bundled qxmpp
|
|
||||||
|
|
||||||
Here is what you do
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone --recurse-submodules https://git.macaw.me/blue/squawk
|
$ git clone --recurse-submodules https://git.macaw.me/blue/squawk
|
||||||
$ cd squawk
|
$ cd squawk
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake .. -D SYSTEM_QXMPP=False [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
$ cmake .. -D SYSTEM_QXMPP=False -D SYSTEM_LMDBAL=False -D LMDB_ROOT_DIR:PATH=<Msys2 Mingw64 Root Directory> -D BOOST_ROOT:PATH=<Msys2 Mingw64 Root Directory>
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
You can always refer to `appveyor.yml` to see how AppVeyor build squawk.
|
You can always refer to `appveyor.yml` to see how AppVeyor build squawk for windows.
|
||||||
|
|
||||||
### List of keys
|
### List of keys
|
||||||
|
|
||||||
Here is the list of keys you can pass to configuration phase of `cmake ..`.
|
Here is the list of keys you can pass to configuration phase of `cmake ..`:
|
||||||
|
|
||||||
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
|
- `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`)
|
||||||
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
|
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
|
||||||
|
- `SYSTEM_LMDBAL` - `True` tries to link against `LMDABL` installed in the system, `False` builds bundled `LMDBAL` library (default is `True`)
|
||||||
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
|
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
|
||||||
- `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`)
|
- `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`)
|
||||||
|
- `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`)
|
||||||
|
- `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license
|
|
||||||
#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code
|
|
||||||
|
|
||||||
# Try to find LMDB headers and library.
|
|
||||||
#
|
|
||||||
# Usage of this module as follows:
|
|
||||||
#
|
|
||||||
# find_package(LMDB)
|
|
||||||
#
|
|
||||||
# Variables used by this module, they can change the default behaviour and need
|
|
||||||
# to be set before calling find_package:
|
|
||||||
#
|
|
||||||
# LMDB_ROOT_DIR Set this variable to the root installation of
|
|
||||||
# LMDB if the module has problems finding the
|
|
||||||
# proper installation path.
|
|
||||||
#
|
|
||||||
# Variables defined by this module:
|
|
||||||
#
|
|
||||||
# LMDB_FOUND System has LMDB library/headers.
|
|
||||||
# LMDB_LIBRARIES The LMDB library.
|
|
||||||
# LMDB_INCLUDE_DIRS The location of LMDB headers.
|
|
||||||
|
|
||||||
find_path(LMDB_ROOT_DIR
|
|
||||||
NAMES include/lmdb.h
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(LMDB_LIBRARIES
|
|
||||||
NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible
|
|
||||||
HINTS ${LMDB_ROOT_DIR}/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(lmdb UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(lmdb PROPERTIES
|
|
||||||
IMPORTED_LOCATION ${LMDB_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
find_path(LMDB_INCLUDE_DIRS
|
|
||||||
NAMES lmdb.h
|
|
||||||
HINTS ${LMDB_ROOT_DIR}/include
|
|
||||||
)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(LMDB DEFAULT_MSG
|
|
||||||
LMDB_LIBRARIES
|
|
||||||
LMDB_INCLUDE_DIRS
|
|
||||||
)
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
LMDB_ROOT_DIR
|
|
||||||
LMDB_LIBRARIES
|
|
||||||
LMDB_INCLUDE_DIRS
|
|
||||||
)
|
|
@ -1,15 +0,0 @@
|
|||||||
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 ()
|
|
@ -3,32 +3,34 @@ if(WIN32)
|
|||||||
set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp)
|
set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
account.cpp
|
||||||
|
adapterfunctions.cpp
|
||||||
|
conference.cpp
|
||||||
|
contact.cpp
|
||||||
|
rosteritem.cpp
|
||||||
|
${SIGNALCATCHER_SOURCE}
|
||||||
|
squawk.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADER_FILES
|
||||||
|
account.h
|
||||||
|
adapterfunctions.h
|
||||||
|
conference.h
|
||||||
|
contact.h
|
||||||
|
rosteritem.h
|
||||||
|
signalcatcher.h
|
||||||
|
squawk.h
|
||||||
|
)
|
||||||
|
|
||||||
target_sources(squawk PRIVATE
|
target_sources(squawk PRIVATE
|
||||||
account.cpp
|
${SOURCE_FILES}
|
||||||
account.h
|
${HEADER_FILES}
|
||||||
adapterFuctions.cpp
|
)
|
||||||
archive.cpp
|
|
||||||
archive.h
|
|
||||||
conference.cpp
|
|
||||||
conference.h
|
|
||||||
contact.cpp
|
|
||||||
contact.h
|
|
||||||
main.cpp
|
|
||||||
networkaccess.cpp
|
|
||||||
networkaccess.h
|
|
||||||
rosteritem.cpp
|
|
||||||
rosteritem.h
|
|
||||||
${SIGNALCATCHER_SOURCE}
|
|
||||||
signalcatcher.h
|
|
||||||
squawk.cpp
|
|
||||||
squawk.h
|
|
||||||
storage.cpp
|
|
||||||
storage.h
|
|
||||||
urlstorage.cpp
|
|
||||||
urlstorage.h
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_subdirectory(handlers)
|
add_subdirectory(handlers)
|
||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
add_subdirectory(components)
|
||||||
|
add_subdirectory(delayManager)
|
||||||
|
842
core/account.cpp
115
core/account.h
@ -15,9 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#pragma once
|
||||||
#ifndef CORE_ACCOUNT_H
|
|
||||||
#define CORE_ACCOUNT_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
@ -29,9 +27,14 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include <QXmppRosterManager.h>
|
#include <QXmppRosterManager.h>
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
#include <QXmppCarbonManagerV2.h>
|
||||||
|
#else
|
||||||
#include <QXmppCarbonManager.h>
|
#include <QXmppCarbonManager.h>
|
||||||
|
#endif
|
||||||
#include <QXmppDiscoveryManager.h>
|
#include <QXmppDiscoveryManager.h>
|
||||||
#include <QXmppMamManager.h>
|
#include <QXmppMamManager.h>
|
||||||
#include <QXmppMucManager.h>
|
#include <QXmppMucManager.h>
|
||||||
@ -39,32 +42,58 @@
|
|||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
#include <QXmppVCardIq.h>
|
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
#include <QXmppPubSubManager.h>
|
||||||
|
|
||||||
#include "shared/shared.h"
|
#include <shared/shared.h>
|
||||||
|
#include <shared/identity.h>
|
||||||
|
#include <shared/info.h>
|
||||||
|
#include <shared/clientid.h>
|
||||||
#include "contact.h"
|
#include "contact.h"
|
||||||
#include "conference.h"
|
#include "conference.h"
|
||||||
#include "networkaccess.h"
|
#include <core/components/networkaccess.h>
|
||||||
|
#include <core/delayManager/manager.h>
|
||||||
|
|
||||||
#include "handlers/messagehandler.h"
|
#include "handlers/messagehandler.h"
|
||||||
#include "handlers/rosterhandler.h"
|
#include "handlers/rosterhandler.h"
|
||||||
|
#include "handlers/vcardhandler.h"
|
||||||
|
#include "handlers/discoveryhandler.h"
|
||||||
|
|
||||||
namespace Core
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
{
|
#include <QXmppTrustManager.h>
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
#include <QXmppOmemoManager.h>
|
||||||
|
#include "handlers/omemohandler.h"
|
||||||
|
#endif
|
||||||
|
#include "handlers/trusthandler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class Account : public QObject
|
namespace Core {
|
||||||
{
|
|
||||||
|
class Account : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class MessageHandler;
|
friend class MessageHandler;
|
||||||
friend class RosterHandler;
|
friend class RosterHandler;
|
||||||
|
friend class VCardHandler;
|
||||||
|
friend class DiscoveryHandler;
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
friend class OmemoHandler;
|
||||||
|
friend class TrustHandler;
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
|
enum class Error {
|
||||||
|
authentication,
|
||||||
|
other,
|
||||||
|
none
|
||||||
|
};
|
||||||
|
|
||||||
Account(
|
Account(
|
||||||
const QString& p_login,
|
const QString& p_login,
|
||||||
const QString& p_server,
|
const QString& p_server,
|
||||||
const QString& p_password,
|
const QString& p_password,
|
||||||
const QString& p_name,
|
const QString& p_name,
|
||||||
|
bool p_active,
|
||||||
NetworkAccess* p_net,
|
NetworkAccess* p_net,
|
||||||
QObject* parent = 0);
|
QObject* parent = 0);
|
||||||
~Account();
|
~Account();
|
||||||
@ -76,8 +105,12 @@ public:
|
|||||||
QString getPassword() const;
|
QString getPassword() const;
|
||||||
QString getResource() const;
|
QString getResource() const;
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath() const;
|
||||||
|
QString getBareJid() const;
|
||||||
|
QString getFullJid() const;
|
||||||
Shared::Availability getAvailability() const;
|
Shared::Availability getAvailability() const;
|
||||||
Shared::AccountPassword getPasswordType() const;
|
Shared::AccountPassword getPasswordType() const;
|
||||||
|
Error getLastError() const;
|
||||||
|
bool getActive() const;
|
||||||
|
|
||||||
void setName(const QString& p_name);
|
void setName(const QString& p_name);
|
||||||
void setLogin(const QString& p_login);
|
void setLogin(const QString& p_login);
|
||||||
@ -86,8 +119,8 @@ public:
|
|||||||
void setResource(const QString& p_resource);
|
void setResource(const QString& p_resource);
|
||||||
void setAvailability(Shared::Availability avail);
|
void setAvailability(Shared::Availability avail);
|
||||||
void setPasswordType(Shared::AccountPassword pt);
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
QString getFullJid() const;
|
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(const Shared::Message& data);
|
||||||
|
void setActive(bool p_active);
|
||||||
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,19 +130,24 @@ public:
|
|||||||
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||||
void renameContactRequest(const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& jid, const QString& newName);
|
||||||
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
void setContactEncryption(const QString& jid, Shared::EncryptionProtocol value);
|
||||||
|
|
||||||
void setRoomJoined(const QString& jid, bool joined);
|
void setRoomJoined(const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||||
void removeRoomRequest(const QString& jid);
|
void removeRoomRequest(const QString& jid);
|
||||||
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void uploadVCard(const Shared::VCard& card);
|
void updateInfo(const Shared::Info& info);
|
||||||
void resendMessage(const QString& jid, const QString& id);
|
void resendMessage(const QString& jid, const QString& id);
|
||||||
|
void replaceMessage(const QString& originalId, const Shared::Message& data);
|
||||||
|
void invalidatePassword();
|
||||||
|
|
||||||
|
void discoverInfo(const QString& address, const QString& node);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void connect();
|
void connect();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void reconnect();
|
void reconnect();
|
||||||
void requestVCard(const QString& jid);
|
void requestInfo(const QString& jid);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changed(const QMap<QString, QVariant>& data);
|
void changed(const QMap<QString, QVariant>& data);
|
||||||
@ -133,9 +171,12 @@ signals:
|
|||||||
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
||||||
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
void infoReady(const Shared::Info& info);
|
||||||
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
||||||
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
||||||
|
void needPassword();
|
||||||
|
void infoDiscovered(const QString& address, const QString& node, const std::set<Shared::Identity>& identities, const std::set<QString>& features);
|
||||||
|
void pepSupportChanged(Shared::Support support);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@ -144,7 +185,26 @@ private:
|
|||||||
QXmppConfiguration config;
|
QXmppConfiguration config;
|
||||||
QXmppPresence presence;
|
QXmppPresence presence;
|
||||||
Shared::ConnectionState state;
|
Shared::ConnectionState state;
|
||||||
|
|
||||||
|
MessageHandler* mh;
|
||||||
|
RosterHandler* rh;
|
||||||
|
VCardHandler* vh;
|
||||||
|
DiscoveryHandler* dh;
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
TrustHandler* th;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
OmemoHandler* oh;
|
||||||
|
|
||||||
|
QXmppOmemoManager* om;
|
||||||
|
#endif
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
QXmppTrustManager* tm;
|
||||||
|
QXmppCarbonManagerV2* cm;
|
||||||
|
QXmppPubSubManager* psm;
|
||||||
|
#else
|
||||||
QXmppCarbonManager* cm;
|
QXmppCarbonManager* cm;
|
||||||
|
#endif
|
||||||
QXmppMamManager* am;
|
QXmppMamManager* am;
|
||||||
QXmppMucManager* mm;
|
QXmppMucManager* mm;
|
||||||
QXmppBookmarkManager* bm;
|
QXmppBookmarkManager* bm;
|
||||||
@ -156,16 +216,14 @@ private:
|
|||||||
bool reconnectScheduled;
|
bool reconnectScheduled;
|
||||||
QTimer* reconnectTimer;
|
QTimer* reconnectTimer;
|
||||||
|
|
||||||
std::set<QString> pendingVCardRequests;
|
|
||||||
|
|
||||||
QString avatarHash;
|
|
||||||
QString avatarType;
|
|
||||||
bool ownVCardRequestInProgress;
|
|
||||||
NetworkAccess* network;
|
NetworkAccess* network;
|
||||||
|
DelayManager::Manager* delay;
|
||||||
Shared::AccountPassword passwordType;
|
Shared::AccountPassword passwordType;
|
||||||
|
Error lastError;
|
||||||
MessageHandler* mh;
|
Shared::Support pepSupport;
|
||||||
RosterHandler* rh;
|
bool active;
|
||||||
|
bool notReadyPassword;
|
||||||
|
bool loadingOmemo;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChange(QXmppClient::State state);
|
void onClientStateChange(QXmppClient::State state);
|
||||||
@ -179,21 +237,12 @@ private slots:
|
|||||||
|
|
||||||
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
||||||
|
|
||||||
void onVCardReceived(const QXmppVCardIq& card);
|
|
||||||
void onOwnVCardReceived(const QXmppVCardIq& card);
|
|
||||||
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
|
||||||
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
|
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleDisconnection();
|
void handleDisconnection();
|
||||||
void onReconnectTimer();
|
void onReconnectTimer();
|
||||||
|
void setPepSupport(Shared::Support support);
|
||||||
|
void runDiscoveryService();
|
||||||
};
|
};
|
||||||
|
|
||||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
|
||||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // CORE_ACCOUNT_H
|
|
||||||
|
@ -15,10 +15,8 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
#define CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
|
|
||||||
#include "account.h"
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
||||||
{
|
{
|
||||||
@ -264,12 +262,10 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
|
|||||||
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const std::pair<QString, QXmppVCardPhone>& phone : phones) {
|
for (const std::pair<const QString, QXmppVCardPhone>& phone : phones) {
|
||||||
phs.push_back(phone.second);
|
phs.push_back(phone.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iq.setEmails(emails);
|
iq.setEmails(emails);
|
||||||
iq.setPhones(phs);
|
iq.setPhones(phs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_ADAPTER_FUNCTIONS_H
|
|
47
core/adapterfunctions.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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_ADAPTER_FUNCTIONS_H
|
||||||
|
#define CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <QXmppTask.h>
|
||||||
|
#include <QXmppPromise.h>
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||||
|
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
QXmppTask<T> makeReadyTask(T &&value) {
|
||||||
|
QXmppPromise<T> promise;
|
||||||
|
promise.finish(std::move(value));
|
||||||
|
return promise.task();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QXmppTask<void> makeReadyTask() {
|
||||||
|
QXmppPromise<void> promise;
|
||||||
|
promise.finish();
|
||||||
|
return promise.task();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CORE_ADAPTER_FUNCTIONS_H
|
998
core/archive.cpp
@ -1,998 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "archive.h"
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
Core::Archive::Archive(const QString& p_jid, QObject* parent):
|
|
||||||
QObject(parent),
|
|
||||||
jid(p_jid),
|
|
||||||
opened(false),
|
|
||||||
fromTheBeginning(false),
|
|
||||||
environment(),
|
|
||||||
main(),
|
|
||||||
order(),
|
|
||||||
stats(),
|
|
||||||
avatars()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Archive::~Archive()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::open(const QString& account)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
mdb_env_create(&environment);
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
|
||||||
path += "/" + account + "/" + jid;
|
|
||||||
QDir cache(path);
|
|
||||||
|
|
||||||
if (!cache.exists()) {
|
|
||||||
bool res = cache.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
throw Directory(path.toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 5);
|
|
||||||
mdb_env_set_mapsize(environment, 512UL * 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, "main", MDB_CREATE, &main);
|
|
||||||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
|
||||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
|
||||||
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
|
||||||
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
try {
|
|
||||||
fromTheBeginning = getStatBoolValue("beginning", txn);
|
|
||||||
} catch (const NotFound& e) {
|
|
||||||
fromTheBeginning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string sJid = jid.toStdString();
|
|
||||||
AvatarInfo info;
|
|
||||||
bool hasAvatar = readAvatarInfo(info, sJid, txn);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
|
|
||||||
if (hasAvatar) {
|
|
||||||
QFile ava(path + "/" + sJid.c_str() + "." + info.type);
|
|
||||||
if (!ava.exists()) {
|
|
||||||
bool success = dropAvatar(sJid);
|
|
||||||
if (!success) {
|
|
||||||
qDebug() << "error opening archive" << jid << "for account" << account
|
|
||||||
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::close()
|
|
||||||
{
|
|
||||||
if (opened) {
|
|
||||||
mdb_dbi_close(environment, sid);
|
|
||||||
mdb_dbi_close(environment, avatars);
|
|
||||||
mdb_dbi_close(environment, stats);
|
|
||||||
mdb_dbi_close(environment, order);
|
|
||||||
mdb_dbi_close(environment, main);
|
|
||||||
mdb_env_close(environment);
|
|
||||||
opened = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::addElement(const Shared::Message& message)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("addElement", jid.toStdString());
|
|
||||||
}
|
|
||||||
QByteArray ba;
|
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
||||||
message.serialize(ds);
|
|
||||||
quint64 stamp = message.getTime().toMSecsSinceEpoch();
|
|
||||||
const std::string& id = message.getId().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();
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
int rc;
|
|
||||||
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
|
||||||
if (rc == 0) {
|
|
||||||
MDB_val orderKey;
|
|
||||||
orderKey.mv_size = 8;
|
|
||||||
orderKey.mv_data = (uint8_t*) &stamp;
|
|
||||||
|
|
||||||
rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "An element couldn't be inserted into the index" << mdb_strerror(rc);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (message.getStanzaId().size() > 0) {
|
|
||||||
const std::string& szid = message.getStanzaId().toStdString();
|
|
||||||
|
|
||||||
lmdbKey.mv_size = szid.size();
|
|
||||||
lmdbKey.mv_data = (char*)szid.c_str();
|
|
||||||
lmdbData.mv_size = id.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)id.data();
|
|
||||||
rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "An element stanzaId to id pair couldn't be inserted into the archive" << mdb_strerror(rc);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
rc = mdb_txn_commit(txn);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "A transaction error: " << mdb_strerror(rc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
rc = mdb_txn_commit(txn);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "A transaction error: " << mdb_strerror(rc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::clear()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("clear", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
mdb_drop(txn, main, 0);
|
|
||||||
mdb_drop(txn, order, 0);
|
|
||||||
mdb_drop(txn, stats, 0);
|
|
||||||
mdb_drop(txn, avatars, 0);
|
|
||||||
mdb_drop(txn, sid, 0);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::getElement(const QString& id) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getElement", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Shared::Message msg = getMessage(id.toStdString(), txn);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return msg;
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::hasElement(const QString& id) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("hasElement", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
|
|
||||||
bool has;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.toStdString().c_str();
|
|
||||||
int rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
|
|
||||||
has = rc == 0;
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
|
|
||||||
return has;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) const
|
|
||||||
{
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
int rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
Shared::Message msg;
|
|
||||||
msg.deserialize(ds);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
} else if (rc == MDB_NOTFOUND) {
|
|
||||||
throw NotFound(id, jid.toStdString());
|
|
||||||
} else {
|
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("setMessageState", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
|
|
||||||
std::string strId(id.toStdString());
|
|
||||||
try {
|
|
||||||
Shared::Message msg = getMessage(strId, txn);
|
|
||||||
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
|
||||||
QDateTime oTime = msg.getTime();
|
|
||||||
bool idChange = msg.change(data);
|
|
||||||
QDateTime nTime = msg.getTime();
|
|
||||||
bool orderChange = oTime != nTime;
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
QByteArray ba;
|
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
||||||
msg.serialize(ds);
|
|
||||||
|
|
||||||
lmdbKey.mv_size = strId.size();
|
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
|
||||||
int rc;
|
|
||||||
if (idChange || orderChange) {
|
|
||||||
if (idChange) {
|
|
||||||
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) {
|
|
||||||
strId = msg.getId().toStdString();
|
|
||||||
lmdbKey.mv_size = strId.size();
|
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
|
||||||
|
|
||||||
quint64 stamp = nTime.toMSecsSinceEpoch();
|
|
||||||
lmdbData.mv_data = (quint8*)&stamp;
|
|
||||||
lmdbData.mv_size = 8;
|
|
||||||
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString qsid = msg.getStanzaId();
|
|
||||||
if (qsid.size() > 0 && (idChange || !hadStanzaId)) {
|
|
||||||
std::string szid = qsid.toStdString();
|
|
||||||
|
|
||||||
lmdbData.mv_size = szid.size();
|
|
||||||
lmdbData.mv_data = (char*)szid.c_str();
|
|
||||||
rc = mdb_put(txn, sid, &lmdbData, &lmdbKey, 0);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
lmdbData.mv_size = ba.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
|
||||||
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0);
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = mdb_txn_commit(txn);
|
|
||||||
} else {
|
|
||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::newest()
|
|
||||||
{
|
|
||||||
return edge(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::newestId()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("newestId", jid.toStdString());
|
|
||||||
}
|
|
||||||
Shared::Message msg = newest();
|
|
||||||
return msg.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::oldestId()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("oldestId", jid.toStdString());
|
|
||||||
}
|
|
||||||
Shared::Message msg = oldest();
|
|
||||||
return msg.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::oldest()
|
|
||||||
{
|
|
||||||
return edge(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::edge(bool end)
|
|
||||||
{
|
|
||||||
QString name;
|
|
||||||
MDB_cursor_op begin;
|
|
||||||
MDB_cursor_op iteration;
|
|
||||||
if (end) {
|
|
||||||
name = "newest";
|
|
||||||
begin = MDB_LAST;
|
|
||||||
iteration = MDB_PREV;
|
|
||||||
} else {
|
|
||||||
name = "oldest";
|
|
||||||
begin = MDB_FIRST;
|
|
||||||
iteration = MDB_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed(name.toStdString(), jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
int rc;
|
|
||||||
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
rc = mdb_cursor_open(txn, order, &cursor);
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, begin);
|
|
||||||
|
|
||||||
Shared::Message msg = getStoredMessage(txn, cursor, iteration, &lmdbKey, &lmdbData, rc);
|
|
||||||
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "Error geting" << name << "message" << mdb_strerror(rc);
|
|
||||||
throw Empty(jid.toStdString());
|
|
||||||
} else {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message Core::Archive::getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc)
|
|
||||||
{
|
|
||||||
Shared::Message msg;
|
|
||||||
std::string sId;
|
|
||||||
while (true) {
|
|
||||||
if (rc) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sId = std::string((char*)value->mv_data, value->mv_size);
|
|
||||||
|
|
||||||
try {
|
|
||||||
msg = getMessage(sId, txn);
|
|
||||||
if (msg.serverStored()) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
rc = mdb_cursor_get(cursor, key, value, op);
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messages)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("addElements", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
int success = 0;
|
|
||||||
int rc = 0;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
std::list<Shared::Message>::const_iterator itr = messages.begin();
|
|
||||||
while (rc == 0 && itr != messages.end()) {
|
|
||||||
const Shared::Message& message = *itr;
|
|
||||||
|
|
||||||
QByteArray ba;
|
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
||||||
message.serialize(ds);
|
|
||||||
quint64 stamp = message.getTime().toMSecsSinceEpoch();
|
|
||||||
const std::string& id = message.getId().toStdString();
|
|
||||||
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = ba.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
|
||||||
|
|
||||||
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
|
||||||
if (rc == 0) {
|
|
||||||
MDB_val orderKey;
|
|
||||||
orderKey.mv_size = 8;
|
|
||||||
orderKey.mv_data = (uint8_t*) &stamp;
|
|
||||||
|
|
||||||
rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc);
|
|
||||||
} else {
|
|
||||||
if (message.getStanzaId().size() > 0) {
|
|
||||||
const std::string& szid = message.getStanzaId().toStdString();
|
|
||||||
|
|
||||||
lmdbKey.mv_size = szid.size();
|
|
||||||
lmdbKey.mv_data = (char*)szid.c_str();
|
|
||||||
lmdbData.mv_size = id.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)id.data();
|
|
||||||
rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "During bulk add an element stanzaId to id pair couldn't be inserted into the archive, continuing without stanzaId" << mdb_strerror(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
success++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (rc == MDB_KEYEXIST) {
|
|
||||||
rc = 0;
|
|
||||||
} else {
|
|
||||||
qDebug() << "An element couldn't been added to the archive, aborting the transaction" << mdb_strerror(rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
success = 0;
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
long unsigned int Core::Archive::size() const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("size", jid.toStdString());
|
|
||||||
}
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
MDB_stat stat;
|
|
||||||
mdb_stat(txn, order, &stat);
|
|
||||||
size_t amount = stat.ms_entries;
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getBefore", jid.toStdString());
|
|
||||||
}
|
|
||||||
std::list<Shared::Message> res;
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
MDB_txn *txn;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
int rc;
|
|
||||||
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
rc = mdb_cursor_open(txn, order, &cursor);
|
|
||||||
if (id == "") {
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "Error getting before" << mdb_strerror(rc) << ", id:" << id;
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
|
|
||||||
throw Empty(jid.toStdString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::string stdId(id.toStdString());
|
|
||||||
try {
|
|
||||||
Shared::Message msg = getMessage(stdId, txn);
|
|
||||||
quint64 stamp = msg.getTime().toMSecsSinceEpoch();
|
|
||||||
lmdbKey.mv_data = (quint8*)&stamp;
|
|
||||||
lmdbKey.mv_size = 8;
|
|
||||||
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET);
|
|
||||||
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "Error getting before: couldn't set " << mdb_strerror(rc);
|
|
||||||
throw NotFound(stdId, jid.toStdString());
|
|
||||||
} else {
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() << "Error getting before, couldn't prev " << mdb_strerror(rc);
|
|
||||||
throw NotFound(stdId, jid.toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (...) {
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
MDB_val dKey, dData;
|
|
||||||
dKey.mv_size = lmdbData.mv_size;
|
|
||||||
dKey.mv_data = lmdbData.mv_data;
|
|
||||||
rc = mdb_get(txn, main, &dKey, &dData);
|
|
||||||
if (rc) {
|
|
||||||
qDebug() <<"Get error: " << mdb_strerror(rc);
|
|
||||||
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw NotFound(sId, jid.toStdString());
|
|
||||||
} else {
|
|
||||||
QByteArray ba((char*)dData.mv_data, dData.mv_size);
|
|
||||||
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
res.emplace_back();
|
|
||||||
Shared::Message& msg = res.back();
|
|
||||||
msg.deserialize(ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
--count;
|
|
||||||
|
|
||||||
} while (count > 0 && mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV) == 0);
|
|
||||||
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::isFromTheBeginning()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("isFromTheBeginning", jid.toStdString());
|
|
||||||
}
|
|
||||||
return fromTheBeginning;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::setFromTheBeginning(bool is)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("setFromTheBeginning", jid.toStdString());
|
|
||||||
}
|
|
||||||
if (fromTheBeginning != is) {
|
|
||||||
fromTheBeginning = is;
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
bool success = setStatValue("beginning", is, txn);
|
|
||||||
if (success) {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
} else {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::idByStanzaId(const QString& stanzaId) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("idByStanzaId", jid.toStdString());
|
|
||||||
}
|
|
||||||
QString id;
|
|
||||||
std::string ssid = stanzaId.toStdString();
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = ssid.size();
|
|
||||||
lmdbKey.mv_data = (char*)ssid.c_str();
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
int rc = mdb_get(txn, sid, &lmdbKey, &lmdbData);
|
|
||||||
if (rc == 0) {
|
|
||||||
id = QString::fromStdString(std::string((char*)lmdbData.mv_data, lmdbData.mv_size));
|
|
||||||
}
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::stanzaIdById(const QString& id) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("stanzaIdById", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Shared::Message msg = getElement(id);
|
|
||||||
return msg.getStanzaId();
|
|
||||||
} catch (const NotFound& e) {
|
|
||||||
return QString();
|
|
||||||
} catch (const Empty& e) {
|
|
||||||
return QString();
|
|
||||||
} catch (...) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::printOrder()
|
|
||||||
{
|
|
||||||
qDebug() << "Printing order";
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
mdb_cursor_open(txn, order, &cursor);
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
|
|
||||||
mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
|
|
||||||
|
|
||||||
do {
|
|
||||||
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
qDebug() << QString(sId.c_str());
|
|
||||||
} while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0);
|
|
||||||
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::printKeys()
|
|
||||||
{
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
mdb_cursor_open(txn, main, &cursor);
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
|
|
||||||
mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
|
|
||||||
|
|
||||||
do {
|
|
||||||
std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size);
|
|
||||||
qDebug() << QString(sId.c_str());
|
|
||||||
} while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0);
|
|
||||||
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
|
|
||||||
{
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
|
||||||
if (rc == MDB_NOTFOUND) {
|
|
||||||
throw NotFound(id, jid.toStdString());
|
|
||||||
} else if (rc) {
|
|
||||||
std::string err(mdb_strerror(rc));
|
|
||||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
|
|
||||||
throw Unknown(jid.toStdString(), err);
|
|
||||||
} else {
|
|
||||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
|
||||||
bool is;
|
|
||||||
if (value == 144) {
|
|
||||||
is = false;
|
|
||||||
} else if (value == 72) {
|
|
||||||
is = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong";
|
|
||||||
throw NotFound(id, jid.toStdString());
|
|
||||||
}
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn)
|
|
||||||
{
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
|
||||||
if (rc == MDB_NOTFOUND) {
|
|
||||||
throw NotFound(id, jid.toStdString());
|
|
||||||
} else if (rc) {
|
|
||||||
std::string err(mdb_strerror(rc));
|
|
||||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
|
|
||||||
throw Unknown(jid.toStdString(), err);
|
|
||||||
} else {
|
|
||||||
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn)
|
|
||||||
{
|
|
||||||
uint8_t binvalue = 144;
|
|
||||||
if (value) {
|
|
||||||
binvalue = 72;
|
|
||||||
}
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = sizeof binvalue;
|
|
||||||
lmdbData.mv_data = &binvalue;
|
|
||||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
|
||||||
if (rc != 0) {
|
|
||||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn)
|
|
||||||
{
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = value.size();
|
|
||||||
lmdbData.mv_data = (char*)value.c_str();
|
|
||||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
|
||||||
if (rc != 0) {
|
|
||||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::dropAvatar(const std::string& resource)
|
|
||||||
{
|
|
||||||
MDB_txn *txn;
|
|
||||||
MDB_val lmdbKey;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
lmdbKey.mv_size = resource.size();
|
|
||||||
lmdbKey.mv_data = (char*)resource.c_str();
|
|
||||||
int rc = mdb_del(txn, avatars, &lmdbKey, NULL);
|
|
||||||
if (rc != 0) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("setAvatar", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarInfo oldInfo;
|
|
||||||
bool hasAvatar = readAvatarInfo(oldInfo, resource);
|
|
||||||
std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString();
|
|
||||||
|
|
||||||
if (data.size() == 0) {
|
|
||||||
if (!hasAvatar) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return dropAvatar(res);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const char* cep;
|
|
||||||
mdb_env_get_path(environment, &cep);
|
|
||||||
QString currentPath(cep);
|
|
||||||
bool needToRemoveOld = false;
|
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
|
||||||
hash.addData(data);
|
|
||||||
QByteArray newHash(hash.result());
|
|
||||||
if (hasAvatar) {
|
|
||||||
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
|
||||||
if (oldAvatar.exists()) {
|
|
||||||
if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) {
|
|
||||||
needToRemoveOld = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QMimeDatabase db;
|
|
||||||
QMimeType type = db.mimeTypeForData(data);
|
|
||||||
QString ext = type.preferredSuffix();
|
|
||||||
QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext);
|
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
|
||||||
newAvatar.write(data);
|
|
||||||
newAvatar.close();
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
QByteArray value;
|
|
||||||
newInfo.type = ext;
|
|
||||||
newInfo.hash = newHash;
|
|
||||||
newInfo.autogenerated = generated;
|
|
||||||
newInfo.serialize(&value);
|
|
||||||
lmdbKey.mv_size = res.size();
|
|
||||||
lmdbKey.mv_data = (char*)res.c_str();
|
|
||||||
lmdbData.mv_size = value.size();
|
|
||||||
lmdbData.mv_data = value.data();
|
|
||||||
int rc = mdb_put(txn, avatars, &lmdbKey, &lmdbData, 0);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
|
||||||
if (needToRemoveOld) {
|
|
||||||
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
|
||||||
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
|
||||||
}
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
if (needToRemoveOld) {
|
|
||||||
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
|
||||||
oldAvatar.remove();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
|
||||||
if (needToRemoveOld) {
|
|
||||||
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
|
||||||
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("readAvatarInfo", jid.toStdString());
|
|
||||||
}
|
|
||||||
std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString();
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
|
|
||||||
try {
|
|
||||||
bool success = readAvatarInfo(target, res, txn);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return success;
|
|
||||||
} catch (...) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std::string& res, MDB_txn* txn) const
|
|
||||||
{
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = res.size();
|
|
||||||
lmdbKey.mv_data = (char*)res.c_str();
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
rc = mdb_get(txn, avatars, &lmdbKey, &lmdbData);
|
|
||||||
if (rc == MDB_NOTFOUND) {
|
|
||||||
return false;
|
|
||||||
} else if (rc) {
|
|
||||||
std::string err(mdb_strerror(rc));
|
|
||||||
qDebug() << "error reading avatar info for" << res.c_str() << "resource of" << jid << err.c_str();
|
|
||||||
throw Unknown(jid.toStdString(), err);
|
|
||||||
} else {
|
|
||||||
target.deserialize((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("readAllResourcesAvatars", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
MDB_txn *txn;
|
|
||||||
MDB_cursor* cursor;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
mdb_cursor_open(txn, avatars, &cursor);
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
|
|
||||||
if (rc == 0) { //the db might be empty yet
|
|
||||||
do {
|
|
||||||
std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size);
|
|
||||||
QString res(sId.c_str());
|
|
||||||
if (res != jid) {
|
|
||||||
data.emplace(res, AvatarInfo());
|
|
||||||
data[res].deserialize((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
}
|
|
||||||
} while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
mdb_cursor_close(cursor);
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("readAvatarInfo", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarInfo info;
|
|
||||||
bool success = readAvatarInfo(info, resource);
|
|
||||||
if (success) {
|
|
||||||
return info;
|
|
||||||
} else {
|
|
||||||
throw NoAvatar(jid.toStdString(), resource.toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Archive::AvatarInfo::AvatarInfo():
|
|
||||||
type(),
|
|
||||||
hash(),
|
|
||||||
autogenerated(false) {}
|
|
||||||
|
|
||||||
Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated):
|
|
||||||
type(p_type),
|
|
||||||
hash(p_hash),
|
|
||||||
autogenerated(p_autogenerated) {}
|
|
||||||
|
|
||||||
void Core::Archive::AvatarInfo::deserialize(char* pointer, uint32_t size)
|
|
||||||
{
|
|
||||||
QByteArray data = QByteArray::fromRawData(pointer, size);
|
|
||||||
QDataStream in(&data, QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
in >> type >> hash >> autogenerated;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Archive::AvatarInfo::serialize(QByteArray* ba) const
|
|
||||||
{
|
|
||||||
QDataStream out(ba, QIODevice::WriteOnly);
|
|
||||||
|
|
||||||
out << type << hash << autogenerated;
|
|
||||||
}
|
|
197
core/archive.h
@ -1,197 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CORE_ARCHIVE_H
|
|
||||||
#define CORE_ARCHIVE_H
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QMimeDatabase>
|
|
||||||
#include <QMimeType>
|
|
||||||
|
|
||||||
#include "shared/message.h"
|
|
||||||
#include "shared/exception.h"
|
|
||||||
#include <lmdb.h>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
|
|
||||||
class Archive : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
class AvatarInfo;
|
|
||||||
|
|
||||||
Archive(const QString& jid, QObject* parent = 0);
|
|
||||||
~Archive();
|
|
||||||
|
|
||||||
void open(const QString& account);
|
|
||||||
void close();
|
|
||||||
|
|
||||||
bool addElement(const Shared::Message& message);
|
|
||||||
unsigned int addElements(const std::list<Shared::Message>& messages);
|
|
||||||
Shared::Message getElement(const QString& id) const;
|
|
||||||
bool hasElement(const QString& id) const;
|
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
|
||||||
Shared::Message oldest();
|
|
||||||
QString oldestId();
|
|
||||||
Shared::Message newest();
|
|
||||||
QString newestId();
|
|
||||||
void clear();
|
|
||||||
long unsigned int size() const;
|
|
||||||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
|
||||||
bool isFromTheBeginning();
|
|
||||||
void setFromTheBeginning(bool is);
|
|
||||||
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
|
|
||||||
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
|
||||||
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
|
|
||||||
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
|
|
||||||
QString idByStanzaId(const QString& stanzaId) const;
|
|
||||||
QString stanzaIdById(const QString& id) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const QString jid;
|
|
||||||
|
|
||||||
public:
|
|
||||||
class Directory:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Directory(const std::string& p_path):Exception(), path(p_path){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "Can't create directory for database at " + path;}
|
|
||||||
private:
|
|
||||||
std::string path;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Closed:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
|
|
||||||
private:
|
|
||||||
std::string operation;
|
|
||||||
std::string account;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NotFound:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
|
|
||||||
private:
|
|
||||||
std::string key;
|
|
||||||
std::string account;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Empty:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Empty(const std::string& acc):Exception(), account(acc){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";}
|
|
||||||
private:
|
|
||||||
std::string account;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Exist:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
|
|
||||||
private:
|
|
||||||
std::string account;
|
|
||||||
std::string key;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NoAvatar:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
|
|
||||||
if (resource.size() == 0) {
|
|
||||||
resource = "for himself";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
|
|
||||||
private:
|
|
||||||
std::string element;
|
|
||||||
std::string resource;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Unknown:
|
|
||||||
public Utils::Exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
|
|
||||||
private:
|
|
||||||
std::string account;
|
|
||||||
std::string msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class AvatarInfo {
|
|
||||||
public:
|
|
||||||
AvatarInfo();
|
|
||||||
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
|
|
||||||
|
|
||||||
void deserialize(char* pointer, uint32_t size);
|
|
||||||
void serialize(QByteArray* ba) const;
|
|
||||||
|
|
||||||
QString type;
|
|
||||||
QByteArray hash;
|
|
||||||
bool autogenerated;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool opened;
|
|
||||||
bool fromTheBeginning;
|
|
||||||
MDB_env* environment;
|
|
||||||
MDB_dbi main; //id to message
|
|
||||||
MDB_dbi order; //time to id
|
|
||||||
MDB_dbi stats;
|
|
||||||
MDB_dbi avatars;
|
|
||||||
MDB_dbi sid; //stanzaId to id
|
|
||||||
|
|
||||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
|
||||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
|
||||||
|
|
||||||
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
|
||||||
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
|
||||||
bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const;
|
|
||||||
void printOrder();
|
|
||||||
void printKeys();
|
|
||||||
bool dropAvatar(const std::string& resource);
|
|
||||||
Shared::Message getMessage(const std::string& id, MDB_txn* txn) const;
|
|
||||||
Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc);
|
|
||||||
Shared::Message edge(bool end);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CORE_ARCHIVE_H
|
|
18
core/components/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
set(SOURCE_FILES
|
||||||
|
networkaccess.cpp
|
||||||
|
clientcache.cpp
|
||||||
|
urlstorage.cpp
|
||||||
|
archive.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADER_FILES
|
||||||
|
networkaccess.h
|
||||||
|
clientcache.h
|
||||||
|
urlstorage.h
|
||||||
|
archive.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(squawk PRIVATE
|
||||||
|
${SOURCE_FILES}
|
||||||
|
${HEADER_FILES}
|
||||||
|
)
|
419
core/components/archive.cpp
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "archive.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* parent):
|
||||||
|
QObject(parent),
|
||||||
|
jid(p_jid),
|
||||||
|
account(account),
|
||||||
|
opened(false),
|
||||||
|
db(account + "/" + jid),
|
||||||
|
messages(db.addStorage<QString, Shared::Message>("messages")),
|
||||||
|
order(db.addStorage<uint64_t, QString>("order", true)),
|
||||||
|
stats(db.addStorage<QString, QVariant>("stats")),
|
||||||
|
avatars(db.addStorage<QString, AvatarInfo>("avatars")),
|
||||||
|
stanzaIdToId(db.addStorage<QString, QString>("stanzaIdToId")),
|
||||||
|
cursor(order->createCursor())
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::Archive::~Archive() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::open() {
|
||||||
|
db.open();
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
|
||||||
|
AvatarInfo info;
|
||||||
|
bool hasAvatar = false;
|
||||||
|
try {
|
||||||
|
avatars->getRecord(jid, info, txn);
|
||||||
|
hasAvatar = true;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
if (!hasAvatar)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile ava(db.getPath() + "/" + jid + "." + info.type);
|
||||||
|
if (ava.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
avatars->removeRecord(jid, txn);
|
||||||
|
txn.commit();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
qDebug() << e.what();
|
||||||
|
qDebug() << "error opening archive" << jid << "for account" << account
|
||||||
|
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::close() {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::addElement(const Shared::Message& message) {
|
||||||
|
QString id = message.getId();
|
||||||
|
qDebug() << "Adding message with id " << id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
messages->addRecord(id, message, txn);
|
||||||
|
order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn);
|
||||||
|
QString stanzaId = message.getStanzaId();
|
||||||
|
if (!stanzaId.isEmpty())
|
||||||
|
stanzaIdToId->addRecord(stanzaId, id, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
qDebug() << "Could not add message with id " + id;
|
||||||
|
qDebug() << e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::clear() {
|
||||||
|
db.drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message Core::Archive::getElement(const QString& id) const {
|
||||||
|
return messages->getRecord(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::hasElement(const QString& id) const {
|
||||||
|
return messages->checkRecord(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVariant>& data) {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
Shared::Message msg = messages->getRecord(id, txn);
|
||||||
|
|
||||||
|
bool hadStanzaId = !msg.getStanzaId().isEmpty();
|
||||||
|
QDateTime oTime = msg.getTime();
|
||||||
|
bool idChange = msg.change(data);
|
||||||
|
QString newId = msg.getId();
|
||||||
|
QDateTime nTime = msg.getTime();
|
||||||
|
|
||||||
|
bool orderChange = oTime != nTime;
|
||||||
|
if (idChange || orderChange) {
|
||||||
|
if (idChange)
|
||||||
|
messages->removeRecord(id, txn);
|
||||||
|
|
||||||
|
if (orderChange)
|
||||||
|
order->removeRecord(oTime.toMSecsSinceEpoch(), txn);
|
||||||
|
|
||||||
|
order->forceRecord(nTime.toMSecsSinceEpoch(), newId, txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sid = msg.getStanzaId();
|
||||||
|
if (!sid.isEmpty() && (idChange || !hadStanzaId))
|
||||||
|
stanzaIdToId->forceRecord(sid, newId, txn);
|
||||||
|
|
||||||
|
messages->forceRecord(newId, msg, txn);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message Core::Archive::newest() const {
|
||||||
|
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor.open(txn);
|
||||||
|
while (true) {
|
||||||
|
std::pair<uint64_t, QString> pair = cursor.prev();
|
||||||
|
Shared::Message msg = messages->getRecord(pair.second, txn);
|
||||||
|
if (msg.serverStored()) {
|
||||||
|
cursor.close();
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
cursor.close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Archive::newestId() const {
|
||||||
|
Shared::Message msg = newest();
|
||||||
|
return msg.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Archive::oldestId() const {
|
||||||
|
Shared::Message msg = oldest();
|
||||||
|
return msg.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message Core::Archive::oldest() const {
|
||||||
|
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor.open(txn);
|
||||||
|
while (true) {
|
||||||
|
std::pair<uint64_t, QString> pair = cursor.next();
|
||||||
|
Shared::Message msg = messages->getRecord(pair.second, txn);
|
||||||
|
if (msg.serverStored()) {
|
||||||
|
cursor.close();
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
cursor.close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messages) {
|
||||||
|
unsigned int success = 0;
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
for (const Shared::Message& message : messages) {
|
||||||
|
QString id = message.getId();
|
||||||
|
bool added = false;
|
||||||
|
try {
|
||||||
|
Core::Archive::messages->addRecord(id, message, txn);
|
||||||
|
added = true;
|
||||||
|
} catch (const LMDBAL::Exist& e) {}
|
||||||
|
|
||||||
|
if (!added)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn);
|
||||||
|
|
||||||
|
QString sid = message.getStanzaId();
|
||||||
|
if (!sid.isEmpty())
|
||||||
|
stanzaIdToId->addRecord(sid, id, txn);
|
||||||
|
|
||||||
|
++success;
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
long unsigned int Core::Archive::size() const {
|
||||||
|
return order->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::Message> Core::Archive::getBefore(unsigned int count, const QString& id) {
|
||||||
|
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
|
||||||
|
std::list<Shared::Message> res;
|
||||||
|
try {
|
||||||
|
cursor.open(txn);
|
||||||
|
if (!id.isEmpty()) {
|
||||||
|
Shared::Message reference = messages->getRecord(id, txn);
|
||||||
|
uint64_t stamp = reference.getTime().toMSecsSinceEpoch();
|
||||||
|
cursor.set(stamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < count; ++i) {
|
||||||
|
std::pair<uint64_t, QString> pair;
|
||||||
|
cursor.prev(pair.first, pair.second);
|
||||||
|
|
||||||
|
res.emplace_back();
|
||||||
|
Shared::Message& msg = res.back();
|
||||||
|
messages->getRecord(pair.second, msg, txn);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
cursor.close();
|
||||||
|
if (res.empty())
|
||||||
|
throw e;
|
||||||
|
else
|
||||||
|
return res;
|
||||||
|
} catch (...) {
|
||||||
|
cursor.close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::isFromTheBeginning() const {
|
||||||
|
try {
|
||||||
|
return stats->getRecord("fromTheBeginning").toBool();
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::setFromTheBeginning(bool is) {
|
||||||
|
stats->forceRecord("fromTheBeginning", is);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::EncryptionProtocol Core::Archive::encryption() const {
|
||||||
|
try {
|
||||||
|
return stats->getRecord("encryption").value<Shared::EncryptionProtocol>();
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
return Shared::EncryptionProtocol::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::setEncryption(Shared::EncryptionProtocol is) {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
Shared::EncryptionProtocol current = Shared::EncryptionProtocol::none;
|
||||||
|
try {
|
||||||
|
current = stats->getRecord("encryption", txn).value<Shared::EncryptionProtocol>();
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
if (is != current) {
|
||||||
|
stats->forceRecord("encryption", static_cast<uint8_t>(is), txn);
|
||||||
|
txn.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Archive::idByStanzaId(const QString& stanzaId) const {
|
||||||
|
return stanzaIdToId->getRecord(stanzaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Archive::stanzaIdById(const QString& id) const {
|
||||||
|
try {
|
||||||
|
Shared::Message msg = getElement(id);
|
||||||
|
return msg.getStanzaId();
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
AvatarInfo oldInfo;
|
||||||
|
bool haveAvatar = false;
|
||||||
|
QString res = resource.isEmpty() ? jid : resource;
|
||||||
|
try {
|
||||||
|
avatars->getRecord(res, oldInfo, txn);
|
||||||
|
haveAvatar = true;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
if (data.size() == 0) {
|
||||||
|
if (!haveAvatar)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
avatars->removeRecord(res, txn);
|
||||||
|
txn.commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString currentPath = db.getPath();
|
||||||
|
bool needToRemoveOld = false;
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
|
hash.addData(data);
|
||||||
|
QByteArray newHash(hash.result());
|
||||||
|
if (haveAvatar) {
|
||||||
|
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type);
|
||||||
|
if (oldAvatar.exists()) {
|
||||||
|
if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) {
|
||||||
|
needToRemoveOld = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QMimeDatabase mimedb;
|
||||||
|
QMimeType type = mimedb.mimeTypeForData(data);
|
||||||
|
QString ext = type.preferredSuffix();
|
||||||
|
QFile newAvatar(currentPath + "/" + res + "." + ext);
|
||||||
|
if (!newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
||||||
|
if (needToRemoveOld) {
|
||||||
|
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
|
||||||
|
oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
newAvatar.write(data);
|
||||||
|
newAvatar.close();
|
||||||
|
|
||||||
|
newInfo.type = ext;
|
||||||
|
newInfo.hash = newHash;
|
||||||
|
newInfo.autogenerated = generated;
|
||||||
|
try {
|
||||||
|
avatars->forceRecord(res, newInfo, txn);
|
||||||
|
txn.commit();
|
||||||
|
} catch (...) {
|
||||||
|
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
||||||
|
if (needToRemoveOld) {
|
||||||
|
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
|
||||||
|
oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needToRemoveOld) {
|
||||||
|
QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak");
|
||||||
|
oldAvatar.remove();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const {
|
||||||
|
try {
|
||||||
|
avatars->getRecord(resource.isEmpty() ? jid : resource, target);
|
||||||
|
return true;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const {
|
||||||
|
avatars->readAll(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const {
|
||||||
|
return avatars->getRecord(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo::AvatarInfo():
|
||||||
|
type(),
|
||||||
|
hash(),
|
||||||
|
autogenerated(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated):
|
||||||
|
type(p_type),
|
||||||
|
hash(p_hash),
|
||||||
|
autogenerated(p_autogenerated)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QDataStream & operator<<(QDataStream& out, const Core::Archive::AvatarInfo& info) {
|
||||||
|
out << info.type;
|
||||||
|
out << info.hash;
|
||||||
|
out << info.autogenerated;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator>>(QDataStream& in, Core::Archive::AvatarInfo& info) {
|
||||||
|
in >> info.type;
|
||||||
|
in >> info.hash;
|
||||||
|
in >> info.autogenerated;
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
101
core/components/archive.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QMimeDatabase>
|
||||||
|
#include <QMimeType>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
#include "shared/enums.h"
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/exception.h"
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <base.h>
|
||||||
|
#include <storage.h>
|
||||||
|
#include <cursor.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class Archive : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
class AvatarInfo;
|
||||||
|
|
||||||
|
Archive(const QString& account, const QString& jid, QObject* parent = 0);
|
||||||
|
~Archive();
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
bool addElement(const Shared::Message& message);
|
||||||
|
unsigned int addElements(const std::list<Shared::Message>& messages);
|
||||||
|
Shared::Message getElement(const QString& id) const;
|
||||||
|
bool hasElement(const QString& id) const;
|
||||||
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
Shared::Message oldest() const;
|
||||||
|
QString oldestId() const;
|
||||||
|
Shared::Message newest() const;
|
||||||
|
QString newestId() const;
|
||||||
|
void clear();
|
||||||
|
long unsigned int size() const;
|
||||||
|
std::list<Shared::Message> getBefore(unsigned int count, const QString& id);
|
||||||
|
bool isFromTheBeginning() const;
|
||||||
|
void setFromTheBeginning(bool is);
|
||||||
|
Shared::EncryptionProtocol encryption() const;
|
||||||
|
bool setEncryption(Shared::EncryptionProtocol value); //returns true if changed, false otherwise
|
||||||
|
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
|
||||||
|
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
||||||
|
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
|
||||||
|
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
|
||||||
|
QString idByStanzaId(const QString& stanzaId) const;
|
||||||
|
QString stanzaIdById(const QString& id) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const QString jid;
|
||||||
|
const QString account;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class AvatarInfo {
|
||||||
|
public:
|
||||||
|
AvatarInfo();
|
||||||
|
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
QByteArray hash;
|
||||||
|
bool autogenerated;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool opened;
|
||||||
|
LMDBAL::Base db;
|
||||||
|
LMDBAL::Storage<QString, Shared::Message>* messages;
|
||||||
|
LMDBAL::Storage<uint64_t, QString>* order;
|
||||||
|
LMDBAL::Storage<QString, QVariant>* stats;
|
||||||
|
LMDBAL::Storage<QString, AvatarInfo>* avatars;
|
||||||
|
LMDBAL::Storage<QString, QString>* stanzaIdToId;
|
||||||
|
mutable LMDBAL::Cursor<uint64_t, QString> cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info);
|
||||||
|
QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info);
|
77
core/components/clientcache.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "clientcache.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Core::ClientCache::ClientCache():
|
||||||
|
db("clients"),
|
||||||
|
cache(db.addCache<QString, Shared::ClientInfo>("info")),
|
||||||
|
requested(),
|
||||||
|
specific()
|
||||||
|
{
|
||||||
|
db.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::ClientCache::~ClientCache() {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::ClientCache::open() {
|
||||||
|
db.open();}
|
||||||
|
|
||||||
|
void Core::ClientCache::close() {
|
||||||
|
db.close();}
|
||||||
|
|
||||||
|
bool Core::ClientCache::checkClient(const Shared::ClientId& p_id) {
|
||||||
|
QString id = p_id.getId();
|
||||||
|
if (requested.count(id) == 0 && !cache->checkRecord(id)) {
|
||||||
|
requested.emplace(id, p_id);
|
||||||
|
emit requestClientInfo(id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::ClientCache::registerClientInfo (
|
||||||
|
const QString& sourceFullJid,
|
||||||
|
const QString& id,
|
||||||
|
const std::set<Shared::Identity>& identities,
|
||||||
|
const std::set<QString>& features)
|
||||||
|
{
|
||||||
|
std::map<QString, Shared::ClientInfo>::iterator itr = requested.find(id);
|
||||||
|
if (itr != requested.end()) {
|
||||||
|
Shared::ClientInfo& info = itr->second;
|
||||||
|
info.identities = identities;
|
||||||
|
info.extensions = features;
|
||||||
|
|
||||||
|
bool valid = info.valid();
|
||||||
|
if (valid) {
|
||||||
|
cache->addRecord(id, info);
|
||||||
|
} else {
|
||||||
|
info.specificPresence = sourceFullJid;
|
||||||
|
specific.insert(std::make_pair(sourceFullJid, info));
|
||||||
|
}
|
||||||
|
requested.erase(id);
|
||||||
|
return valid;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
63
core/components/clientcache.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <cache.h>
|
||||||
|
|
||||||
|
#include <shared/clientid.h>
|
||||||
|
#include <shared/clientinfo.h>
|
||||||
|
#include <shared/identity.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class ClientCache : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ClientCache();
|
||||||
|
~ClientCache();
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void requestClientInfo(const QString& id);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool checkClient(const Shared::ClientId& id);
|
||||||
|
bool registerClientInfo(
|
||||||
|
const QString& sourceFullJid,
|
||||||
|
const QString& id,
|
||||||
|
const std::set<Shared::Identity>& identities,
|
||||||
|
const std::set<QString>& features
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LMDBAL::Base db;
|
||||||
|
LMDBAL::Cache<QString, Shared::ClientInfo>* cache;
|
||||||
|
std::map<QString, Shared::ClientInfo> requested;
|
||||||
|
std::map<QString, Shared::ClientInfo> specific;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -16,7 +16,6 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
|
|
||||||
@ -28,17 +27,18 @@ Core::NetworkAccess::NetworkAccess(QObject* parent):
|
|||||||
manager(0),
|
manager(0),
|
||||||
storage("fileURLStorage"),
|
storage("fileURLStorage"),
|
||||||
downloads(),
|
downloads(),
|
||||||
uploads()
|
uploads(),
|
||||||
|
currentPath()
|
||||||
{
|
{
|
||||||
|
QSettings settings;
|
||||||
|
currentPath = settings.value("downloadsPath").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::NetworkAccess::~NetworkAccess()
|
Core::NetworkAccess::~NetworkAccess() {
|
||||||
{
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::downladFile(const QString& url)
|
void Core::NetworkAccess::downladFile(const QString& url) {
|
||||||
{
|
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
if (itr != downloads.end()) {
|
if (itr != downloads.end()) {
|
||||||
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
|
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
|
||||||
@ -47,27 +47,25 @@ void Core::NetworkAccess::downladFile(const QString& url)
|
|||||||
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
|
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
|
||||||
if (p.first.size() > 0) {
|
if (p.first.size() > 0) {
|
||||||
QFileInfo info(p.first);
|
QFileInfo info(p.first);
|
||||||
if (info.exists() && info.isFile()) {
|
if (info.exists() && info.isFile())
|
||||||
emit downloadFileComplete(p.second, p.first);
|
emit downloadFileComplete(p.second, p.first);
|
||||||
} else {
|
else
|
||||||
startDownload(p.second, url);
|
startDownload(p.second, url);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
startDownload(p.second, url);
|
startDownload(p.second, url);
|
||||||
}
|
}
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway";
|
qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway";
|
||||||
storage.addFile(url);
|
storage.addFile(url);
|
||||||
startDownload(std::list<Shared::MessageInfo>(), url);
|
startDownload(std::list<Shared::MessageInfo>(), url);
|
||||||
} catch (const Archive::Unknown& e) {
|
} catch (const LMDBAL::Unknown& e) {
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
qDebug() << "Error requesting file path:" << e.what();
|
||||||
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
|
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::start()
|
void Core::NetworkAccess::start() {
|
||||||
{
|
|
||||||
if (!running) {
|
if (!running) {
|
||||||
manager = new QNetworkAccessManager();
|
manager = new QNetworkAccessManager();
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
@ -78,8 +76,7 @@ void Core::NetworkAccess::start()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::stop()
|
void Core::NetworkAccess::stop() {
|
||||||
{
|
|
||||||
if (running) {
|
if (running) {
|
||||||
storage.close();
|
storage.close();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
@ -93,8 +90,7 @@ void Core::NetworkAccess::stop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
{
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
@ -112,8 +108,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) {
|
||||||
{
|
|
||||||
qDebug() << "DEBUG: DOWNLOAD ERROR";
|
qDebug() << "DEBUG: DOWNLOAD ERROR";
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
qDebug() << rpl->errorString();
|
qDebug() << rpl->errorString();
|
||||||
@ -131,12 +126,11 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
|
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors) {
|
||||||
{
|
|
||||||
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
|
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
|
||||||
for (const QSslError& err : errors) {
|
for (const QSslError& err : errors)
|
||||||
qDebug() << err.errorString();
|
qDebug() << err.errorString();
|
||||||
}
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
@ -151,9 +145,7 @@ void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) {
|
||||||
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|
||||||
{
|
|
||||||
QString errorText("");
|
QString errorText("");
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case QNetworkReply::NoError:
|
case QNetworkReply::NoError:
|
||||||
@ -277,8 +269,7 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadFinished()
|
void Core::NetworkAccess::onDownloadFinished() {
|
||||||
{
|
|
||||||
qDebug() << "DEBUG: DOWNLOAD FINISHED";
|
qDebug() << "DEBUG: DOWNLOAD FINISHED";
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
@ -293,16 +284,16 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
QStringList hops = url.split("/");
|
QStringList hops = url.split("/");
|
||||||
QString fileName = hops.back();
|
QString fileName = hops.back();
|
||||||
QString jid;
|
QString jid;
|
||||||
if (dwn->messages.size() > 0) {
|
if (dwn->messages.size() > 0)
|
||||||
jid = dwn->messages.front().jid;
|
jid = dwn->messages.front().jid;
|
||||||
} else {
|
else
|
||||||
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
|
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
|
||||||
}
|
|
||||||
QString path = prepareDirectory(jid);
|
QString path = prepareDirectory(jid);
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0) {
|
||||||
path = checkFileName(fileName, path);
|
path = checkFileName(fileName, path);
|
||||||
|
|
||||||
QFile file(path);
|
QFile file(Shared::resolvePath(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();
|
||||||
@ -316,11 +307,10 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
err = "Couldn't prepare a directory for file";
|
err = "Couldn't prepare a directory for file";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0)
|
||||||
emit downloadFileComplete(dwn->messages, path);
|
emit downloadFileComplete(dwn->messages, path);
|
||||||
} else {
|
else
|
||||||
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
|
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dwn->reply->deleteLater();
|
dwn->reply->deleteLater();
|
||||||
@ -329,8 +319,7 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url)
|
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url) {
|
||||||
{
|
|
||||||
Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0});
|
Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0});
|
||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
dwn->reply = manager->get(req);
|
dwn->reply = manager->get(req);
|
||||||
@ -346,8 +335,7 @@ void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& ms
|
|||||||
emit loadFileProgress(dwn->messages, 0, false);
|
emit loadFileProgress(dwn->messages, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) {
|
||||||
{
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
@ -365,8 +353,7 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadFinished()
|
void Core::NetworkAccess::onUploadFinished() {
|
||||||
{
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
@ -377,8 +364,29 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
if (upl->success) {
|
if (upl->success) {
|
||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
|
|
||||||
|
// Copy file to Download folder if it is a temp file. See Conversation::onImagePasted.
|
||||||
|
if (upl->path.startsWith(QDir::tempPath() + QDir::separator() + QStringLiteral("squawk_img_attach_")) && upl->path.endsWith(".png")) {
|
||||||
|
QString err = "";
|
||||||
|
QString downloadDirPath = prepareDirectory(upl->messages.front().jid);
|
||||||
|
if (downloadDirPath.size() > 0) {
|
||||||
|
QString newPath = downloadDirPath + QDir::separator() + upl->path.mid(QDir::tempPath().length() + 1);
|
||||||
|
|
||||||
|
// Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder
|
||||||
|
bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath));
|
||||||
|
if (copyResult)
|
||||||
|
upl->path = newPath; // Change storage
|
||||||
|
else
|
||||||
|
err = "copying to " + newPath + " failed";
|
||||||
|
} else {
|
||||||
|
err = "Couldn't prepare a directory for file";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.size() != 0)
|
||||||
|
qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err;
|
||||||
|
}
|
||||||
|
|
||||||
storage.addFile(upl->messages, upl->url, upl->path);
|
storage.addFile(upl->messages, upl->url, upl->path);
|
||||||
emit uploadFileComplete(upl->messages, upl->url);
|
emit uploadFileComplete(upl->messages, upl->url, upl->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
upl->reply->deleteLater();
|
upl->reply->deleteLater();
|
||||||
@ -389,8 +397,7 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
{
|
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
@ -408,14 +415,13 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) {
|
||||||
{
|
QString p = Shared::squawkifyPath(path);
|
||||||
QString p;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
p = storage.getUrl(path);
|
p = storage.getUrl(p);
|
||||||
} catch (const Archive::NotFound& err) {
|
} catch (const LMDBAL::NotFound& err) {
|
||||||
|
p = "";
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@ -423,8 +429,13 @@ QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
void Core::NetworkAccess::uploadFile(
|
||||||
{
|
const Shared::MessageInfo& info,
|
||||||
|
const QString& path,
|
||||||
|
const QUrl& put,
|
||||||
|
const QUrl& get,
|
||||||
|
const QMap<QString, QString> headers
|
||||||
|
) {
|
||||||
QFile* file = new QFile(path);
|
QFile* file = new QFile(path);
|
||||||
Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file});
|
Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file});
|
||||||
QNetworkRequest req(put);
|
QNetworkRequest req(put);
|
||||||
@ -451,22 +462,18 @@ void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QStr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) {
|
||||||
{
|
|
||||||
storage.addFile(url, account, jid, id);
|
storage.addFile(url, account, jid, id);
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
if (itr != downloads.end()) {
|
if (itr != downloads.end())
|
||||||
itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
|
void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) {
|
||||||
{
|
|
||||||
storage.addFile(url, path, account, jid, id);
|
storage.addFile(url, path, account, jid, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path)
|
bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) {
|
||||||
{
|
|
||||||
for (const std::pair<const QString, Transfer*>& pair : uploads) {
|
for (const std::pair<const QString, Transfer*>& pair : uploads) {
|
||||||
Transfer* info = pair.second;
|
Transfer* info = pair.second;
|
||||||
if (pair.second->path == path) {
|
if (pair.second->path == path) {
|
||||||
@ -488,53 +495,68 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
QString Core::NetworkAccess::prepareDirectory(const QString& jid) {
|
||||||
{
|
QString path = currentPath;
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
QString addition;
|
||||||
path += "/" + QApplication::applicationName();
|
|
||||||
if (jid.size() > 0) {
|
if (jid.size() > 0) {
|
||||||
path += "/" + jid;
|
addition = jid;
|
||||||
|
path += QDir::separator() + jid;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir location(path);
|
QDir location(path);
|
||||||
|
|
||||||
if (!location.exists()) {
|
if (!location.exists()) {
|
||||||
bool res = location.mkpath(path);
|
bool res = location.mkpath(path);
|
||||||
if (!res) {
|
if (!res)
|
||||||
return "";
|
return "";
|
||||||
} else {
|
else
|
||||||
return path;
|
return "squawk://" + addition;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return path;
|
return "squawk://" + addition;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) {
|
||||||
{
|
|
||||||
QStringList parts = name.split(".");
|
QStringList parts = name.split(".");
|
||||||
QString suffix("");
|
QString suffix("");
|
||||||
QStringList::const_iterator sItr = parts.begin();
|
QStringList::const_iterator sItr = parts.begin();
|
||||||
QString realName = *sItr;
|
QString realName = *sItr;
|
||||||
++sItr;
|
++sItr;
|
||||||
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr)
|
||||||
suffix += "." + (*sItr);
|
suffix += "." + (*sItr);
|
||||||
}
|
|
||||||
QString postfix("");
|
QString postfix("");
|
||||||
QFileInfo proposedName(path + "/" + realName + suffix);
|
QString resolvedPath = Shared::resolvePath(path);
|
||||||
|
QString count("");
|
||||||
|
QFileInfo proposedName(resolvedPath + QDir::separator() + realName + count + suffix);
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (proposedName.exists()) {
|
while (proposedName.exists()) {
|
||||||
QString count = QString("(") + std::to_string(++counter).c_str() + ")";
|
count = QString("(") + std::to_string(++counter).c_str() + ")";
|
||||||
proposedName = QFileInfo(path + "/" + realName + count + suffix);
|
proposedName = QFileInfo(resolvedPath + QDir::separator() + realName + count + suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
return proposedName.absoluteFilePath();
|
return path + QDir::separator() + realName + count + suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) {
|
||||||
{
|
|
||||||
return storage.addMessageAndCheckForPath(url, account, jid, id);
|
return storage.addMessageAndCheckForPath(url, account, jid, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path)
|
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& path) {
|
||||||
{
|
|
||||||
return storage.deletedFile(path);
|
return storage.deletedFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) {
|
||||||
|
QDir dir(currentPath);
|
||||||
|
bool success = true;
|
||||||
|
qDebug() << "moving" << currentPath << "to" << newPath;
|
||||||
|
for (QFileInfo fileInfo : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) {
|
||||||
|
QString fileName = fileInfo.fileName();
|
||||||
|
success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
qDebug() << "couldn't move downloads directory, most probably downloads will be broken";
|
||||||
|
|
||||||
|
currentPath = newPath;
|
||||||
|
}
|
@ -16,8 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_NETWORKACCESS_H
|
#pragma once
|
||||||
#define CORE_NETWORKACCESS_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
@ -26,20 +25,17 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <shared/pathcheck.h>
|
||||||
#include "urlstorage.h"
|
#include "urlstorage.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
|
|
||||||
//TODO Need to describe how to get rid of records when file is no longer reachable;
|
//TODO Need to describe how to get rid of records when file is no longer reachable;
|
||||||
class NetworkAccess : public QObject
|
class NetworkAccess : public QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
struct Transfer;
|
struct Transfer;
|
||||||
public:
|
public:
|
||||||
@ -58,13 +54,14 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
||||||
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
||||||
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void downladFile(const QString& url);
|
void downladFile(const QString& url);
|
||||||
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void moveFilesDirectory(const QString& newPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
@ -87,6 +84,7 @@ private:
|
|||||||
UrlStorage storage;
|
UrlStorage storage;
|
||||||
std::map<QString, Transfer*> downloads;
|
std::map<QString, Transfer*> downloads;
|
||||||
std::map<QString, Transfer*> uploads;
|
std::map<QString, Transfer*> uploads;
|
||||||
|
QString currentPath;
|
||||||
|
|
||||||
struct Transfer {
|
struct Transfer {
|
||||||
std::list<Shared::MessageInfo> messages;
|
std::list<Shared::MessageInfo> messages;
|
||||||
@ -100,5 +98,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_NETWORKACCESS_H
|
|
252
core/components/urlstorage.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "urlstorage.h"
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlStorage(const QString& p_name):
|
||||||
|
base(p_name),
|
||||||
|
urlToInfo(base.addStorage<QString, UrlInfo>("urlToInfo")),
|
||||||
|
pathToUrl(base.addStorage<QString, QString>("pathToUrl"))
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::UrlStorage::~UrlStorage() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::open() {
|
||||||
|
base.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::close() {
|
||||||
|
base.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) {
|
||||||
|
LMDBAL::WriteTransaction txn = base.beginTransaction();
|
||||||
|
writeInfo(key, info, txn, overwrite);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite) {
|
||||||
|
if (overwrite)
|
||||||
|
urlToInfo->forceRecord(key, info, txn);
|
||||||
|
else
|
||||||
|
urlToInfo->addRecord(key, info, txn);
|
||||||
|
|
||||||
|
if (info.hasPath())
|
||||||
|
pathToUrl->forceRecord(info.getPath(), key, txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url) {
|
||||||
|
addToInfo(url, "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& path) {
|
||||||
|
addToInfo(url, "", "", "", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) {
|
||||||
|
addToInfo(url, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) {
|
||||||
|
addToInfo(url, account, jid, id, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path) {
|
||||||
|
UrlInfo info (path, msgs);
|
||||||
|
writeInfo(url, info, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id){
|
||||||
|
return addToInfo(url, account, jid, id).getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(
|
||||||
|
const QString& url,
|
||||||
|
const QString& account,
|
||||||
|
const QString& jid,
|
||||||
|
const QString& id,
|
||||||
|
const QString& path
|
||||||
|
) {
|
||||||
|
UrlInfo info;
|
||||||
|
LMDBAL::WriteTransaction txn = base.beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
urlToInfo->getRecord(url, info, txn);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
bool pathChange = false;
|
||||||
|
bool listChange = false;
|
||||||
|
if (path != "-s") {
|
||||||
|
if (info.getPath() != path) {
|
||||||
|
info.setPath(path);
|
||||||
|
pathChange = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.size() > 0 && jid.size() > 0 && id.size() > 0)
|
||||||
|
listChange = info.addMessage(account, jid, id);
|
||||||
|
|
||||||
|
if (pathChange || listChange) {
|
||||||
|
writeInfo(url, info, txn, true);
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path) {
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
LMDBAL::WriteTransaction txn = base.beginTransaction();
|
||||||
|
UrlInfo info;
|
||||||
|
|
||||||
|
try {
|
||||||
|
urlToInfo->getRecord(url, info, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
info.setPath(path);
|
||||||
|
writeInfo(url, info, txn, true);
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url) {
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
LMDBAL::WriteTransaction txn = base.beginTransaction();
|
||||||
|
UrlInfo info;
|
||||||
|
|
||||||
|
urlToInfo->getRecord(url, info, txn);
|
||||||
|
urlToInfo->removeRecord(url, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
|
||||||
|
if (info.hasPath())
|
||||||
|
pathToUrl->removeRecord(info.getPath(), txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path) {
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
LMDBAL::WriteTransaction txn = base.beginTransaction();
|
||||||
|
|
||||||
|
QString url = pathToUrl->getRecord(path, txn);
|
||||||
|
pathToUrl->removeRecord(path, txn);
|
||||||
|
|
||||||
|
UrlInfo info = urlToInfo->getRecord(url, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
info.setPath(QString());
|
||||||
|
urlToInfo->changeRecord(url, info, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::UrlStorage::getUrl(const QString& path) {
|
||||||
|
return pathToUrl->getRecord(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<QString, std::list<Shared::MessageInfo>> Core::UrlStorage::getPath(const QString& url) {
|
||||||
|
UrlInfo info = urlToInfo->getRecord(url);
|
||||||
|
std::list<Shared::MessageInfo> container;
|
||||||
|
info.getMessages(container);
|
||||||
|
return std::make_pair(info.getPath(), container);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo():
|
||||||
|
localPath(),
|
||||||
|
messages() {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path):
|
||||||
|
localPath(path),
|
||||||
|
messages() {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs):
|
||||||
|
localPath(path),
|
||||||
|
messages(msgs) {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::~UrlInfo() {}
|
||||||
|
|
||||||
|
bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) {
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
if (info.account == acc && info.jid == jid && info.messageId == id)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
messages.emplace_back(acc, jid, id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const {
|
||||||
|
data << localPath;
|
||||||
|
std::list<Shared::MessageInfo>::size_type size = messages.size();
|
||||||
|
data << quint32(size);
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
data << info.account;
|
||||||
|
data << info.jid;
|
||||||
|
data << info.messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator << (QDataStream& in, const Core::UrlStorage::UrlInfo& info) {
|
||||||
|
info.serialize(in);
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator >> (QDataStream& out, Core::UrlStorage::UrlInfo& info) {
|
||||||
|
info.deserialize(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) {
|
||||||
|
data >> localPath;
|
||||||
|
quint32 size;
|
||||||
|
data >> size;
|
||||||
|
for (quint32 i = 0; i < size; ++i) {
|
||||||
|
messages.emplace_back();
|
||||||
|
Shared::MessageInfo& info = messages.back();
|
||||||
|
data >> info.account;
|
||||||
|
data >> info.jid;
|
||||||
|
data >> info.messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::getMessages(std::list<Shared::MessageInfo>& container) const {
|
||||||
|
for (const Shared::MessageInfo& info : messages)
|
||||||
|
container.emplace_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::UrlStorage::UrlInfo::getPath() const {
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::UrlStorage::UrlInfo::hasPath() const {
|
||||||
|
return localPath.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::setPath(const QString& path) {
|
||||||
|
localPath = path;
|
||||||
|
}
|
@ -16,25 +16,23 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_URLSTORAGE_H
|
#pragma once
|
||||||
#define CORE_URLSTORAGE_H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <lmdb.h>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "archive.h"
|
#include <storage.h>
|
||||||
|
|
||||||
#include <shared/messageinfo.h>
|
#include <shared/messageinfo.h>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
class UrlStorage {
|
||||||
* @todo write docs
|
public:
|
||||||
*/
|
|
||||||
class UrlStorage
|
|
||||||
{
|
|
||||||
class UrlInfo;
|
class UrlInfo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UrlStorage(const QString& name);
|
UrlStorage(const QString& name);
|
||||||
~UrlStorage();
|
~UrlStorage();
|
||||||
@ -55,20 +53,16 @@ public:
|
|||||||
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
|
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
LMDBAL::Base base;
|
||||||
bool opened;
|
LMDBAL::Storage<QString, UrlInfo>* urlToInfo;
|
||||||
MDB_env* environment;
|
LMDBAL::Storage<QString, QString>* pathToUrl;
|
||||||
MDB_dbi base;
|
|
||||||
MDB_dbi map;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
|
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
|
||||||
void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false);
|
void writeInfo(const QString& key, const UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite = false);
|
||||||
void readInfo(const QString& key, UrlInfo& info);
|
|
||||||
void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn);
|
|
||||||
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
|
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
|
||||||
|
|
||||||
private:
|
public:
|
||||||
class UrlInfo {
|
class UrlInfo {
|
||||||
public:
|
public:
|
||||||
UrlInfo(const QString& path);
|
UrlInfo(const QString& path);
|
||||||
@ -96,4 +90,5 @@ private:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_URLSTORAGE_H
|
QDataStream& operator >> (QDataStream &in, Core::UrlStorage::UrlInfo& info);
|
||||||
|
QDataStream& operator << (QDataStream &out, const Core::UrlStorage::UrlInfo& info);
|
@ -42,57 +42,48 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||||||
connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError);
|
connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError);
|
||||||
|
|
||||||
room->setNickName(nick);
|
room->setNickName(nick);
|
||||||
if (autoJoin) {
|
if (autoJoin)
|
||||||
room->join();
|
room->join();
|
||||||
}
|
|
||||||
|
|
||||||
archive->readAllResourcesAvatars(exParticipants);
|
archive->readAllResourcesAvatars(exParticipants);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Conference::~Conference()
|
Core::Conference::~Conference(){
|
||||||
{
|
if (joined)
|
||||||
if (joined) {
|
|
||||||
room->leave();
|
room->leave();
|
||||||
}
|
|
||||||
room->deleteLater();
|
room->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::Conference::getNick() const
|
QString Core::Conference::getNick() const {
|
||||||
{
|
|
||||||
return nick;
|
return nick;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Conference::getAutoJoin()
|
bool Core::Conference::getAutoJoin() const {
|
||||||
{
|
|
||||||
return autoJoin;
|
return autoJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Conference::getJoined() const
|
bool Core::Conference::getJoined() const {
|
||||||
{
|
|
||||||
return joined;
|
return joined;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::setJoined(bool p_joined)
|
void Core::Conference::setJoined(bool p_joined) {
|
||||||
{
|
|
||||||
if (joined != p_joined) {
|
if (joined != p_joined) {
|
||||||
if (p_joined) {
|
if (p_joined)
|
||||||
room->join();
|
room->join();
|
||||||
} else {
|
else
|
||||||
room->leave();
|
room->leave();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::setAutoJoin(bool p_autoJoin)
|
void Core::Conference::setAutoJoin(bool p_autoJoin) {
|
||||||
{
|
|
||||||
if (autoJoin != p_autoJoin) {
|
if (autoJoin != p_autoJoin) {
|
||||||
autoJoin = p_autoJoin;
|
autoJoin = p_autoJoin;
|
||||||
emit autoJoinChanged(autoJoin);
|
emit autoJoinChanged(autoJoin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::setNick(const QString& p_nick)
|
void Core::Conference::setNick(const QString& p_nick) {
|
||||||
{
|
|
||||||
if (nick != p_nick) {
|
if (nick != p_nick) {
|
||||||
if (joined) {
|
if (joined) {
|
||||||
room->setNickName(p_nick);
|
room->setNickName(p_nick);
|
||||||
@ -103,105 +94,99 @@ void Core::Conference::setNick(const QString& p_nick)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomJoined()
|
void Core::Conference::onRoomJoined() {
|
||||||
{
|
|
||||||
joined = true;
|
joined = true;
|
||||||
emit joinedChanged(joined);
|
emit joinedChanged(joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomLeft()
|
void Core::Conference::onRoomLeft() {
|
||||||
{
|
|
||||||
joined = false;
|
joined = false;
|
||||||
emit joinedChanged(joined);
|
emit joinedChanged(joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomNameChanged(const QString& p_name)
|
void Core::Conference::onRoomNameChanged(const QString& p_name) {
|
||||||
{
|
|
||||||
setName(p_name);
|
setName(p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomNickNameChanged(const QString& p_nick)
|
void Core::Conference::onRoomNickNameChanged(const QString& p_nick) {
|
||||||
{
|
|
||||||
if (p_nick != nick) {
|
if (p_nick != nick) {
|
||||||
nick = p_nick;
|
nick = p_nick;
|
||||||
emit nickChanged(nick);
|
emit nickChanged(nick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomError(const QXmppStanza::Error& err)
|
void Core::Conference::onRoomError(const QXmppStanza::Error& err) {
|
||||||
{
|
|
||||||
qDebug() << "MUC" << jid << "error:" << err.text();
|
qDebug() << "MUC" << jid << "error:" << err.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
void Core::Conference::onRoomParticipantAdded(const QString& p_name) {
|
||||||
{
|
|
||||||
QStringList comps = p_name.split("/");
|
QStringList comps = p_name.split("/");
|
||||||
QString resource = comps.back();
|
QString resource = comps.back();
|
||||||
QXmppPresence pres = room->participantPresence(p_name);
|
QXmppPresence pres = room->participantPresence(p_name);
|
||||||
QXmppMucItem mi = pres.mucItem();
|
QXmppMucItem mi = pres.mucItem();
|
||||||
if (resource == jid) {
|
if (resource == jid)
|
||||||
resource = "";
|
resource = "";
|
||||||
}
|
|
||||||
|
|
||||||
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
|
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
|
||||||
bool hasAvatar = itr != exParticipants.end();
|
bool hasAvatar = itr != exParticipants.end();
|
||||||
|
|
||||||
if (resource.size() > 0) {
|
if (resource.size() > 0) {
|
||||||
QDateTime lastInteraction = pres.lastUserInteraction();
|
QDateTime lastInteraction = pres.lastUserInteraction();
|
||||||
if (!lastInteraction.isValid()) {
|
if (!lastInteraction.isValid())
|
||||||
lastInteraction = QDateTime::currentDateTimeUtc();
|
lastInteraction = QDateTime::currentDateTimeUtc();
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, QVariant> cData = {
|
QMap<QString, QVariant> cData = {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
{"availability", pres.availableStatusType()},
|
{"availability", pres.availableStatusType()},
|
||||||
{"status", pres.statusText()},
|
{"status", pres.statusText()},
|
||||||
{"affiliation", mi.affiliation()},
|
{"affiliation", mi.affiliation()},
|
||||||
{"role", mi.role()}
|
{"role", mi.role()},
|
||||||
};
|
{"client", QVariant::fromValue(
|
||||||
|
Shared::ClientId(
|
||||||
if (hasAvatar) {
|
pres.capabilityNode(),
|
||||||
if (itr->second.autogenerated) {
|
pres.capabilityVer().toBase64(),
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
pres.capabilityHash())
|
||||||
} else {
|
)
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
|
||||||
}
|
}
|
||||||
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
|
};
|
||||||
} else {
|
careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name);
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
|
||||||
cData.insert("avatarPath", "");
|
|
||||||
requestVCard(p_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit addParticipant(resource, cData);
|
emit addParticipant(resource, cData);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!hasAvatar) // because this way vCard is already requested, no need to handle possible avatar update
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handlePossibleAvatarUpdate(pres, resource, hasAvatar, itr->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Conference::handlePossibleAvatarUpdate (
|
||||||
|
const QXmppPresence& pres,
|
||||||
|
const QString& resource,
|
||||||
|
bool hasAvatar,
|
||||||
|
const Archive::AvatarInfo& info
|
||||||
|
) {
|
||||||
switch (pres.vCardUpdateType()) {
|
switch (pres.vCardUpdateType()) {
|
||||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||||
break;
|
break;
|
||||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||||
break;
|
break;
|
||||||
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
|
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||||
if (!hasAvatar || !itr->second.autogenerated) {
|
if (!hasAvatar || !info.autogenerated)
|
||||||
setAutoGeneratedAvatar(resource);
|
setAutoGeneratedAvatar(resource);
|
||||||
}
|
|
||||||
}
|
break;
|
||||||
break;
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||||
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) {
|
if (info.autogenerated || info.hash != pres.photoHash())
|
||||||
emit requestVCard(p_name);
|
emit requestVCard(pres.from());
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
emit requestVCard(p_name);
|
emit requestVCard(pres.from());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomParticipantChanged(const QString& p_name)
|
void Core::Conference::onRoomParticipantChanged(const QString& p_name) {
|
||||||
{
|
|
||||||
QStringList comps = p_name.split("/");
|
QStringList comps = p_name.split("/");
|
||||||
QString resource = comps.back();
|
QString resource = comps.back();
|
||||||
QXmppPresence pres = room->participantPresence(p_name);
|
QXmppPresence pres = room->participantPresence(p_name);
|
||||||
@ -209,22 +194,27 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name)
|
|||||||
handlePresence(pres);
|
handlePresence(pres);
|
||||||
if (resource != jid) {
|
if (resource != jid) {
|
||||||
QDateTime lastInteraction = pres.lastUserInteraction();
|
QDateTime lastInteraction = pres.lastUserInteraction();
|
||||||
if (!lastInteraction.isValid()) {
|
if (!lastInteraction.isValid())
|
||||||
lastInteraction = QDateTime::currentDateTimeUtc();
|
lastInteraction = QDateTime::currentDateTimeUtc();
|
||||||
}
|
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
{"availability", pres.availableStatusType()},
|
{"availability", pres.availableStatusType()},
|
||||||
{"status", pres.statusText()},
|
{"status", pres.statusText()},
|
||||||
{"affiliation", mi.affiliation()},
|
{"affiliation", mi.affiliation()},
|
||||||
{"role", mi.role()}
|
{"role", mi.role()},
|
||||||
|
{"client", QVariant::fromValue(
|
||||||
|
Shared::ClientId(
|
||||||
|
pres.capabilityNode(),
|
||||||
|
pres.capabilityVer().toBase64(),
|
||||||
|
pres.capabilityHash())
|
||||||
|
)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomParticipantRemoved(const QString& p_name)
|
void Core::Conference::onRoomParticipantRemoved(const QString& p_name) {
|
||||||
{
|
|
||||||
QStringList comps = p_name.split("/");
|
QStringList comps = p_name.split("/");
|
||||||
QString resource = comps.back();
|
QString resource = comps.back();
|
||||||
if (resource == jid) {
|
if (resource == jid) {
|
||||||
@ -234,69 +224,40 @@ void Core::Conference::onRoomParticipantRemoved(const QString& p_name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::Conference::getSubject() const
|
QString Core::Conference::getSubject() const {
|
||||||
{
|
if (joined)
|
||||||
if (joined) {
|
|
||||||
return room->subject();
|
return room->subject();
|
||||||
} else {
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::onRoomSubjectChanged(const QString& p_name)
|
void Core::Conference::onRoomSubjectChanged(const QString& p_name) {
|
||||||
{
|
|
||||||
emit subjectChanged(p_name);
|
emit subjectChanged(p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Conference::handlePresence(const QXmppPresence& pres)
|
void Core::Conference::handlePresence(const QXmppPresence& pres) {
|
||||||
{
|
|
||||||
QString id = pres.from();
|
QString id = pres.from();
|
||||||
QStringList comps = id.split("/");
|
QStringList comps = id.split("/");
|
||||||
QString jid = comps.front();
|
QString jid = comps.front();
|
||||||
QString resource("");
|
QString resource("");
|
||||||
if (comps.size() > 1) {
|
if (comps.size() > 1)
|
||||||
resource = comps.back();
|
resource = comps.back();
|
||||||
}
|
|
||||||
|
|
||||||
switch (pres.vCardUpdateType()) {
|
Archive::AvatarInfo info;
|
||||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
bool hasAvatar = readAvatarInfo(info, resource);
|
||||||
break;
|
handlePossibleAvatarUpdate(pres, resource, hasAvatar, info);
|
||||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
|
|
||||||
Archive::AvatarInfo info;
|
|
||||||
bool hasAvatar = readAvatarInfo(info, resource);
|
|
||||||
if (!hasAvatar || !info.autogenerated) {
|
|
||||||
setAutoGeneratedAvatar(resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
|
||||||
Archive::AvatarInfo info;
|
|
||||||
bool hasAvatar = readAvatarInfo(info, resource);
|
|
||||||
if (hasAvatar) {
|
|
||||||
if (info.autogenerated || info.hash != pres.photoHash()) {
|
|
||||||
emit requestVCard(id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit requestVCard(id);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
|
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) {
|
||||||
{
|
|
||||||
Archive::AvatarInfo newInfo;
|
Archive::AvatarInfo newInfo;
|
||||||
bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource);
|
bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource);
|
||||||
if (result && resource.size() != 0) {
|
if (result && resource.size() != 0) {
|
||||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||||
if (itr == exParticipants.end()) {
|
if (itr == exParticipants.end())
|
||||||
exParticipants.insert(std::make_pair(resource, newInfo));
|
exParticipants.insert(std::make_pair(resource, newInfo));
|
||||||
} else {
|
else
|
||||||
itr->second = newInfo;
|
itr->second = newInfo;
|
||||||
}
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
|
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
|
||||||
@ -306,17 +267,15 @@ bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
|
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) {
|
||||||
{
|
|
||||||
bool result = RosterItem::setAvatar(data, info, resource);
|
bool result = RosterItem::setAvatar(data, info, resource);
|
||||||
if (result && resource.size() != 0) {
|
if (result && resource.size() != 0) {
|
||||||
if (data.size() > 0) {
|
if (data.size() > 0) {
|
||||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||||
if (itr == exParticipants.end()) {
|
if (itr == exParticipants.end())
|
||||||
exParticipants.insert(std::make_pair(resource, info));
|
exParticipants.insert(std::make_pair(resource, info));
|
||||||
} else {
|
else
|
||||||
itr->second = info;
|
itr->second = info;
|
||||||
}
|
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
@ -324,9 +283,8 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||||
if (itr != exParticipants.end()) {
|
if (itr != exParticipants.end())
|
||||||
exParticipants.erase(itr);
|
exParticipants.erase(itr);
|
||||||
}
|
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
|
||||||
@ -339,25 +297,31 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource)
|
void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) {
|
||||||
{
|
RosterItem::handleResponseVCard(card, resource, out);
|
||||||
Shared::VCard result = RosterItem::handleResponseVCard(card, resource);
|
if (resource.size() > 0)
|
||||||
|
|
||||||
if (resource.size() > 0) {
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(result.getAvatarType())},
|
{"avatarState", static_cast<uint>(out.getAvatarType())},
|
||||||
{"avatarPath", result.getAvatarPath()}
|
{"avatarPath", out.getAvatarPath()}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
|
QMap<QString, QVariant> Core::Conference::getAllAvatars() const {
|
||||||
{
|
|
||||||
QMap<QString, QVariant> result;
|
QMap<QString, QVariant> result;
|
||||||
for (const std::pair<QString, Archive::AvatarInfo>& pair : exParticipants) {
|
for (const std::pair<const QString, Archive::AvatarInfo>& pair : exParticipants)
|
||||||
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> Core::Conference::getInfo() const {
|
||||||
|
QMap<QString, QVariant> data = RosterItem::getInfo();
|
||||||
|
|
||||||
|
data.insert("autoJoin", getAutoJoin());
|
||||||
|
data.insert("joined", getJoined());
|
||||||
|
data.insert("nick", getNick());
|
||||||
|
data.insert("avatars", getAllAvatars());
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_CONFERENCE_H
|
#pragma once
|
||||||
#define CORE_CONFERENCE_H
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
@ -26,16 +25,11 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "rosteritem.h"
|
#include "rosteritem.h"
|
||||||
#include "shared/global.h"
|
#include <shared/global.h>
|
||||||
|
#include <shared/clientid.h>
|
||||||
|
|
||||||
namespace Core
|
namespace Core {
|
||||||
{
|
class Conference : public RosterItem {
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
class Conference : public RosterItem
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room);
|
Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room);
|
||||||
@ -48,12 +42,13 @@ public:
|
|||||||
bool getJoined() const;
|
bool getJoined() const;
|
||||||
void setJoined(bool p_joined);
|
void setJoined(bool p_joined);
|
||||||
|
|
||||||
bool getAutoJoin();
|
bool getAutoJoin() const;
|
||||||
void setAutoJoin(bool p_autoJoin);
|
void setAutoJoin(bool p_autoJoin);
|
||||||
void handlePresence(const QXmppPresence & pres) override;
|
void handlePresence(const QXmppPresence & pres) override;
|
||||||
bool setAutoGeneratedAvatar(const QString& resource = "") override;
|
bool setAutoGeneratedAvatar(const QString& resource = "") override;
|
||||||
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
|
void handleResponseVCard(const QXmppVCardIq & card, const QString &resource, Shared::VCard& out) override;
|
||||||
QMap<QString, QVariant> getAllAvatars() const;
|
QMap<QString, QVariant> getAllAvatars() const;
|
||||||
|
QMap<QString, QVariant> getInfo() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nickChanged(const QString& nick);
|
void nickChanged(const QString& nick);
|
||||||
@ -67,6 +62,14 @@ signals:
|
|||||||
protected:
|
protected:
|
||||||
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
|
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handlePossibleAvatarUpdate(
|
||||||
|
const QXmppPresence& pres,
|
||||||
|
const QString& resource,
|
||||||
|
bool hasAvatar,
|
||||||
|
const Archive::AvatarInfo& info
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString nick;
|
QString nick;
|
||||||
QXmppMucRoom* room;
|
QXmppMucRoom* room;
|
||||||
@ -89,5 +92,3 @@ private slots:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_CONFERENCE_H
|
|
||||||
|
@ -22,55 +22,49 @@
|
|||||||
Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent):
|
Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent):
|
||||||
RosterItem(pJid, account, parent),
|
RosterItem(pJid, account, parent),
|
||||||
groups(),
|
groups(),
|
||||||
subscriptionState(Shared::SubscriptionState::unknown)
|
subscriptionState(Shared::SubscriptionState::unknown),
|
||||||
{
|
pep(Shared::Support::unknown)
|
||||||
}
|
|
||||||
|
|
||||||
Core::Contact::~Contact()
|
#ifdef WITH_OMEMO
|
||||||
{
|
,omemoBundles(Shared::Possible::unknown)
|
||||||
}
|
#endif
|
||||||
|
{}
|
||||||
|
|
||||||
QSet<QString> Core::Contact::getGroups() const
|
Core::Contact::~Contact() {}
|
||||||
{
|
|
||||||
|
QSet<QString> Core::Contact::getGroups() const {
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Core::Contact::groupsCount() const
|
unsigned int Core::Contact::groupsCount() const {
|
||||||
{
|
|
||||||
return groups.size();
|
return groups.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Contact::setGroups(const QSet<QString>& set)
|
void Core::Contact::setGroups(const QSet<QString>& set) {
|
||||||
{
|
|
||||||
QSet<QString> toRemove = groups - set;
|
QSet<QString> toRemove = groups - set;
|
||||||
QSet<QString> toAdd = set - groups;
|
QSet<QString> toAdd = set - groups;
|
||||||
|
|
||||||
groups = set;
|
groups = set;
|
||||||
|
|
||||||
for (QSet<QString>::iterator itr = toRemove.begin(), end = toRemove.end(); itr != end; ++itr) {
|
for (const QString& group : toRemove)
|
||||||
emit groupRemoved(*itr);
|
emit groupRemoved(group);
|
||||||
}
|
|
||||||
|
|
||||||
for (QSet<QString>::iterator itr = toAdd.begin(), end = toAdd.end(); itr != end; ++itr) {
|
for (const QString& group : toAdd)
|
||||||
emit groupAdded(*itr);
|
emit groupAdded(group);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::SubscriptionState Core::Contact::getSubscriptionState() const
|
Shared::SubscriptionState Core::Contact::getSubscriptionState() const {
|
||||||
{
|
|
||||||
return subscriptionState;
|
return subscriptionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Contact::setSubscriptionState(Shared::SubscriptionState state)
|
void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) {
|
||||||
{
|
|
||||||
if (subscriptionState != state) {
|
if (subscriptionState != state) {
|
||||||
subscriptionState = state;
|
subscriptionState = state;
|
||||||
emit subscriptionStateChanged(subscriptionState);
|
emit subscriptionStateChanged(subscriptionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Contact::handlePresence(const QXmppPresence& pres)
|
void Core::Contact::handlePresence(const QXmppPresence& pres) {
|
||||||
{
|
|
||||||
switch (pres.vCardUpdateType()) {
|
switch (pres.vCardUpdateType()) {
|
||||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||||
break;
|
break;
|
||||||
@ -79,18 +73,17 @@ void Core::Contact::handlePresence(const QXmppPresence& pres)
|
|||||||
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
|
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
|
||||||
Archive::AvatarInfo info;
|
Archive::AvatarInfo info;
|
||||||
bool hasAvatar = readAvatarInfo(info);
|
bool hasAvatar = readAvatarInfo(info);
|
||||||
if (!hasAvatar || !info.autogenerated) {
|
if (!hasAvatar || !info.autogenerated)
|
||||||
setAutoGeneratedAvatar();
|
setAutoGeneratedAvatar();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
||||||
Archive::AvatarInfo info;
|
Archive::AvatarInfo info;
|
||||||
bool hasAvatar = readAvatarInfo(info);
|
bool hasAvatar = readAvatarInfo(info);
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
if (info.autogenerated || info.hash != pres.photoHash()) {
|
if (info.autogenerated || info.hash != pres.photoHash())
|
||||||
emit requestVCard(jid);
|
emit requestVCard(jid);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
emit requestVCard(jid);
|
emit requestVCard(jid);
|
||||||
}
|
}
|
||||||
@ -98,3 +91,23 @@ void Core::Contact::handlePresence(const QXmppPresence& pres)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Contact::setPepSupport(Shared::Support support) {
|
||||||
|
if (pep != support)
|
||||||
|
pep = support;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Support Core::Contact::getPepSupport() const {
|
||||||
|
return pep;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> Core::Contact::getInfo() const {
|
||||||
|
QMap<QString, QVariant> data = RosterItem::getInfo();
|
||||||
|
|
||||||
|
data.insert("state", QVariant::fromValue(subscriptionState));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,17 +16,18 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_CONTACT_H
|
#pragma once
|
||||||
#define CORE_CONTACT_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
#include "rosteritem.h"
|
#include "rosteritem.h"
|
||||||
|
|
||||||
|
#include <shared/enums.h>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class Contact : public RosterItem
|
class Contact : public RosterItem {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Contact(const QString& pJid, const QString& account, QObject* parent = 0);
|
Contact(const QString& pJid, const QString& account, QObject* parent = 0);
|
||||||
@ -38,7 +39,11 @@ public:
|
|||||||
|
|
||||||
void setSubscriptionState(Shared::SubscriptionState state);
|
void setSubscriptionState(Shared::SubscriptionState state);
|
||||||
Shared::SubscriptionState getSubscriptionState() const;
|
Shared::SubscriptionState getSubscriptionState() const;
|
||||||
|
void setPepSupport(Shared::Support support);
|
||||||
|
Shared::Support getPepSupport() const;
|
||||||
|
|
||||||
void handlePresence(const QXmppPresence & pres) override;
|
void handlePresence(const QXmppPresence & pres) override;
|
||||||
|
QMap<QString, QVariant> getInfo() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void groupAdded(const QString& name);
|
void groupAdded(const QString& name);
|
||||||
@ -48,7 +53,11 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QSet<QString> groups;
|
QSet<QString> groups;
|
||||||
Shared::SubscriptionState subscriptionState;
|
Shared::SubscriptionState subscriptionState;
|
||||||
|
Shared::Support pep;
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
public:
|
||||||
|
Shared::Possible omemoBundles;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_CONTACT_H
|
|
||||||
|
26
core/delayManager/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
set(SOURCE_FILES
|
||||||
|
manager.cpp
|
||||||
|
job.cpp
|
||||||
|
cardinternal.cpp
|
||||||
|
infoforuser.cpp
|
||||||
|
owncardinternal.cpp
|
||||||
|
owninfoforuser.cpp
|
||||||
|
contact.cpp
|
||||||
|
info.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADER_FILES
|
||||||
|
manager.h
|
||||||
|
job.h
|
||||||
|
cardinternal.h
|
||||||
|
infoforuser.h
|
||||||
|
owncardinternal.h
|
||||||
|
owninfoforuser.h
|
||||||
|
contact.h
|
||||||
|
info.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(squawk PRIVATE
|
||||||
|
${SOURCE_FILES}
|
||||||
|
${HEADER_FILES}
|
||||||
|
)
|
30
core/delayManager/cardinternal.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cardinternal.h"
|
||||||
|
|
||||||
|
Core::DelayManager::CardInternal::CardInternal(Id p_id, const QString& p_jid) :
|
||||||
|
Job(p_id, Type::cardInternal),
|
||||||
|
Contact(p_id, p_jid, Type::cardInternal)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
Core::DelayManager::CardInternal::CardInternal(const CardInternal& other) :
|
||||||
|
Job(other),
|
||||||
|
Contact(other)
|
||||||
|
{}
|
35
core/delayManager/cardinternal.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "contact.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class CardInternal : public Contact {
|
||||||
|
public:
|
||||||
|
CardInternal(Id id, const QString& jid);
|
||||||
|
CardInternal(const CardInternal& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
28
core/delayManager/contact.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "contact.h"
|
||||||
|
|
||||||
|
Core::DelayManager::Contact::Contact(const Contact& other):
|
||||||
|
Job(other),
|
||||||
|
jid(other.jid) {}
|
||||||
|
|
||||||
|
|
||||||
|
Core::DelayManager::Contact::Contact(Id p_id, const QString& p_jid, Type p_type):
|
||||||
|
Job(p_id, p_type),
|
||||||
|
jid(p_jid) {}
|
38
core/delayManager/contact.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class Contact : public virtual Job {
|
||||||
|
protected:
|
||||||
|
Contact(Id id, const QString& jid, Type type);
|
||||||
|
Contact(const Contact& other);
|
||||||
|
|
||||||
|
public:
|
||||||
|
const QString jid;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
61
core/delayManager/info.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "info.h"
|
||||||
|
|
||||||
|
Core::DelayManager::Info::Info(Id p_id, Type p_type) :
|
||||||
|
Job(p_id, p_type),
|
||||||
|
stage(Stage::waitingForVCard),
|
||||||
|
info(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::Info::Info(const Info& other) :
|
||||||
|
Job(other),
|
||||||
|
stage(other.stage),
|
||||||
|
info(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::Info::~Info() {
|
||||||
|
if (stage == Stage::waitingForBundles) {
|
||||||
|
delete info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::DelayManager::Info::Stage Core::DelayManager::Info::getStage() const {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Info::receivedVCard(const Shared::VCard& card) {
|
||||||
|
if (stage != Stage::waitingForVCard)
|
||||||
|
throw 245;
|
||||||
|
|
||||||
|
info = new Shared::VCard(card);
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
stage = Stage::waitingForBundles;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard * Core::DelayManager::Info::claim() {
|
||||||
|
if (stage != Stage::waitingForBundles)
|
||||||
|
throw 246;
|
||||||
|
|
||||||
|
Shared::VCard* res = info;
|
||||||
|
info = nullptr;
|
||||||
|
return res;
|
||||||
|
}
|
55
core/delayManager/info.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
#include <shared/info.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class Info : public virtual Job {
|
||||||
|
public:
|
||||||
|
enum class Stage {
|
||||||
|
waitingForVCard,
|
||||||
|
waitingForBundles,
|
||||||
|
finished
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Info(Id id, Type type);
|
||||||
|
Info(const Info& other);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~Info();
|
||||||
|
|
||||||
|
void receivedVCard(const Shared::VCard& card);
|
||||||
|
Shared::VCard* claim();
|
||||||
|
Stage getStage() const;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stage stage;
|
||||||
|
Shared::VCard* info;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
31
core/delayManager/infoforuser.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "infoforuser.h"
|
||||||
|
|
||||||
|
Core::DelayManager::InfoForUser::InfoForUser(Id p_id, const QString& p_jid) :
|
||||||
|
Job(p_id, Type::infoForUser),
|
||||||
|
Contact(p_id, p_jid, Type::infoForUser),
|
||||||
|
Info(p_id, Type::infoForUser)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::InfoForUser::InfoForUser(const InfoForUser& other) :
|
||||||
|
Job(other),
|
||||||
|
Contact(other),
|
||||||
|
Info(other)
|
||||||
|
{}
|
34
core/delayManager/infoforuser.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "contact.h"
|
||||||
|
#include "info.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class InfoForUser : public Contact, public Info {
|
||||||
|
public:
|
||||||
|
InfoForUser(Id id, const QString& jid);
|
||||||
|
InfoForUser(const InfoForUser& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
30
core/delayManager/job.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
Core::DelayManager::Job::Job(Id p_id, Type p_type) :
|
||||||
|
id (p_id),
|
||||||
|
type (p_type) {}
|
||||||
|
|
||||||
|
|
||||||
|
Core::DelayManager::Job::Job(const Job& other) :
|
||||||
|
id(other.id),
|
||||||
|
type(other.type) {}
|
||||||
|
|
||||||
|
Core::DelayManager::Job::~Job() {}
|
54
core/delayManager/job.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class Job {
|
||||||
|
public:
|
||||||
|
typedef uint16_t Id;
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
cardInternal,
|
||||||
|
ownCardInternal,
|
||||||
|
infoForUser,
|
||||||
|
ownInfoForUser
|
||||||
|
};
|
||||||
|
inline static constexpr const char * const TypeString[] = {
|
||||||
|
"cardInternal",
|
||||||
|
"ownCardInternal",
|
||||||
|
"infoForUser",
|
||||||
|
"ownInfoForUser"
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
Job(Id id, Type type);
|
||||||
|
Job(const Job& other);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Job();
|
||||||
|
|
||||||
|
const Id id;
|
||||||
|
const Type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
439
core/delayManager/manager.cpp
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "cardinternal.h"
|
||||||
|
#include "infoforuser.h"
|
||||||
|
#include "owncardinternal.h"
|
||||||
|
#include "owninfoforuser.h"
|
||||||
|
|
||||||
|
Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* parent) :
|
||||||
|
QObject(parent),
|
||||||
|
maxParallelJobs(mpj),
|
||||||
|
nextJobId(1),
|
||||||
|
scheduledJobs(),
|
||||||
|
scheduledJobsById(scheduledJobs.get<id>()),
|
||||||
|
jobSequence(scheduledJobs.get<sequence>()),
|
||||||
|
runningJobs(),
|
||||||
|
ownVCardJobId(0),
|
||||||
|
ownInfoJobId(0),
|
||||||
|
scheduledVCards(),
|
||||||
|
requestedVCards(),
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
requestedBundles(),
|
||||||
|
#endif
|
||||||
|
ownJid(poj)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::Manager::~Manager() {
|
||||||
|
for (const std::pair<const Job::Id, Job*>& pair : runningJobs)
|
||||||
|
delete pair.second;
|
||||||
|
|
||||||
|
for (Job* job : jobSequence)
|
||||||
|
delete job;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() {
|
||||||
|
Job::Id id = nextJobId++;
|
||||||
|
if (id == 0)
|
||||||
|
id = nextJobId++;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::getInfo(const QString& jid) {
|
||||||
|
if (jid == ownJid)
|
||||||
|
return getOwnInfo();
|
||||||
|
|
||||||
|
Job* job = nullptr;
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
std::map<QString, Job::Id>::const_iterator bitr = requestedBundles.find(jid);
|
||||||
|
if (bitr != requestedBundles.end()) {
|
||||||
|
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(bitr->second);
|
||||||
|
if (itr == runningJobs.end())
|
||||||
|
throw JobNotFound(bitr->second);
|
||||||
|
|
||||||
|
job = itr->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
job = getVCardJob(jid);
|
||||||
|
|
||||||
|
if (job != nullptr) {
|
||||||
|
if (job->type == Job::Type::cardInternal)
|
||||||
|
replaceJob(new InfoForUser(job->id, jid));
|
||||||
|
} else
|
||||||
|
scheduleJob(new InfoForUser(getNextJobId(), jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::getOwnInfo() {
|
||||||
|
if (ownInfoJobId == 0) {
|
||||||
|
if (ownVCardJobId != 0)
|
||||||
|
replaceJob(new OwnInfoForUser(ownVCardJobId));
|
||||||
|
else
|
||||||
|
scheduleJob(new OwnInfoForUser(getNextJobId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::getVCard(const QString& jid) {
|
||||||
|
Job* job = getVCardJob(jid);
|
||||||
|
if (job == nullptr)
|
||||||
|
scheduleJob(new CardInternal(getNextJobId(), jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::getOwnVCard() {
|
||||||
|
if (ownVCardJobId == 0)
|
||||||
|
scheduleJob(new OwnCardInternal(getNextJobId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::DelayManager::Manager::isOwnVCardPending() const {
|
||||||
|
return ownVCardJobId != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) {
|
||||||
|
Job* job = nullptr;
|
||||||
|
std::map<QString, Job::Id>::const_iterator sitr = scheduledVCards.find(jid);
|
||||||
|
if (sitr == scheduledVCards.end()) {
|
||||||
|
std::map<QString, Job::Id>::const_iterator ritr = requestedVCards.find(jid);
|
||||||
|
if (ritr != requestedVCards.end()) {
|
||||||
|
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(ritr->second);
|
||||||
|
if (itr == runningJobs.end())
|
||||||
|
throw JobNotFound(ritr->second, "getVCardJob:1");
|
||||||
|
|
||||||
|
job = itr->second;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StorageById::const_iterator itr = scheduledJobsById.find(sitr->second);
|
||||||
|
if (itr == scheduledJobsById.end())
|
||||||
|
throw JobNotFound(sitr->second, "getVCardJob:2");
|
||||||
|
|
||||||
|
job = *itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
void Core::DelayManager::Manager::preScheduleJob(Job* job) {
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::cardInternal:
|
||||||
|
scheduledVCards.emplace(dynamic_cast<CardInternal*>(job)->jid, job->id);
|
||||||
|
break;
|
||||||
|
case Job::Type::ownCardInternal:
|
||||||
|
ownVCardJobId = job->id;
|
||||||
|
break;
|
||||||
|
case Job::Type::infoForUser:
|
||||||
|
scheduledVCards.emplace(dynamic_cast<InfoForUser*>(job)->jid, job->id);
|
||||||
|
break;
|
||||||
|
case Job::Type::ownInfoForUser:
|
||||||
|
ownVCardJobId = job->id;
|
||||||
|
ownInfoJobId = job->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::scheduleJob(Job* job) {
|
||||||
|
preScheduleJob(job);
|
||||||
|
if (runningJobs.size() < maxParallelJobs)
|
||||||
|
executeJob(job);
|
||||||
|
else
|
||||||
|
scheduledJobs.push_back(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::preExecuteJob(Job* job) {
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::cardInternal:
|
||||||
|
case Job::Type::infoForUser: {
|
||||||
|
Contact* cij = dynamic_cast<Contact*>(job);
|
||||||
|
requestedVCards.emplace(cij->jid, job->id);
|
||||||
|
scheduledVCards.erase(cij->jid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Job::Type::ownInfoForUser:
|
||||||
|
case Job::Type::ownCardInternal:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::executeJob(Job* job) {
|
||||||
|
preExecuteJob(job);
|
||||||
|
runningJobs.emplace(job->id, job);
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::cardInternal:
|
||||||
|
case Job::Type::infoForUser:
|
||||||
|
emit requestVCard(dynamic_cast<Contact*>(job)->jid);
|
||||||
|
break;
|
||||||
|
case Job::Type::ownInfoForUser:
|
||||||
|
case Job::Type::ownCardInternal:
|
||||||
|
emit requestOwnVCard();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) {
|
||||||
|
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
|
||||||
|
if (itr == runningJobs.end())
|
||||||
|
throw JobNotFound(jobId, "jobIsDone");
|
||||||
|
|
||||||
|
Job* job = itr->second;
|
||||||
|
delete job;
|
||||||
|
runningJobs.erase(itr);
|
||||||
|
if (scheduledJobs.size() > 0) {
|
||||||
|
Job* job = scheduledJobs.front();
|
||||||
|
scheduledJobs.pop_front();
|
||||||
|
executeJob(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::replaceJob(Job* job) {
|
||||||
|
preScheduleJob(job);
|
||||||
|
std::map<Job::Id, Job*>::iterator itr = runningJobs.find(job->id);
|
||||||
|
if (itr != runningJobs.end()) {
|
||||||
|
preExecuteJob(job);
|
||||||
|
delete itr->second;
|
||||||
|
itr->second = job;
|
||||||
|
} else {
|
||||||
|
StorageById::iterator sitr = scheduledJobsById.find(job->id);
|
||||||
|
if (sitr != scheduledJobsById.end()) {
|
||||||
|
delete *(sitr);
|
||||||
|
scheduledJobsById.replace(sitr, job);
|
||||||
|
} else
|
||||||
|
throw JobNotFound(job->id, "replaceJob");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) {
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::cardInternal: {
|
||||||
|
CardInternal* jb = dynamic_cast<CardInternal*>(job);
|
||||||
|
if (wasRunning)
|
||||||
|
requestedVCards.erase(jb->jid);
|
||||||
|
else
|
||||||
|
scheduledVCards.erase(jb->jid);
|
||||||
|
|
||||||
|
emit gotVCard(jb->jid, Shared::VCard());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Job::Type::infoForUser: {
|
||||||
|
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
|
||||||
|
switch (jb->getStage()) {
|
||||||
|
case InfoForUser::Stage::waitingForVCard:
|
||||||
|
if (wasRunning)
|
||||||
|
requestedVCards.erase(jb->jid);
|
||||||
|
else
|
||||||
|
scheduledVCards.erase(jb->jid);
|
||||||
|
|
||||||
|
emit gotVCard(jb->jid, Shared::VCard());
|
||||||
|
break;
|
||||||
|
case InfoForUser::Stage::waitingForBundles:
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
requestedBundles.erase(jb->jid);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
emit gotInfo(Shared::Info(jb->jid));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Job::Type::ownInfoForUser: {
|
||||||
|
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
|
||||||
|
if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) {
|
||||||
|
ownVCardJobId = 0;
|
||||||
|
emit gotOwnVCard(Shared::VCard());
|
||||||
|
}
|
||||||
|
ownInfoJobId = 0;
|
||||||
|
emit gotOwnInfo(Shared::Info (ownJid));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Job::Type::ownCardInternal:
|
||||||
|
ownVCardJobId = 0;
|
||||||
|
emit gotOwnVCard(Shared::VCard());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete job;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::disconnected() {
|
||||||
|
for (const std::pair<const Job::Id, Job*> pair : runningJobs)
|
||||||
|
jobIsCanceled(pair.second, true);
|
||||||
|
|
||||||
|
for (Job* job : scheduledJobs)
|
||||||
|
jobIsCanceled(job, false);
|
||||||
|
|
||||||
|
runningJobs.clear();
|
||||||
|
scheduledJobs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) {
|
||||||
|
std::map<QString, Job::Id>::const_iterator cardItr = requestedVCards.find(jid);
|
||||||
|
if (cardItr == requestedVCards.end()) {
|
||||||
|
qDebug() << "received VCard for" << jid << "but it was never requested through manager, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Job::Id jobId = cardItr->second;
|
||||||
|
requestedVCards.erase(cardItr);
|
||||||
|
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
|
||||||
|
if (itr == runningJobs.end())
|
||||||
|
throw JobNotFound(jobId, "receivedVCard");
|
||||||
|
|
||||||
|
Job* job = itr->second;
|
||||||
|
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::cardInternal:
|
||||||
|
jobIsDone(jobId);
|
||||||
|
emit gotVCard(jid, card);
|
||||||
|
break;
|
||||||
|
case Job::Type::infoForUser: {
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
requestedBundles.emplace(jid, jobId);
|
||||||
|
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
|
||||||
|
jb->receivedVCard(card);
|
||||||
|
emit requestBundles(jid);
|
||||||
|
#else
|
||||||
|
Shared::Info info(jid);
|
||||||
|
info.turnIntoContact(card);
|
||||||
|
emit gotInfo(info);
|
||||||
|
jobIsDone(jobId);
|
||||||
|
#endif
|
||||||
|
emit gotVCard(jid, card);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw UnexpectedJobType(job->type, "receivedVCard");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) {
|
||||||
|
if (ownVCardJobId == 0) {
|
||||||
|
qDebug() << "received own VCard for" << ownJid << "but it was never requested through manager, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Job::Id jobId = ownVCardJobId;
|
||||||
|
ownVCardJobId = 0;
|
||||||
|
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
|
||||||
|
if (itr == runningJobs.end())
|
||||||
|
throw JobNotFound(jobId, "receivedOwnVCard");
|
||||||
|
|
||||||
|
Job* job = itr->second;
|
||||||
|
switch (job->type) {
|
||||||
|
case Job::Type::ownCardInternal:
|
||||||
|
jobIsDone(jobId);
|
||||||
|
emit gotOwnVCard(card);
|
||||||
|
break;
|
||||||
|
case Job::Type::ownInfoForUser: {
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
|
||||||
|
jb->receivedVCard(card);
|
||||||
|
emit requestOwnBundles();
|
||||||
|
#else
|
||||||
|
Shared::Info info(ownJid);
|
||||||
|
info.turnIntoOwnAccount(card);
|
||||||
|
emit gotOwnInfo(info);
|
||||||
|
jobIsDone(jobId);
|
||||||
|
#endif
|
||||||
|
emit gotOwnVCard(card);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw UnexpectedJobType(job->type, "receivedVCard");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list<Shared::KeyInfo>& keys) {
|
||||||
|
std::map<QString, Job::Id>::const_iterator itr = requestedBundles.find(jid);
|
||||||
|
if (itr == requestedBundles.end()) {
|
||||||
|
qDebug() << "received bundles for" << jid << "but they were never requested through manager, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Job::Id jobId = itr->second;
|
||||||
|
requestedBundles.erase(itr);
|
||||||
|
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
|
||||||
|
if (jitr == runningJobs.end())
|
||||||
|
throw JobNotFound(jobId, "receivedBundles");
|
||||||
|
|
||||||
|
Job* jb = jitr->second;
|
||||||
|
InfoForUser* job = dynamic_cast<InfoForUser*>(jb);
|
||||||
|
|
||||||
|
Shared::Info info(jid);
|
||||||
|
info.turnIntoContact(job->claim(), new std::list<Shared::KeyInfo>(keys));
|
||||||
|
emit gotInfo(info);
|
||||||
|
jobIsDone(jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::receivedOwnBundles(const std::list<Shared::KeyInfo>& keys) {
|
||||||
|
if (ownInfoJobId == 0) {
|
||||||
|
qDebug() << "received own bundles for" << ownJid << "but they were never requested through manager, ignoring";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Job::Id jobId = ownInfoJobId;
|
||||||
|
ownInfoJobId = 0;
|
||||||
|
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
|
||||||
|
if (jitr == runningJobs.end())
|
||||||
|
throw JobNotFound(jobId, "receivedOwnBundles");
|
||||||
|
|
||||||
|
Job* jb = jitr->second;
|
||||||
|
OwnInfoForUser* job = dynamic_cast<OwnInfoForUser*>(jb);
|
||||||
|
|
||||||
|
Shared::Info info(ownJid);
|
||||||
|
info.turnIntoOwnAccount(job->claim(), new std::list<Shared::KeyInfo>(keys));
|
||||||
|
emit gotOwnInfo(info);
|
||||||
|
jobIsDone(jobId);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Core::DelayManager::Manager::setOwnJid(const QString& jid) {
|
||||||
|
ownJid = jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Core::DelayManager::Manager::UnexpectedJobType::UnexpectedJobType(Job::Type p_type, const std::string& p_method):
|
||||||
|
Exception(),
|
||||||
|
type(p_type),
|
||||||
|
method(p_method)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string Core::DelayManager::Manager::UnexpectedJobType::getMessage() const{
|
||||||
|
std::string msg("Unexpected job type: ");
|
||||||
|
msg += Job::TypeString[static_cast<int>(type)];
|
||||||
|
if (method.size() > 0)
|
||||||
|
msg += " in method " + method;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::DelayManager::Manager::JobNotFound::JobNotFound(Job::Id p_id, const std::string& p_method) :
|
||||||
|
Exception(),
|
||||||
|
id(p_id),
|
||||||
|
method(p_method)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string Core::DelayManager::Manager::JobNotFound::getMessage() const {
|
||||||
|
std::string msg("Job with id ");
|
||||||
|
msg += std::to_string(id);
|
||||||
|
msg += " was not found";
|
||||||
|
if (method.size() > 0)
|
||||||
|
msg += " in method " + method;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
153
core/delayManager/manager.h
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
#include <boost/multi_index/member.hpp>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
#include <shared/info.h>
|
||||||
|
#include <shared/exception.h>
|
||||||
|
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class Manager : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr);
|
||||||
|
~Manager();
|
||||||
|
|
||||||
|
void setOwnJid(const QString& jid);
|
||||||
|
bool isOwnVCardPending() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void getOwnVCard();
|
||||||
|
void getOwnInfo();
|
||||||
|
void getVCard(const QString& jid);
|
||||||
|
void getInfo(const QString& jid);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void requestVCard(const QString& jid);
|
||||||
|
void requestOwnVCard();
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
void requestBundles(const QString& jid);
|
||||||
|
void requestOwnBundles();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void gotVCard(const QString& jid, const Shared::VCard& info);
|
||||||
|
void gotOwnVCard(const Shared::VCard& info);
|
||||||
|
void gotInfo(const Shared::Info& info);
|
||||||
|
void gotOwnInfo(const Shared::Info& info);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void disconnected();
|
||||||
|
void receivedOwnVCard(const Shared::VCard& card);
|
||||||
|
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
void receivedBundles(const QString& jid, const std::list<Shared::KeyInfo>& keys);
|
||||||
|
void receivedOwnBundles(const std::list<Shared::KeyInfo>& keys);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
void preScheduleJob(Job* job);
|
||||||
|
void scheduleJob(Job* job);
|
||||||
|
void preExecuteJob(Job* job);
|
||||||
|
void executeJob(Job* job);
|
||||||
|
void jobIsCanceled(Job* job, bool wasRunning);
|
||||||
|
void jobIsDone(Job::Id jobId);
|
||||||
|
Job::Id getNextJobId();
|
||||||
|
void replaceJob(Job* job);
|
||||||
|
Job* getVCardJob(const QString& jid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct id {};
|
||||||
|
struct sequence {};
|
||||||
|
|
||||||
|
typedef boost::multi_index_container<
|
||||||
|
Job*,
|
||||||
|
boost::multi_index::indexed_by<
|
||||||
|
boost::multi_index::sequenced<
|
||||||
|
boost::multi_index::tag<sequence>
|
||||||
|
>,
|
||||||
|
boost::multi_index::ordered_unique<
|
||||||
|
boost::multi_index::tag<id>,
|
||||||
|
boost::multi_index::member<
|
||||||
|
Job,
|
||||||
|
const Job::Id,
|
||||||
|
&Job::id
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> Storage;
|
||||||
|
|
||||||
|
|
||||||
|
typedef Storage::index<id>::type StorageById;
|
||||||
|
typedef Storage::index<sequence>::type StorageSequence;
|
||||||
|
Job::Id maxParallelJobs;
|
||||||
|
Job::Id nextJobId;
|
||||||
|
|
||||||
|
Storage scheduledJobs;
|
||||||
|
StorageById& scheduledJobsById;
|
||||||
|
StorageSequence& jobSequence;
|
||||||
|
std::map<Job::Id, Job*> runningJobs;
|
||||||
|
|
||||||
|
Job::Id ownVCardJobId;
|
||||||
|
Job::Id ownInfoJobId;
|
||||||
|
std::map<QString, Job::Id> scheduledVCards;
|
||||||
|
std::map<QString, Job::Id> requestedVCards;
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
std::map<QString, Job::Id> requestedBundles;
|
||||||
|
#endif
|
||||||
|
QString ownJid;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class UnexpectedJobType: public Utils::Exception {
|
||||||
|
public:
|
||||||
|
UnexpectedJobType(Job::Type p_type, const std::string& p_method = "");
|
||||||
|
std::string getMessage() const override;
|
||||||
|
private:
|
||||||
|
Job::Type type;
|
||||||
|
std::string method;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JobNotFound: public Utils::Exception {
|
||||||
|
public:
|
||||||
|
JobNotFound(Job::Id p_id, const std::string& p_method = "");
|
||||||
|
std::string getMessage() const override;
|
||||||
|
private:
|
||||||
|
Job::Id id;
|
||||||
|
std::string method;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
32
core/delayManager/owncardinternal.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "owncardinternal.h"
|
||||||
|
|
||||||
|
Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id) :
|
||||||
|
Job(p_id, Type::ownCardInternal)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id, Type p_type) :
|
||||||
|
Job(p_id, p_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::OwnCardInternal::OwnCardInternal(const OwnCardInternal& other) :
|
||||||
|
Job(other)
|
||||||
|
{}
|
||||||
|
|
36
core/delayManager/owncardinternal.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class OwnCardInternal : public Job {
|
||||||
|
protected:
|
||||||
|
OwnCardInternal(Id id, Type type);
|
||||||
|
|
||||||
|
public:
|
||||||
|
OwnCardInternal(Id id);
|
||||||
|
OwnCardInternal(const OwnCardInternal& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
29
core/delayManager/owninfoforuser.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "owninfoforuser.h"
|
||||||
|
|
||||||
|
Core::DelayManager::OwnInfoForUser::OwnInfoForUser(Id p_id) :
|
||||||
|
Job(p_id, Type::ownInfoForUser),
|
||||||
|
Info(p_id, Type::ownInfoForUser)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Core::DelayManager::OwnInfoForUser::OwnInfoForUser(const OwnInfoForUser& other) :
|
||||||
|
Job(other),
|
||||||
|
Info(other)
|
||||||
|
{}
|
34
core/delayManager/owninfoforuser.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "contact.h"
|
||||||
|
#include "info.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace DelayManager {
|
||||||
|
|
||||||
|
class OwnInfoForUser : public Info {
|
||||||
|
public:
|
||||||
|
OwnInfoForUser(Id id);
|
||||||
|
OwnInfoForUser(const OwnInfoForUser& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,22 @@
|
|||||||
target_sources(squawk PRIVATE
|
set(SOURCE_FILES
|
||||||
messagehandler.cpp
|
messagehandler.cpp
|
||||||
messagehandler.h
|
|
||||||
rosterhandler.cpp
|
rosterhandler.cpp
|
||||||
|
vcardhandler.cpp
|
||||||
|
discoveryhandler.cpp
|
||||||
|
trusthandler.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADER_FILES
|
||||||
|
messagehandler.h
|
||||||
rosterhandler.h
|
rosterhandler.h
|
||||||
)
|
vcardhandler.h
|
||||||
|
discoveryhandler.h
|
||||||
|
trusthandler.h
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WITH_OMEMO)
|
||||||
|
list(APPEND SOURCE_FILES omemohandler.cpp)
|
||||||
|
list(APPEND HEADER_FILES omemohandler.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_sources(squawk PRIVATE ${SOURCE_FILES})
|
||||||
|
155
core/handlers/discoveryhandler.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "discoveryhandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account):
|
||||||
|
QObject(),
|
||||||
|
acc(account),
|
||||||
|
omemoToCarbonsConnected (false) {}
|
||||||
|
|
||||||
|
Core::DiscoveryHandler::~DiscoveryHandler() {}
|
||||||
|
|
||||||
|
void Core::DiscoveryHandler::initialize()
|
||||||
|
{
|
||||||
|
QObject::connect(acc->dm, &QXmppDiscoveryManager::itemsReceived, this, &DiscoveryHandler::onItemsReceived);
|
||||||
|
QObject::connect(acc->dm, &QXmppDiscoveryManager::infoReceived, this, &DiscoveryHandler::onInfoReceived);
|
||||||
|
|
||||||
|
acc->dm->setClientType("pc");
|
||||||
|
acc->dm->setClientCategory("client");
|
||||||
|
acc->dm->setClientName(qApp->applicationDisplayName() + " " + qApp->applicationVersion());
|
||||||
|
acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items)
|
||||||
|
{
|
||||||
|
QString server = acc->getServer();
|
||||||
|
if (items.from() == server) {
|
||||||
|
std::set<QString> needToRequest;
|
||||||
|
qDebug() << "Server items list received for account " << acc->getName() << ":";
|
||||||
|
for (QXmppDiscoveryIq::Item item : items.items()) {
|
||||||
|
QString jid = item.jid();
|
||||||
|
if (jid != server) {
|
||||||
|
qDebug() << " Node" << jid;
|
||||||
|
needToRequest.insert(jid);
|
||||||
|
} else {
|
||||||
|
qDebug() << " " << item.node().toStdString().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& jid : needToRequest) {
|
||||||
|
acc->dm->requestInfo(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info)
|
||||||
|
{
|
||||||
|
QString from = info.from();
|
||||||
|
QString server = acc->getServer();
|
||||||
|
QString accName = acc->getName();
|
||||||
|
QString bareJid = acc->getBareJid();
|
||||||
|
if (from == server) {
|
||||||
|
bool enableCC = false;
|
||||||
|
qDebug() << "Server info received for account" << accName;
|
||||||
|
QStringList features = info.features();
|
||||||
|
qDebug() << "List of supported features of the server " << server << ":";
|
||||||
|
for (const QString& feature : features) {
|
||||||
|
qDebug() << " " << feature.toStdString().c_str();
|
||||||
|
if (feature == "urn:xmpp:carbons:2") {
|
||||||
|
enableCC = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableCC) {
|
||||||
|
qDebug() << "Enabling carbon copies for account" << accName;
|
||||||
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
acc->cm->setCarbonsEnabled(true);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
if (!omemoToCarbonsConnected && acc->oh->hasOwnDevice()) {
|
||||||
|
// connect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage);
|
||||||
|
// connect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage);
|
||||||
|
omemoToCarbonsConnected = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (omemoToCarbonsConnected) {
|
||||||
|
// disconnect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage);
|
||||||
|
// disconnect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage);
|
||||||
|
omemoToCarbonsConnected = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Requesting account" << accName << "capabilities";
|
||||||
|
acc->dm->requestInfo(bareJid);
|
||||||
|
} else if (from == bareJid) {
|
||||||
|
qDebug() << "Received capabilities for account" << accName << ":";
|
||||||
|
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
||||||
|
bool pepSupported = false;
|
||||||
|
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
||||||
|
QString type = identity.type();
|
||||||
|
QString category = identity.category();
|
||||||
|
qDebug() << " " << category << type;
|
||||||
|
if (type == "pep" && category == "pubsub") {
|
||||||
|
pepSupported = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported);
|
||||||
|
} else {
|
||||||
|
QString node = info.queryNode();
|
||||||
|
if (!node.isEmpty()) {
|
||||||
|
qDebug() << "Received features and identities for account" << accName << "about" << from;
|
||||||
|
QStringList feats = info.features();
|
||||||
|
std::set<Shared::Identity> identities;
|
||||||
|
std::set<QString> features(feats.begin(), feats.end());
|
||||||
|
QList<QXmppDiscoveryIq::Identity> idents = info.identities();
|
||||||
|
for (const QXmppDiscoveryIq::Identity& ident : idents) {
|
||||||
|
Shared::Identity identity;
|
||||||
|
identity.category = ident.category();
|
||||||
|
identity.language = ident.language();
|
||||||
|
identity.name = ident.name();
|
||||||
|
identity.type = ident.type();
|
||||||
|
identities.insert(identity);
|
||||||
|
|
||||||
|
qDebug() << " " << identity.name << identity.category << identity.type;
|
||||||
|
}
|
||||||
|
for (const QString& feat : features) {
|
||||||
|
qDebug() << " " << feat;
|
||||||
|
}
|
||||||
|
emit acc->infoDiscovered(from, node, identities, features);
|
||||||
|
} else {
|
||||||
|
Contact* cont = acc->rh->getContact(from);
|
||||||
|
if (cont != nullptr) {
|
||||||
|
qDebug() << "Received info for account" << accName << "about contact" << from;
|
||||||
|
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
||||||
|
bool pepSupported = false;
|
||||||
|
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
||||||
|
QString type = identity.type();
|
||||||
|
QString category = identity.category();
|
||||||
|
qDebug() << " " << category << type;
|
||||||
|
if (type == "pep" && category == "pubsub") {
|
||||||
|
pepSupported = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
core/handlers/discoveryhandler.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CORE_DISCOVERYHANDLER_H
|
||||||
|
#define CORE_DISCOVERYHANDLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <QXmppDiscoveryManager.h>
|
||||||
|
#include <QXmppDiscoveryIq.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class DiscoveryHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DiscoveryHandler(Account* account);
|
||||||
|
~DiscoveryHandler();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
|
void onInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
bool omemoToCarbonsConnected;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_DISCOVERYHANDLER_H
|
@ -19,149 +19,160 @@
|
|||||||
#include "messagehandler.h"
|
#include "messagehandler.h"
|
||||||
#include "core/account.h"
|
#include "core/account.h"
|
||||||
|
|
||||||
|
static const QMap<QString, QVariant> statePending({{"state", static_cast<uint8_t>(Shared::Message::State::pending)}});
|
||||||
|
static const QMap<QString, QVariant> stateDelivered({{"state", static_cast<uint8_t>(Shared::Message::State::delivered)}});
|
||||||
|
static const QMap<QString, QVariant> stateSent({{"state", static_cast<uint8_t>(Shared::Message::State::sent)}});
|
||||||
|
|
||||||
Core::MessageHandler::MessageHandler(Core::Account* account):
|
Core::MessageHandler::MessageHandler(Core::Account* account):
|
||||||
QObject(),
|
QObject(),
|
||||||
acc(account),
|
acc(account),
|
||||||
pendingStateMessages(),
|
pendingStateMessages(),
|
||||||
uploadingSlotsQueue()
|
uploadingSlotsQueue()
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
|
void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) {
|
||||||
{
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
switch (msg.encryptionMethod()) {
|
||||||
|
case QXmpp::NoEncryption:
|
||||||
|
break; //just do nothing
|
||||||
|
case QXmpp::UnknownEncryption:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received a message with unknown encryption type";
|
||||||
|
break; //let it go the way it is, there is nothing I can do here
|
||||||
|
case QXmpp::Otr:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an OTR encrypted message, not supported yet";
|
||||||
|
break; //let it go the way it is, there is nothing I can do yet
|
||||||
|
case QXmpp::LegacyOpenPgp:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an LegacyOpenPgp encrypted message, not supported yet";
|
||||||
|
break; //let it go the way it is, there is nothing I can do yet
|
||||||
|
case QXmpp::Ox:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an Ox encrypted message, not supported yet";
|
||||||
|
break; //let it go the way it is, there is nothing I can do yet
|
||||||
|
case QXmpp::Omemo0:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an Omemo0 encrypted message, not supported yet";
|
||||||
|
break; //let it go the way it is, there is nothing I can do yet
|
||||||
|
case QXmpp::Omemo1:
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an Omemo1 encrypted message, not supported yet";
|
||||||
|
break; //let it go the way it is, there is nothing I can do yet
|
||||||
|
case QXmpp::Omemo2:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
switch (msg.type()) {
|
switch (msg.type()) {
|
||||||
case QXmppMessage::Normal:
|
case QXmppMessage::Normal:
|
||||||
qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
|
qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
|
||||||
break;
|
break;
|
||||||
case QXmppMessage::Chat:
|
case QXmppMessage::Chat:
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
handled = handleChatMessage(msg, false, msg.isCarbonForwarded(), true);
|
||||||
|
#else
|
||||||
handled = handleChatMessage(msg);
|
handled = handleChatMessage(msg);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case QXmppMessage::GroupChat:
|
case QXmppMessage::GroupChat:
|
||||||
handled = handleGroupMessage(msg);
|
handled = handleGroupMessage(msg);
|
||||||
break;
|
break;
|
||||||
case QXmppMessage::Error: {
|
case QXmppMessage::Error:
|
||||||
QString id = msg.id();
|
handled = handlePendingMessageError(msg.id(), msg.error().text());
|
||||||
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
if (!handled)
|
||||||
if (itr != pendingStateMessages.end()) {
|
|
||||||
QString jid = itr->second;
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
|
||||||
QMap<QString, QVariant> cData = {
|
|
||||||
{"state", static_cast<uint>(Shared::Message::State::error)},
|
|
||||||
{"errorText", msg.error().text()}
|
|
||||||
};
|
|
||||||
if (cnt != 0) {
|
|
||||||
cnt->changeMessage(id, cData);
|
|
||||||
}
|
|
||||||
;
|
|
||||||
emit acc->changeMessage(jid, id, cData);
|
|
||||||
pendingStateMessages.erase(itr);
|
|
||||||
handled = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
|
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
|
||||||
}
|
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
case QXmppMessage::Headline:
|
case QXmppMessage::Headline:
|
||||||
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
|
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!handled) {
|
if (!handled)
|
||||||
logMessage(msg);
|
logMessage(msg);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
|
bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) {
|
||||||
{
|
return adjustPendingMessage(id, {
|
||||||
if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) {
|
{"state", static_cast<uint8_t>(Shared::Message::State::error)},
|
||||||
Shared::Message sMsg(Shared::Message::chat);
|
{"errorText", errorText}
|
||||||
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
}, true);
|
||||||
QString jid = sMsg.getPenPalJid();
|
|
||||||
Contact* cnt = acc->rh->getContact(jid);
|
|
||||||
if (cnt == 0) {
|
|
||||||
cnt = acc->rh->addOutOfRosterContact(jid);
|
|
||||||
}
|
|
||||||
if (outgoing) {
|
|
||||||
if (forwarded) {
|
|
||||||
sMsg.setState(Shared::Message::State::sent);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sMsg.setState(Shared::Message::State::delivered);
|
|
||||||
}
|
|
||||||
QString oId = msg.replaceId();
|
|
||||||
if (oId.size() > 0) {
|
|
||||||
QMap<QString, QVariant> cData = {
|
|
||||||
{"body", sMsg.getBody()},
|
|
||||||
{"stamp", sMsg.getTime()}
|
|
||||||
};
|
|
||||||
cnt->correctMessageInArchive(oId, sMsg);
|
|
||||||
emit acc->changeMessage(jid, oId, cData);
|
|
||||||
} else {
|
|
||||||
cnt->appendMessageToArchive(sMsg);
|
|
||||||
emit acc->message(sMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
|
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) {
|
||||||
{
|
if (msg.body().isEmpty() && msg.outOfBandUrl().isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Shared::Message sMsg(Shared::Message::chat);
|
||||||
|
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
||||||
|
QString jid = sMsg.getPenPalJid();
|
||||||
|
Contact* cnt = acc->rh->getContact(jid);
|
||||||
|
if (cnt == 0) {
|
||||||
|
cnt = acc->rh->addOutOfRosterContact(jid);
|
||||||
|
qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact";
|
||||||
|
}
|
||||||
|
if (sMsg.getOutgoing()) {
|
||||||
|
if (sMsg.getForwarded())
|
||||||
|
sMsg.setState(Shared::Message::State::sent);
|
||||||
|
} else {
|
||||||
|
sMsg.setState(Shared::Message::State::delivered);
|
||||||
|
}
|
||||||
|
QString oId = msg.replaceId();
|
||||||
|
if (oId.size() > 0) {
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"body", sMsg.getBody()},
|
||||||
|
{"stamp", sMsg.getTime()}
|
||||||
|
};
|
||||||
|
cnt->correctMessageInArchive(oId, sMsg);
|
||||||
|
emit acc->changeMessage(jid, oId, cData);
|
||||||
|
} else {
|
||||||
|
cnt->appendMessageToArchive(sMsg);
|
||||||
|
emit acc->message(sMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) {
|
||||||
const QString& body(msg.body());
|
const QString& body(msg.body());
|
||||||
if (body.size() != 0) {
|
if (body.isEmpty())
|
||||||
QString id = msg.id();
|
return false;
|
||||||
|
|
||||||
Shared::Message sMsg(Shared::Message::groupChat);
|
Shared::Message sMsg(Shared::Message::groupChat);
|
||||||
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
||||||
QString jid = sMsg.getPenPalJid();
|
QString jid = sMsg.getPenPalJid();
|
||||||
Conference* cnt = acc->rh->getConference(jid);
|
Conference* cnt = acc->rh->getConference(jid);
|
||||||
if (cnt == 0) {
|
if (cnt == 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
std::map<QString, QString>::const_iterator pItr = pendingStateMessages.find(id);
|
bool result = adjustPendingMessage(msg.id(), stateDelivered, true);
|
||||||
if (pItr != pendingStateMessages.end()) {
|
if (result) //then it was an echo of my own sent message, nothing else needs to be done
|
||||||
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
return result;
|
||||||
cnt->changeMessage(id, cData);
|
|
||||||
pendingStateMessages.erase(pItr);
|
|
||||||
emit acc->changeMessage(jid, id, cData);
|
|
||||||
} else {
|
|
||||||
QString oId = msg.replaceId();
|
|
||||||
if (oId.size() > 0) {
|
|
||||||
QMap<QString, QVariant> cData = {
|
|
||||||
{"body", sMsg.getBody()},
|
|
||||||
{"stamp", sMsg.getTime()}
|
|
||||||
};
|
|
||||||
cnt->correctMessageInArchive(oId, sMsg);
|
|
||||||
emit acc->changeMessage(jid, oId, cData);
|
|
||||||
} else {
|
|
||||||
cnt->appendMessageToArchive(sMsg);
|
|
||||||
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
|
|
||||||
if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt
|
|
||||||
emit acc->message(sMsg);
|
|
||||||
} else {
|
|
||||||
//qDebug() << "Delayed delivery: ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
QString oId = msg.replaceId();
|
||||||
|
if (oId.size() > 0) {
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"body", sMsg.getBody()},
|
||||||
|
{"stamp", sMsg.getTime()}
|
||||||
|
};
|
||||||
|
cnt->correctMessageInArchive(oId, sMsg);
|
||||||
|
emit acc->changeMessage(jid, oId, cData);
|
||||||
|
} else {
|
||||||
|
cnt->appendMessageToArchive(sMsg);
|
||||||
|
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
|
||||||
|
if (sMsg.getTime() > minAgo) //otherwise it's considered a delayed delivery, most probably MUC history initial fetch
|
||||||
|
emit acc->message(sMsg);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const {
|
||||||
void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const
|
|
||||||
{
|
|
||||||
const QDateTime& time(source.stamp());
|
const QDateTime& time(source.stamp());
|
||||||
QString id;
|
QString id;
|
||||||
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
||||||
id = source.originId();
|
id = source.originId();
|
||||||
if (id.size() == 0) {
|
if (id.size() == 0)
|
||||||
id = source.id();
|
id = source.id();
|
||||||
}
|
|
||||||
target.setStanzaId(source.stanzaId());
|
target.setStanzaId(source.stanzaId());
|
||||||
|
qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId();
|
||||||
#else
|
#else
|
||||||
id = source.id();
|
id = source.id();
|
||||||
#endif
|
#endif
|
||||||
@ -170,35 +181,36 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
if (messageId.size() == 0) {
|
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();
|
messageId = target.getId();
|
||||||
|
qDebug() << "Had do initialize a message with no id, assigning autogenerated" << messageId;
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
if (source.encryptionMethod() == QXmpp::EncryptionMethod::Omemo2)
|
||||||
|
target.setEncryption(Shared::EncryptionProtocol::omemo2);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (guessing)
|
||||||
|
outgoing = target.getFromJid() == acc->getBareJid();
|
||||||
|
|
||||||
if (guessing) {
|
|
||||||
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
|
||||||
outgoing = true;
|
|
||||||
} else {
|
|
||||||
outgoing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target.setOutgoing(outgoing);
|
target.setOutgoing(outgoing);
|
||||||
if (time.isValid()) {
|
if (time.isValid())
|
||||||
target.setTime(time);
|
target.setTime(time);
|
||||||
} else {
|
else
|
||||||
target.setCurrentTime();
|
target.setCurrentTime();
|
||||||
}
|
|
||||||
|
|
||||||
QString oob = source.outOfBandUrl();
|
QString oob = source.outOfBandUrl();
|
||||||
if (oob.size() > 0) {
|
if (oob.size() > 0)
|
||||||
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
|
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
|
||||||
}
|
|
||||||
target.setOutOfBandUrl(oob);
|
target.setOutOfBandUrl(oob);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) {
|
||||||
{
|
|
||||||
qDebug() << reason;
|
qDebug() << reason;
|
||||||
qDebug() << "- from: " << msg.from();
|
qDebug() << "- from: " << msg.from();
|
||||||
qDebug() << "- to: " << msg.to();
|
qDebug() << "- to: " << msg.to();
|
||||||
@ -214,157 +226,282 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re
|
|||||||
qDebug() << "==============================";
|
qDebug() << "==============================";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg)
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
|
||||||
{
|
void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) {
|
||||||
handleChatMessage(msg, false, true);
|
handleChatMessage(msg, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg)
|
void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) {
|
||||||
{
|
|
||||||
handleChatMessage(msg, true, true);
|
handleChatMessage(msg, true, true);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
|
std::optional<Shared::MessageInfo> Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) {
|
||||||
{
|
|
||||||
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
||||||
if (itr != pendingStateMessages.end()) {
|
if (itr != pendingStateMessages.end()) {
|
||||||
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
Shared::MessageInfo info(acc->name, itr->second, itr->first);
|
||||||
RosterItem* ri = acc->rh->getRosterItem(itr->second);
|
std::map<QString, QString>::const_iterator itrC = pendingCorrectionMessages.find(id);
|
||||||
if (ri != 0) {
|
if (itrC != pendingCorrectionMessages.end()) {
|
||||||
ri->changeMessage(id, cData);
|
if (itrC->second.size() > 0)
|
||||||
|
info.jid = itrC->second;
|
||||||
|
|
||||||
|
if (clear)
|
||||||
|
pendingCorrectionMessages.erase(itrC);
|
||||||
}
|
}
|
||||||
emit acc->changeMessage(itr->second, id, cData);
|
|
||||||
pendingStateMessages.erase(itr);
|
if (clear)
|
||||||
|
pendingStateMessages.erase(itr);
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage)
|
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) {
|
||||||
{
|
SHARED_UNUSED(jid);
|
||||||
|
adjustPendingMessage(id, {{"state", static_cast<uint>(Shared::Message::State::delivered)}}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) {
|
||||||
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
||||||
|
pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId));
|
||||||
prepareUpload(data, newMessage);
|
prepareUpload(data, newMessage);
|
||||||
} else {
|
} else {
|
||||||
performSending(data, newMessage);
|
performSending(data, originalId, newMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
|
void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) {
|
||||||
{
|
|
||||||
QString jid = data.getPenPalJid();
|
QString jid = data.getPenPalJid();
|
||||||
QString id = data.getId();
|
QString id = data.getId();
|
||||||
QString oob = data.getOutOfBandUrl();
|
qDebug() << "Sending message with id:" << id;
|
||||||
|
if (originalId.size() > 0)
|
||||||
|
qDebug() << "To replace the one with id:" << originalId;
|
||||||
|
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
bool sent = false;
|
if (newMessage && originalId.size() > 0)
|
||||||
QMap<QString, QVariant> changes;
|
newMessage = false;
|
||||||
|
|
||||||
QDateTime sendTime = QDateTime::currentDateTimeUtc();
|
QDateTime sendTime = QDateTime::currentDateTimeUtc();
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
std::pair<Shared::Message::State, QString> result = scheduleSending(data, sendTime, originalId);
|
||||||
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
|
data.setState(result.first);
|
||||||
|
data.setErrorText(result.second);
|
||||||
|
|
||||||
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId));
|
||||||
msg.setOriginId(id);
|
if (ri != nullptr) {
|
||||||
#endif
|
if (newMessage)
|
||||||
msg.setId(id);
|
|
||||||
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
|
||||||
msg.setOutOfBandUrl(oob);
|
|
||||||
msg.setReceiptRequested(true);
|
|
||||||
msg.setStamp(sendTime);
|
|
||||||
|
|
||||||
sent = acc->client.sendPacket(msg);
|
|
||||||
//sent = false;
|
|
||||||
|
|
||||||
if (sent) {
|
|
||||||
data.setState(Shared::Message::State::sent);
|
|
||||||
} else {
|
|
||||||
data.setState(Shared::Message::State::error);
|
|
||||||
data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
data.setState(Shared::Message::State::error);
|
|
||||||
data.setErrorText("You are is offline or reconnecting");
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message::State mstate = data.getState();
|
|
||||||
changes.insert("state", static_cast<uint>(mstate));
|
|
||||||
if (mstate == Shared::Message::State::error) {
|
|
||||||
changes.insert("errorText", data.getErrorText());
|
|
||||||
}
|
|
||||||
if (oob.size() > 0) {
|
|
||||||
changes.insert("outOfBandUrl", oob);
|
|
||||||
}
|
|
||||||
if (newMessage) {
|
|
||||||
data.setTime(sendTime);
|
|
||||||
}
|
|
||||||
changes.insert("stamp", sendTime);
|
|
||||||
|
|
||||||
if (ri != 0) {
|
|
||||||
if (newMessage) {
|
|
||||||
ri->appendMessageToArchive(data);
|
ri->appendMessageToArchive(data);
|
||||||
} else {
|
else
|
||||||
ri->changeMessage(id, changes);
|
ri->changeMessage(originalId.isEmpty() ? id : originalId, changes);
|
||||||
}
|
|
||||||
if (sent) {
|
if (data.getState() != Shared::Message::State::error) {
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
if (originalId.size() > 0)
|
||||||
|
pendingCorrectionMessages.insert(std::make_pair(id, originalId));
|
||||||
} else {
|
} else {
|
||||||
pendingStateMessages.erase(id);
|
pendingStateMessages.erase(id);
|
||||||
|
pendingCorrectionMessages.erase(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit acc->changeMessage(jid, id, changes);
|
emit acc->changeMessage(jid, originalId.isEmpty() ? id : originalId, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage)
|
std::pair<Shared::Message::State, QString> Core::MessageHandler::scheduleSending(
|
||||||
{
|
const Shared::Message& message,
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
const QDateTime& sendTime,
|
||||||
QString jid = data.getPenPalJid();
|
const QString& originalId
|
||||||
QString id = data.getId();
|
) {
|
||||||
RosterItem* ri = acc->rh->getRosterItem(jid);
|
if (acc->state != Shared::ConnectionState::connected)
|
||||||
if (!ri) {
|
return {Shared::Message::State::error, "You are is offline or reconnecting"};
|
||||||
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;
|
QXmppMessage msg = createPacket(message, sendTime, originalId);
|
||||||
}
|
QString id = msg.id();
|
||||||
QString path = data.getAttachPath();
|
#ifdef WITH_OMEMO
|
||||||
QString url = acc->network->getFileRemoteUrl(path);
|
if (message.getEncryption() == Shared::EncryptionProtocol::omemo2) {
|
||||||
if (url.size() != 0) {
|
QXmppTask<QXmppE2eeExtension::MessageEncryptResult> task = acc->om->encryptMessage(std::move(msg), std::nullopt);
|
||||||
sendMessageWithLocalUploadedFile(data, url, newMessage);
|
if (task.isFinished()) {
|
||||||
} else {
|
const QXmppE2eeExtension::MessageEncryptResult& res = task.result();
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
if (std::holds_alternative<std::unique_ptr<QXmppMessage>>(res)) {
|
||||||
if (newMessage) {
|
qDebug() << "Successfully encrypted a message";
|
||||||
ri->appendMessageToArchive(data);
|
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(res);
|
||||||
} else {
|
encrypted->setBody(QString());
|
||||||
QMap<QString, QVariant> changes({
|
encrypted->setOutOfBandUrl(QString());
|
||||||
{"state", (uint)Shared::Message::State::pending}
|
bool success = acc->client.sendPacket(*encrypted.get());
|
||||||
});
|
if (success) {
|
||||||
ri->changeMessage(id, changes);
|
qDebug() << "Successfully sent an encrypted message";
|
||||||
emit acc->changeMessage(jid, id, changes);
|
return {Shared::Message::State::sent, ""};
|
||||||
}
|
|
||||||
//this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file
|
|
||||||
if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
|
||||||
if (acc->um->serviceFound()) {
|
|
||||||
QFileInfo file(path);
|
|
||||||
if (file.exists() && file.isReadable()) {
|
|
||||||
pendingStateMessages.insert(std::make_pair(id, jid));
|
|
||||||
uploadingSlotsQueue.emplace_back(path, id);
|
|
||||||
if (uploadingSlotsQueue.size() == 1) {
|
|
||||||
acc->um->requestUploadSlot(file);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
|
|
||||||
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
|
qDebug() << "Couldn't sent an encrypted message";
|
||||||
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
return {Shared::Message::State::error, "Error sending successfully encrypted message"};
|
||||||
}
|
}
|
||||||
|
} else if (std::holds_alternative<QXmppError>(res)) {
|
||||||
|
qDebug() << "Couldn't encrypt a message";
|
||||||
|
const QXmppError& err = std::get<QXmppError>(res);
|
||||||
|
return {Shared::Message::State::error, err.description};
|
||||||
|
} else {
|
||||||
|
qDebug() << "Couldn't encrypt a message";
|
||||||
|
return {Shared::Message::State::error, "Unexpected error ecryptng the message"};
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) {
|
||||||
|
if (std::holds_alternative<std::unique_ptr<QXmppMessage>>(result)) {
|
||||||
|
qDebug() << "Successfully encrypted a message";
|
||||||
|
const std::unique_ptr<QXmppMessage>& encrypted = std::get<std::unique_ptr<QXmppMessage>>(result);
|
||||||
|
encrypted->setBody(QString());
|
||||||
|
encrypted->setOutOfBandUrl(QString());
|
||||||
|
bool success = acc->client.sendPacket(*encrypted.get());
|
||||||
|
if (success) {
|
||||||
|
qDebug() << "Successfully sent an encrypted message";
|
||||||
|
if (!adjustPendingMessage(id, stateSent, false))
|
||||||
|
qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate";
|
||||||
|
} else {
|
||||||
|
qDebug() << "Couldn't sent an encrypted message";
|
||||||
|
handlePendingMessageError(id, "Error sending successfully encrypted message");
|
||||||
|
}
|
||||||
|
} else if (std::holds_alternative<QXmppError>(result)) {
|
||||||
|
qDebug() << "Couldn't encrypt a message";
|
||||||
|
const QXmppError& err = std::get<QXmppError>(result);
|
||||||
|
handlePendingMessageError(id, err.description);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Couldn't encrypt a message";
|
||||||
|
handlePendingMessageError(id, "Unexpected error ecryptng the message");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {Shared::Message::State::pending, ""};
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
bool success = acc->client.sendPacket(msg);
|
||||||
|
if (success)
|
||||||
|
return {Shared::Message::State::sent, ""};
|
||||||
|
else
|
||||||
|
return {Shared::Message::State::error, "Error sending message, internal QXMPP error"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::MessageHandler::adjustPendingMessage(const QString& messageId, const QMap<QString, QVariant>& data, bool final) {
|
||||||
|
std::optional<Shared::MessageInfo> info = getOriginalPendingMessageId(messageId, final);
|
||||||
|
if (info) {
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(info->jid);
|
||||||
|
if (ri != nullptr)
|
||||||
|
ri->changeMessage(info->messageId, data);
|
||||||
|
|
||||||
|
emit acc->changeMessage(info->jid, info->messageId, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const {
|
||||||
|
QMap<QString, QVariant> changes;
|
||||||
|
|
||||||
|
QString oob = data.getOutOfBandUrl();
|
||||||
|
Shared::Message::State mstate = data.getState();
|
||||||
|
changes.insert("state", static_cast<uint>(mstate));
|
||||||
|
if (mstate == Shared::Message::State::error)
|
||||||
|
changes.insert("errorText", data.getErrorText());
|
||||||
|
|
||||||
|
if (oob.size() > 0)
|
||||||
|
changes.insert("outOfBandUrl", oob);
|
||||||
|
|
||||||
|
if (newMessage)
|
||||||
|
data.setTime(time);
|
||||||
|
|
||||||
|
if (originalId.size() > 0)
|
||||||
|
changes.insert("body", data.getBody());
|
||||||
|
|
||||||
|
changes.insert("stamp", time);
|
||||||
|
|
||||||
|
//sometimes (when the image is pasted with ctrl+v)
|
||||||
|
//I start sending message with one path, then copy it to downloads directory
|
||||||
|
//so, the final path changes. Let's assume it changes always since it costs me close to nothing
|
||||||
|
QString attachPath = data.getAttachPath();
|
||||||
|
if (attachPath.size() > 0) {
|
||||||
|
QString squawkified = Shared::squawkifyPath(attachPath);
|
||||||
|
changes.insert("attachPath", squawkified);
|
||||||
|
if (attachPath != squawkified)
|
||||||
|
data.setAttachPath(squawkified);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const {
|
||||||
|
QXmppMessage msg(QString(), data.getTo(), data.getBody(), data.getThread());
|
||||||
|
QString id(data.getId());
|
||||||
|
|
||||||
|
if (originalId.size() > 0)
|
||||||
|
msg.setReplaceId(originalId);
|
||||||
|
|
||||||
|
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
||||||
|
msg.setOriginId(id);
|
||||||
|
#endif
|
||||||
|
msg.setId(id);
|
||||||
|
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
|
||||||
|
msg.setOutOfBandUrl(data.getOutOfBandUrl());
|
||||||
|
msg.setReceiptRequested(true);
|
||||||
|
msg.setStamp(time);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) {
|
||||||
|
if (acc->state != Shared::ConnectionState::connected) {
|
||||||
handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting");
|
handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting");
|
||||||
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
|
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString jid = data.getPenPalJid();
|
||||||
|
QString id = data.getId();
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
|
if (ri == nullptr) {
|
||||||
|
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = data.getAttachPath();
|
||||||
|
QString url = acc->network->getFileRemoteUrl(path);
|
||||||
|
if (url.size() != 0)
|
||||||
|
return sendMessageWithLocalUploadedFile(data, url, newMessage);
|
||||||
|
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
if (newMessage) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
|
} else {
|
||||||
|
ri->changeMessage(id, statePending);
|
||||||
|
emit acc->changeMessage(jid, id, statePending);
|
||||||
|
}
|
||||||
|
|
||||||
|
//this checks if the file is already uploading, and if so it subscribes to it's success,
|
||||||
|
//So, I need to do stuff only if the network knows nothing of this file
|
||||||
|
if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!acc->um->serviceFound()) {
|
||||||
|
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
|
||||||
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo file(path);
|
||||||
|
if (file.exists() && file.isReadable()) {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
uploadingSlotsQueue.emplace_back(path, id);
|
||||||
|
if (uploadingSlotsQueue.size() == 1)
|
||||||
|
acc->um->requestUploadSlot(file);
|
||||||
|
} else {
|
||||||
|
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
|
||||||
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
|
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) {
|
||||||
{
|
|
||||||
if (uploadingSlotsQueue.size() == 0) {
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
|
||||||
} else {
|
} else {
|
||||||
@ -374,14 +511,12 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
|
|||||||
acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
||||||
|
|
||||||
uploadingSlotsQueue.pop_front();
|
uploadingSlotsQueue.pop_front();
|
||||||
if (uploadingSlotsQueue.size() > 0) {
|
if (uploadingSlotsQueue.size() > 0)
|
||||||
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) {
|
||||||
{
|
|
||||||
QString err(request.error().text());
|
QString err(request.error().text());
|
||||||
if (uploadingSlotsQueue.size() == 0) {
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
|
||||||
@ -392,94 +527,90 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques
|
|||||||
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
|
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
|
||||||
|
|
||||||
uploadingSlotsQueue.pop_front();
|
uploadingSlotsQueue.pop_front();
|
||||||
if (uploadingSlotsQueue.size() > 0) {
|
if (uploadingSlotsQueue.size() > 0)
|
||||||
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path) {
|
||||||
{
|
|
||||||
QMap<QString, QVariant> cData = {
|
QMap<QString, QVariant> cData = {
|
||||||
{"attachPath", path}
|
{"attachPath", path}
|
||||||
};
|
};
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
if (info.account == acc->getName()) {
|
if (info.account != acc->getName())
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
|
continue;
|
||||||
if (cnt != 0) {
|
|
||||||
if (cnt->changeMessage(info.messageId, cData)) {
|
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
|
||||||
emit acc->changeMessage(info.jid, info.messageId, cData);
|
if (cnt != nullptr) {
|
||||||
}
|
bool changed = cnt->changeMessage(info.messageId, cData);
|
||||||
}
|
if (changed)
|
||||||
|
emit acc->changeMessage(info.jid, info.messageId, cData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up)
|
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up) {
|
||||||
{
|
if (!up)
|
||||||
if (up) {
|
return;
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
|
||||||
if (info.account == acc->getName()) {
|
for (const Shared::MessageInfo& info : msgs)
|
||||||
handleUploadError(info.jid, info.messageId, text);
|
if (info.account == acc->getName())
|
||||||
}
|
handleUploadError(info.jid, info.messageId, text);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
|
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) {
|
||||||
{
|
|
||||||
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
||||||
pendingStateMessages.erase(jid);
|
pendingStateMessages.erase(messageId);
|
||||||
|
pendingCorrectionMessages.erase(messageId);
|
||||||
requestChangeMessage(jid, messageId, {
|
requestChangeMessage(jid, messageId, {
|
||||||
{"state", static_cast<uint>(Shared::Message::State::error)},
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
{"errorText", errorText}
|
{"errorText", errorText}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path) {
|
||||||
{
|
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
if (info.account == acc->getName()) {
|
if (info.account != acc->getName())
|
||||||
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
continue;
|
||||||
if (ri != 0) {
|
|
||||||
Shared::Message msg = ri->getMessage(info.messageId);
|
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
||||||
sendMessageWithLocalUploadedFile(msg, path, false);
|
if (ri != nullptr) {
|
||||||
} else {
|
Shared::Message msg = ri->getMessage(info.messageId);
|
||||||
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";
|
msg.setAttachPath(path);
|
||||||
}
|
sendMessageWithLocalUploadedFile(msg, url, 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)
|
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage) {
|
||||||
{
|
|
||||||
msg.setOutOfBandUrl(url);
|
msg.setOutOfBandUrl(url);
|
||||||
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
if (msg.getBody().size() == 0) //not sure why, but most messengers do that
|
||||||
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
||||||
}
|
|
||||||
performSending(msg, newMessage);
|
performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage);
|
||||||
//TODO removal/progress update
|
//TODO removal/progress update
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::set<QString> allowerToChangeKeys({
|
static const std::set<QString> allowedToChangeKeys({
|
||||||
"attachPath",
|
"attachPath",
|
||||||
"outOfBandUrl",
|
"outOfBandUrl",
|
||||||
"state",
|
"state",
|
||||||
"errorText"
|
"errorText"
|
||||||
});
|
});
|
||||||
|
|
||||||
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
|
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
if (cnt != 0) {
|
if (cnt != nullptr) {
|
||||||
bool allSupported = true;
|
bool allSupported = true;
|
||||||
QString unsupportedString;
|
QString unsupportedString;
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
||||||
if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
if (allowedToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
||||||
allSupported = false; //to make a message to look like if it was edited
|
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
|
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
|
break; //because the underlying tech assumes that
|
||||||
} //not by system
|
} //the change is initiated by user, not by system
|
||||||
}
|
}
|
||||||
if (allSupported) {
|
if (allSupported) {
|
||||||
cnt->changeMessage(messageId, data);
|
cnt->changeMessage(messageId, data);
|
||||||
@ -491,18 +622,23 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
|
void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) {
|
||||||
{
|
|
||||||
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
if (cnt != 0) {
|
if (cnt != nullptr) {
|
||||||
try {
|
try {
|
||||||
Shared::Message msg = cnt->getMessage(id);
|
Shared::Message msg = cnt->getMessage(id);
|
||||||
if (msg.getState() == Shared::Message::State::error) {
|
if (msg.getState() == Shared::Message::State::error) {
|
||||||
sendMessage(msg, false);
|
if (msg.getEdited()) {
|
||||||
|
QString originalId = msg.getId();
|
||||||
|
msg.generateRandomId();
|
||||||
|
sendMessage(msg, false, originalId);
|
||||||
|
} else {
|
||||||
|
sendMessage(msg, false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping";
|
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping";
|
||||||
}
|
}
|
||||||
} catch (const Archive::NotFound& err) {
|
} catch (const LMDBAL::NotFound& err) {
|
||||||
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
|
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,48 +16,49 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_MESSAGEHANDLER_H
|
#pragma once
|
||||||
#define CORE_MESSAGEHANDLER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <QXmppMessage.h>
|
#include <QXmppMessage.h>
|
||||||
#include <QXmppHttpUploadIq.h>
|
#include <QXmppHttpUploadIq.h>
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
#include <QXmppE2eeExtension.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
#include <shared/messageinfo.h>
|
#include <shared/messageinfo.h>
|
||||||
|
#include <shared/pathcheck.h>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo write docs
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Account;
|
class Account;
|
||||||
|
|
||||||
class MessageHandler : public QObject
|
class MessageHandler : public QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
MessageHandler(Account* account);
|
MessageHandler(Account* account);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void sendMessage(const Shared::Message& data, bool newMessage = true);
|
void sendMessage(const Shared::Message& data, bool newMessage = true, QString originalId = "");
|
||||||
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;
|
||||||
void resendMessage(const QString& jid, const QString& id);
|
void resendMessage(const QString& jid, const QString& id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onMessageReceived(const QXmppMessage& message);
|
void onMessageReceived(const QXmppMessage& message);
|
||||||
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0)
|
||||||
void onCarbonMessageReceived(const QXmppMessage& message);
|
void onCarbonMessageReceived(const QXmppMessage& message);
|
||||||
void onCarbonMessageSent(const QXmppMessage& message);
|
void onCarbonMessageSent(const QXmppMessage& message);
|
||||||
|
#endif
|
||||||
void onReceiptReceived(const QString& jid, const QString& id);
|
void onReceiptReceived(const QString& jid, const QString& id);
|
||||||
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
||||||
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
||||||
void onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
void onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
||||||
void onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& path, bool up);
|
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);
|
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
@ -66,16 +67,21 @@ private:
|
|||||||
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, bool newMessage = true);
|
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
|
||||||
void performSending(Shared::Message data, bool newMessage = true);
|
void performSending(Shared::Message data, const QString& originalId, bool newMessage = true);
|
||||||
void prepareUpload(const Shared::Message& data, bool newMessage = true);
|
void prepareUpload(const Shared::Message& data, bool newMessage = true);
|
||||||
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
|
||||||
|
QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const;
|
||||||
|
QMap<QString, QVariant> getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const;
|
||||||
|
std::optional<Shared::MessageInfo> getOriginalPendingMessageId(const QString& id, bool clear = true);
|
||||||
|
bool handlePendingMessageError(const QString& id, const QString& errorText);
|
||||||
|
std::pair<Shared::Message::State, QString> scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId);
|
||||||
|
bool adjustPendingMessage(const QString& messageId, const QMap<QString, QVariant>& data, bool final);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Account* acc;
|
Account* acc;
|
||||||
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
|
||||||
|
std::map<QString, QString> pendingCorrectionMessages; //key is new mesage, value is originalOne
|
||||||
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_MESSAGEHANDLER_H
|
|
||||||
|
285
core/handlers/omemohandler.cpp
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include "omemohandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
#include "core/adapterfunctions.h"
|
||||||
|
|
||||||
|
Core::OmemoHandler::OmemoHandler(Account* account) :
|
||||||
|
QObject(),
|
||||||
|
QXmppOmemoStorage(),
|
||||||
|
acc(account),
|
||||||
|
ownDevice(std::nullopt),
|
||||||
|
db(acc->getName() + "/omemo"),
|
||||||
|
meta(db.addCache<QString, QVariant>("meta")),
|
||||||
|
devices(db.addCache<QString, QHash<uint32_t, Device>>("devices")),
|
||||||
|
preKeyPairs(db.addCache<uint32_t, QByteArray>("preKeyPairs")),
|
||||||
|
signedPreKeyPairs(db.addCache<uint32_t, SignedPreKeyPair>("signedPreKeyPairs"))
|
||||||
|
{
|
||||||
|
db.open();
|
||||||
|
try {
|
||||||
|
QVariant own = meta->getRecord("ownDevice");
|
||||||
|
ownDevice = own.value<OwnDevice>();
|
||||||
|
qDebug() << "Successfully found own device omemo data for account" << acc->getName();
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
qDebug() << "No device omemo data was found for account" << acc->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::OmemoHandler::~OmemoHandler() {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::OmemoHandler::hasOwnDevice() {
|
||||||
|
return ownDevice.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QXmppOmemoStorage::OmemoData> Core::OmemoHandler::allData() {
|
||||||
|
OmemoData data;
|
||||||
|
data.ownDevice = ownDevice;
|
||||||
|
|
||||||
|
LMDBAL::Transaction txn = db.beginReadOnlyTransaction();
|
||||||
|
std::map<uint32_t, QByteArray> pkeys = preKeyPairs->readAll(txn);
|
||||||
|
for (const std::pair<const uint32_t, QByteArray>& pair : pkeys)
|
||||||
|
data.preKeyPairs.insert(pair.first, pair.second);
|
||||||
|
|
||||||
|
std::map<uint32_t, SignedPreKeyPair> spre = signedPreKeyPairs->readAll(txn);
|
||||||
|
for (const std::pair<const uint32_t, SignedPreKeyPair>& pair : spre) {
|
||||||
|
QXmppOmemoStorage::SignedPreKeyPair qxpair = {pair.second.first, pair.second.second};
|
||||||
|
data.signedPreKeyPairs.insert(pair.first, qxpair);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<QString, QHash<uint32_t, Device>> devs = devices->readAll(txn);
|
||||||
|
for (const std::pair<const QString, QHash<uint32_t, Device>>& pair : devs)
|
||||||
|
data.devices.insert(pair.first, pair.second);
|
||||||
|
|
||||||
|
return Core::makeReadyTask(std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) {
|
||||||
|
QHash<uint32_t, Device> devs;
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
bool had = true;
|
||||||
|
try {
|
||||||
|
devices->getRecord(jid, devs, txn);
|
||||||
|
} catch (const LMDBAL::NotFound& error) {
|
||||||
|
had = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
devs.insert(deviceId, device); //overwrites
|
||||||
|
if (had)
|
||||||
|
devices->changeRecord(jid, devs, txn);
|
||||||
|
else
|
||||||
|
devices->addRecord(jid, devs, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::addPreKeyPairs(const QHash<uint32_t, QByteArray>& keyPairs) {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
for (QHash<uint32_t, QByteArray>::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr)
|
||||||
|
preKeyPairs->forceRecord(itr.key(), itr.value(), txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) {
|
||||||
|
signedPreKeyPairs->forceRecord(keyId, std::make_pair(keyPair.creationDate, keyPair.data));
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) {
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
QHash<uint32_t, Device> devs = devices->getRecord(jid, txn);
|
||||||
|
devs.remove(deviceId);
|
||||||
|
if (devs.isEmpty())
|
||||||
|
devices->removeRecord(jid, txn);
|
||||||
|
else
|
||||||
|
devices->changeRecord(jid, devs, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::removeDevices(const QString& jid) {
|
||||||
|
devices->removeRecord(jid);
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::removePreKeyPair(uint32_t keyId) {
|
||||||
|
try {
|
||||||
|
preKeyPairs->removeRecord(keyId);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
qDebug() << "Couldn't remove preKeyPair " << e.what();
|
||||||
|
}
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) {
|
||||||
|
try {
|
||||||
|
signedPreKeyPairs->removeRecord(keyId);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::setOwnDevice(const std::optional<OwnDevice>& device) {
|
||||||
|
bool had = ownDevice.has_value();
|
||||||
|
ownDevice = device;
|
||||||
|
if (ownDevice.has_value()) {
|
||||||
|
if (had)
|
||||||
|
meta->changeRecord("ownDevice", QVariant::fromValue(ownDevice.value()));
|
||||||
|
else
|
||||||
|
meta->addRecord("ownDevice", QVariant::fromValue(ownDevice.value()));
|
||||||
|
} else if (had) {
|
||||||
|
meta->removeRecord("ownDevice");
|
||||||
|
}
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::OmemoHandler::resetAll() {
|
||||||
|
ownDevice = std::nullopt;
|
||||||
|
db.drop();
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::getDevices(const QString& jid, std::list<Shared::KeyInfo>& out) const {
|
||||||
|
QHash<uint32_t, Device> devs;
|
||||||
|
try {
|
||||||
|
devices->getRecord(jid, devs);
|
||||||
|
} catch (const LMDBAL::NotFound& error) {}
|
||||||
|
|
||||||
|
for (QHash<uint32_t, Device>::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) {
|
||||||
|
const Device& dev = itr.value();
|
||||||
|
out.emplace_back(
|
||||||
|
itr.key(),
|
||||||
|
dev.keyId,
|
||||||
|
dev.label,
|
||||||
|
dev.removalFromDeviceListDate,
|
||||||
|
Shared::TrustLevel::undecided,
|
||||||
|
Shared::EncryptionProtocol::omemo2,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::requestBundles(const QString& jid) {
|
||||||
|
QXmppTask<void> task = acc->om->buildMissingSessions({jid});
|
||||||
|
Contact* cnt = acc->rh->getContact(jid);
|
||||||
|
if (cnt)
|
||||||
|
cnt->omemoBundles = Shared::Possible::discovering;
|
||||||
|
|
||||||
|
task.then(this, std::bind(&OmemoHandler::onBundlesReceived, this, jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::requestOwnBundles() {
|
||||||
|
QXmppTask<void> task = acc->om->buildMissingSessions({acc->getBareJid()});
|
||||||
|
task.then(this, std::bind(&OmemoHandler::onOwnBundlesReceived, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::onBundlesReceived(const QString& jid) {
|
||||||
|
std::list<Shared::KeyInfo> keys = readKeys(jid);
|
||||||
|
|
||||||
|
Contact* cnt = acc->rh->getContact(jid);
|
||||||
|
if (cnt)
|
||||||
|
cnt->omemoBundles = Shared::Possible::present;
|
||||||
|
|
||||||
|
acc->delay->receivedBundles(jid, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::onOwnBundlesReceived() {
|
||||||
|
std::list<Shared::KeyInfo> keys = readKeys(acc->getBareJid());
|
||||||
|
if (ownDevice)
|
||||||
|
keys.emplace_front(
|
||||||
|
ownDevice->id,
|
||||||
|
ownDevice->publicIdentityKey,
|
||||||
|
ownDevice->label,
|
||||||
|
QDateTime::currentDateTime(),
|
||||||
|
Shared::TrustLevel::authenticated,
|
||||||
|
Shared::EncryptionProtocol::omemo2,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
acc->delay->receivedOwnBundles(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::KeyInfo> Core::OmemoHandler::readKeys(const QString& jid) {
|
||||||
|
std::list<Shared::KeyInfo> keys;
|
||||||
|
getDevices(jid, keys);
|
||||||
|
std::map<QByteArray, Shared::TrustLevel> trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid);
|
||||||
|
|
||||||
|
for (Shared::KeyInfo& key : keys) {
|
||||||
|
std::map<QByteArray, Shared::TrustLevel>::const_iterator itr = trustLevels.find(key.fingerPrint);
|
||||||
|
if (itr != trustLevels.end())
|
||||||
|
key.trustLevel = itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) {
|
||||||
|
SHARED_UNUSED(id);
|
||||||
|
qDebug() << "OMEMO device added for" << jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) {
|
||||||
|
in >> device.label;
|
||||||
|
in >> device.keyId;
|
||||||
|
in >> device.session;
|
||||||
|
in >> device.unrespondedSentStanzasCount;
|
||||||
|
in >> device.unrespondedReceivedStanzasCount;
|
||||||
|
in >> device.removalFromDeviceListDate;
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::Device& device) {
|
||||||
|
out << device.label;
|
||||||
|
out << device.keyId;
|
||||||
|
out << device.session;
|
||||||
|
out << device.unrespondedSentStanzasCount;
|
||||||
|
out << device.unrespondedReceivedStanzasCount;
|
||||||
|
out << device.removalFromDeviceListDate;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::OwnDevice& device) {
|
||||||
|
in >> device.id;
|
||||||
|
in >> device.label;
|
||||||
|
in >> device.privateIdentityKey;
|
||||||
|
in >> device.publicIdentityKey;
|
||||||
|
in >> device.latestSignedPreKeyId;
|
||||||
|
in >> device.latestPreKeyId;
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::OwnDevice& device) {
|
||||||
|
out << device.id;
|
||||||
|
out << device.label;
|
||||||
|
out << device.privateIdentityKey;
|
||||||
|
out << device.publicIdentityKey;
|
||||||
|
out << device.latestSignedPreKeyId;
|
||||||
|
out << device.latestPreKeyId;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
90
core/handlers/omemohandler.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QXmppOmemoStorage.h>
|
||||||
|
#include <cache.h>
|
||||||
|
|
||||||
|
#include <shared/keyinfo.h>
|
||||||
|
#include <shared/enums.h>
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice);
|
||||||
|
Q_DECLARE_METATYPE(QXmppOmemoStorage::Device);
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class OmemoHandler : public QObject, public QXmppOmemoStorage {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
typedef std::pair<QDateTime, QByteArray> SignedPreKeyPair;
|
||||||
|
|
||||||
|
OmemoHandler(Account* account);
|
||||||
|
~OmemoHandler() override;
|
||||||
|
|
||||||
|
virtual QXmppTask<OmemoData> allData() override;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> setOwnDevice(const std::optional<OwnDevice> &device) override;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override;
|
||||||
|
virtual QXmppTask<void> removeSignedPreKeyPair(uint32_t keyId) override;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> addPreKeyPairs(const QHash<uint32_t, QByteArray> &keyPairs) override;
|
||||||
|
virtual QXmppTask<void> removePreKeyPair(uint32_t keyId) override;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> addDevice(const QString &jid, uint32_t deviceId, const Device &device) override;
|
||||||
|
virtual QXmppTask<void> removeDevice(const QString &jid, uint32_t deviceId) override;
|
||||||
|
virtual QXmppTask<void> removeDevices(const QString &jid) override;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> resetAll() override;
|
||||||
|
|
||||||
|
bool hasOwnDevice();
|
||||||
|
|
||||||
|
void requestBundles(const QString& jid);
|
||||||
|
void requestOwnBundles();
|
||||||
|
void getDevices(const QString& jid, std::list<Shared::KeyInfo>& out) const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onOmemoDeviceAdded(const QString& jid, uint32_t id);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onBundlesReceived(const QString& jid);
|
||||||
|
void onOwnBundlesReceived();
|
||||||
|
std::list<Shared::KeyInfo> readKeys(const QString& jid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
std::optional<OwnDevice> ownDevice;
|
||||||
|
LMDBAL::Base db;
|
||||||
|
LMDBAL::Cache<QString, QVariant>* meta;
|
||||||
|
LMDBAL::Cache<QString, QHash<uint32_t, Device>>* devices;
|
||||||
|
LMDBAL::Cache<uint32_t, QByteArray>* preKeyPairs;
|
||||||
|
LMDBAL::Cache<uint32_t, SignedPreKeyPair>* signedPreKeyPairs;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::Device& device);
|
||||||
|
QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device);
|
||||||
|
|
||||||
|
QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device);
|
||||||
|
QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device);
|
@ -26,8 +26,9 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
|
|||||||
conferences(),
|
conferences(),
|
||||||
groups(),
|
groups(),
|
||||||
queuedContacts(),
|
queuedContacts(),
|
||||||
outOfRosterContacts()
|
outOfRosterContacts() {}
|
||||||
{
|
|
||||||
|
void Core::RosterHandler::initialize() {
|
||||||
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
||||||
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
||||||
connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved);
|
connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved);
|
||||||
@ -36,24 +37,30 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
|
|||||||
|
|
||||||
connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded);
|
connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded);
|
||||||
connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived);
|
connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived);
|
||||||
|
|
||||||
|
connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged);
|
||||||
|
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
connect(acc->th, &TrustHandler::trustLevelsChanged, this, &RosterHandler::onTrustChanged);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::RosterHandler::~RosterHandler()
|
Core::RosterHandler::~RosterHandler() {
|
||||||
{
|
clear();
|
||||||
for (std::map<QString, Contact*>::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) {
|
|
||||||
delete itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
|
|
||||||
delete itr->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onRosterReceived()
|
void Core::RosterHandler::clear() {
|
||||||
{
|
for (const std::pair<const QString, Contact*>& pair : contacts)
|
||||||
acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
|
delete pair.second;
|
||||||
acc->ownVCardRequestInProgress = true;
|
|
||||||
|
|
||||||
|
for (const std::pair<const QString, Conference*>& pair : conferences)
|
||||||
|
delete pair.second;
|
||||||
|
|
||||||
|
contacts.clear();
|
||||||
|
conferences.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onRosterReceived() {
|
||||||
QStringList bj = acc->rm->getRosterBareJids();
|
QStringList bj = acc->rm->getRosterBareJids();
|
||||||
for (int i = 0; i < bj.size(); ++i) {
|
for (int i = 0; i < bj.size(); ++i) {
|
||||||
const QString& jid = bj[i];
|
const QString& jid = bj[i];
|
||||||
@ -61,8 +68,7 @@ void Core::RosterHandler::onRosterReceived()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onRosterItemAdded(const QString& bareJid)
|
void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) {
|
||||||
{
|
|
||||||
QString lcJid = bareJid.toLower();
|
QString lcJid = bareJid.toLower();
|
||||||
addedAccount(lcJid);
|
addedAccount(lcJid);
|
||||||
std::map<QString, QString>::const_iterator itr = queuedContacts.find(lcJid);
|
std::map<QString, QString>::const_iterator itr = queuedContacts.find(lcJid);
|
||||||
@ -72,8 +78,7 @@ void Core::RosterHandler::onRosterItemAdded(const QString& bareJid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::addedAccount(const QString& jid)
|
void Core::RosterHandler::addedAccount(const QString& jid) {
|
||||||
{
|
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||||
QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid);
|
QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid);
|
||||||
Contact* contact;
|
Contact* contact;
|
||||||
@ -82,7 +87,6 @@ void Core::RosterHandler::addedAccount(const QString& jid)
|
|||||||
newContact = true;
|
newContact = true;
|
||||||
contact = new Contact(jid, acc->name);
|
contact = new Contact(jid, acc->name);
|
||||||
contacts.insert(std::make_pair(jid, contact));
|
contacts.insert(std::make_pair(jid, contact));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
contact = itr->second;
|
contact = itr->second;
|
||||||
}
|
}
|
||||||
@ -94,70 +98,44 @@ void Core::RosterHandler::addedAccount(const QString& jid)
|
|||||||
contact->setName(re.name());
|
contact->setName(re.name());
|
||||||
|
|
||||||
if (newContact) {
|
if (newContact) {
|
||||||
QMap<QString, QVariant> cData({
|
handleNewContact(contact);
|
||||||
{"name", re.name()},
|
QMap<QString, QVariant> cData = contact->getInfo();
|
||||||
{"state", QVariant::fromValue(state)}
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
});
|
cData.insert("trust", QVariant::fromValue(acc->th->getSummary(jid)));
|
||||||
|
#endif
|
||||||
careAboutAvatar(contact, cData);
|
|
||||||
int grCount = 0;
|
int grCount = 0;
|
||||||
for (QSet<QString>::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) {
|
for (const QString& groupName : gr) {
|
||||||
const QString& groupName = *itr;
|
|
||||||
addToGroup(jid, groupName);
|
addToGroup(jid, groupName);
|
||||||
emit acc->addContact(jid, groupName, cData);
|
emit acc->addContact(jid, groupName, cData);
|
||||||
grCount++;
|
grCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grCount == 0) {
|
if (grCount == 0)
|
||||||
emit acc->addContact(jid, "", cData);
|
emit acc->addContact(jid, "", cData);
|
||||||
|
|
||||||
|
if (acc->pepSupport == Shared::Support::supported) {
|
||||||
|
acc->dm->requestInfo(jid);
|
||||||
|
//acc->dm->requestItems(jid);
|
||||||
}
|
}
|
||||||
handleNewContact(contact);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin)
|
void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) {
|
||||||
{
|
|
||||||
QXmppMucRoom* room = acc->mm->addRoom(jid);
|
QXmppMucRoom* room = acc->mm->addRoom(jid);
|
||||||
QString lNick = nick;
|
QString lNick = nick;
|
||||||
if (lNick.size() == 0) {
|
if (lNick.size() == 0)
|
||||||
lNick = acc->getName();
|
lNick = acc->getName();
|
||||||
}
|
|
||||||
Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room);
|
Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room);
|
||||||
conferences.insert(std::make_pair(jid, conf));
|
conferences.insert(std::make_pair(jid, conf));
|
||||||
|
|
||||||
handleNewConference(conf);
|
handleNewConference(conf);
|
||||||
|
|
||||||
QMap<QString, QVariant> cData = {
|
QMap<QString, QVariant> cData = conf->getInfo();
|
||||||
{"autoJoin", conf->getAutoJoin()},
|
|
||||||
{"joined", conf->getJoined()},
|
|
||||||
{"nick", conf->getNick()},
|
|
||||||
{"name", conf->getName()},
|
|
||||||
{"avatars", conf->getAllAvatars()}
|
|
||||||
};
|
|
||||||
careAboutAvatar(conf, cData);
|
|
||||||
emit acc->addRoom(jid, cData);
|
emit acc->addRoom(jid, cData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data)
|
void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups) {
|
||||||
{
|
|
||||||
Archive::AvatarInfo info;
|
|
||||||
bool hasAvatar = item->readAvatarInfo(info);
|
|
||||||
if (hasAvatar) {
|
|
||||||
if (info.autogenerated) {
|
|
||||||
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
|
|
||||||
} else {
|
|
||||||
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
|
|
||||||
}
|
|
||||||
data.insert("avatarPath", item->avatarPath() + "." + info.type);
|
|
||||||
} else {
|
|
||||||
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
|
|
||||||
data.insert("avatarPath", "");
|
|
||||||
acc->requestVCard(item->jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups)
|
|
||||||
{
|
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
std::map<QString, QString>::const_iterator itr = queuedContacts.find(jid);
|
std::map<QString, QString>::const_iterator itr = queuedContacts.find(jid);
|
||||||
if (itr != queuedContacts.end()) {
|
if (itr != queuedContacts.end()) {
|
||||||
@ -171,8 +149,7 @@ void Core::RosterHandler::addContactRequest(const QString& jid, const QString& n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::removeContactRequest(const QString& jid)
|
void Core::RosterHandler::removeContactRequest(const QString& jid) {
|
||||||
{
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
if (acc->state == Shared::ConnectionState::connected) {
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
std::set<QString>::const_iterator itr = outOfRosterContacts.find(lcJid);
|
std::set<QString>::const_iterator itr = outOfRosterContacts.find(lcJid);
|
||||||
@ -187,25 +164,23 @@ void Core::RosterHandler::removeContactRequest(const QString& jid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact)
|
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) {
|
||||||
{
|
|
||||||
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
|
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
|
||||||
connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse);
|
connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse);
|
||||||
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
|
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
|
||||||
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
|
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
|
||||||
connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
|
connect(contact, &RosterItem::encryptionChanged, this, &RosterHandler::onContactEncryptionChanged);
|
||||||
|
connect(contact, &RosterItem::requestVCard, acc->delay, &DelayManager::Manager::getVCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::handleNewContact(Core::Contact* contact)
|
void Core::RosterHandler::handleNewContact(Core::Contact* contact) {
|
||||||
{
|
|
||||||
handleNewRosterItem(contact);
|
handleNewRosterItem(contact);
|
||||||
connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded);
|
connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded);
|
||||||
connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved);
|
connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved);
|
||||||
connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged);
|
connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::handleNewConference(Core::Conference* contact)
|
void Core::RosterHandler::handleNewConference(Core::Conference* contact) {
|
||||||
{
|
|
||||||
handleNewRosterItem(contact);
|
handleNewRosterItem(contact);
|
||||||
connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged);
|
connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged);
|
||||||
connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged);
|
connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged);
|
||||||
@ -216,34 +191,27 @@ void Core::RosterHandler::handleNewConference(Core::Conference* contact)
|
|||||||
connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant);
|
connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
|
void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->addRoomParticipant(room->jid, nickName, data);
|
emit acc->addRoomParticipant(room->jid, nickName, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
|
void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->changeRoomParticipant(room->jid, nickName, data);
|
emit acc->changeRoomParticipant(room->jid, nickName, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName)
|
void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) {
|
||||||
{
|
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->removeRoomParticipant(room->jid, nickName);
|
emit acc->removeRoomParticipant(room->jid, nickName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucSubjectChanged(const QString& subject)
|
void Core::RosterHandler::onMucSubjectChanged(const QString& subject) {
|
||||||
{
|
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->changeRoom(room->jid, {
|
emit acc->changeRoom(room->jid, {{"subject", subject}});
|
||||||
{"subject", subject}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactGroupAdded(const QString& group)
|
void Core::RosterHandler::onContactGroupAdded(const QString& group) {
|
||||||
{
|
|
||||||
Contact* contact = static_cast<Contact*>(sender());
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
if (contact->groupsCount() == 1) {
|
if (contact->groupsCount() == 1) {
|
||||||
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
||||||
@ -251,14 +219,17 @@ void Core::RosterHandler::onContactGroupAdded(const QString& group)
|
|||||||
|
|
||||||
QMap<QString, QVariant> cData({
|
QMap<QString, QVariant> cData({
|
||||||
{"name", contact->getName()},
|
{"name", contact->getName()},
|
||||||
{"state", QVariant::fromValue(contact->getSubscriptionState())}
|
{"state", QVariant::fromValue(contact->getSubscriptionState())},
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0)
|
||||||
|
{"trust", QVariant::fromValue(acc->th->getSummary(contact->jid))},
|
||||||
|
#endif
|
||||||
|
{"encryption", QVariant::fromValue(contact->encryption())}
|
||||||
});
|
});
|
||||||
addToGroup(contact->jid, group);
|
addToGroup(contact->jid, group);
|
||||||
emit acc->addContact(contact->jid, group, cData);
|
emit acc->addContact(contact->jid, group, cData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactGroupRemoved(const QString& group)
|
void Core::RosterHandler::onContactGroupRemoved(const QString& group) {
|
||||||
{
|
|
||||||
Contact* contact = static_cast<Contact*>(sender());
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
if (contact->groupsCount() == 0) {
|
if (contact->groupsCount() == 0) {
|
||||||
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
||||||
@ -268,26 +239,26 @@ void Core::RosterHandler::onContactGroupRemoved(const QString& group)
|
|||||||
removeFromGroup(contact->jid, group);
|
removeFromGroup(contact->jid, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactNameChanged(const QString& cname)
|
void Core::RosterHandler::onContactNameChanged(const QString& cname) {
|
||||||
{
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
Contact* contact = static_cast<Contact*>(sender());
|
emit acc->changeContact(contact->jid, {{"name", cname}});
|
||||||
QMap<QString, QVariant> cData({
|
|
||||||
{"name", cname},
|
|
||||||
});
|
|
||||||
emit acc->changeContact(contact->jid, cData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate)
|
void Core::RosterHandler::onContactEncryptionChanged(Shared::EncryptionProtocol value) {
|
||||||
{
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
Contact* contact = static_cast<Contact*>(sender());
|
emit acc->changeContact(contact->jid, {{"encryption", QVariant::fromValue(value)}});
|
||||||
QMap<QString, QVariant> cData({
|
|
||||||
{"state", QVariant::fromValue(cstate)},
|
|
||||||
});
|
|
||||||
emit acc->changeContact(contact->jid, cData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::addToGroup(const QString& jid, const QString& group)
|
void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) {
|
||||||
{
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
|
emit acc->changeContact(contact->jid, {{"state", QVariant::fromValue(cstate)}});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onTrustChanged(const QString& jid, const Shared::TrustSummary& trust) {
|
||||||
|
emit acc->changeContact(jid, {{"trust", QVariant::fromValue(trust)}});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) {
|
||||||
std::map<QString, std::set<QString>>::iterator gItr = groups.find(group);
|
std::map<QString, std::set<QString>>::iterator gItr = groups.find(group);
|
||||||
if (gItr == groups.end()) {
|
if (gItr == groups.end()) {
|
||||||
gItr = groups.insert(std::make_pair(group, std::set<QString>())).first;
|
gItr = groups.insert(std::make_pair(group, std::set<QString>())).first;
|
||||||
@ -296,8 +267,7 @@ void Core::RosterHandler::addToGroup(const QString& jid, const QString& group)
|
|||||||
gItr->second.insert(jid.toLower());
|
gItr->second.insert(jid.toLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group)
|
void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) {
|
||||||
{
|
|
||||||
QSet<QString> toRemove;
|
QSet<QString> toRemove;
|
||||||
std::map<QString, std::set<QString>>::iterator itr = groups.find(group);
|
std::map<QString, std::set<QString>>::iterator itr = groups.find(group);
|
||||||
if (itr == groups.end()) {
|
if (itr == groups.end()) {
|
||||||
@ -315,58 +285,53 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
Core::RosterItem* Core::RosterHandler::getRosterItem(const QString& jid) {
|
||||||
{
|
RosterItem* item = nullptr;
|
||||||
RosterItem* item = 0;
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
std::map<QString, Contact*>::const_iterator citr = contacts.find(lcJid);
|
std::map<QString, Contact*>::const_iterator citr = contacts.find(lcJid);
|
||||||
if (citr != contacts.end()) {
|
if (citr != contacts.end()) {
|
||||||
item = citr->second;
|
item = citr->second;
|
||||||
} else {
|
} else {
|
||||||
std::map<QString, Conference*>::const_iterator coitr = conferences.find(lcJid);
|
std::map<QString, Conference*>::const_iterator coitr = conferences.find(lcJid);
|
||||||
if (coitr != conferences.end()) {
|
if (coitr != conferences.end())
|
||||||
item = coitr->second;
|
item = coitr->second;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Conference * Core::RosterHandler::getConference(const QString& jid)
|
Core::Conference* Core::RosterHandler::getConference(const QString& jid) {
|
||||||
{
|
|
||||||
Conference* item = 0;
|
Conference* item = 0;
|
||||||
std::map<QString, Conference*>::const_iterator coitr = conferences.find(jid.toLower());
|
std::map<QString, Conference*>::const_iterator coitr = conferences.find(jid.toLower());
|
||||||
if (coitr != conferences.end()) {
|
if (coitr != conferences.end())
|
||||||
item = coitr->second;
|
item = coitr->second;
|
||||||
}
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Contact * Core::RosterHandler::getContact(const QString& jid)
|
Core::Contact* Core::RosterHandler::getContact(const QString& jid) {
|
||||||
{
|
|
||||||
Contact* item = 0;
|
Contact* item = 0;
|
||||||
std::map<QString, Contact*>::const_iterator citr = contacts.find(jid.toLower());
|
std::map<QString, Contact*>::const_iterator citr = contacts.find(jid.toLower());
|
||||||
if (citr != contacts.end()) {
|
if (citr != contacts.end())
|
||||||
item = citr->second;
|
item = citr->second;
|
||||||
}
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid)
|
Core::Contact* Core::RosterHandler::addOutOfRosterContact(const QString& jid) {
|
||||||
{
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
Contact* cnt = new Contact(lcJid, acc->name);
|
Contact* cnt = new Contact(lcJid, acc->name);
|
||||||
contacts.insert(std::make_pair(lcJid, cnt));
|
contacts.insert(std::make_pair(lcJid, cnt));
|
||||||
outOfRosterContacts.insert(lcJid);
|
outOfRosterContacts.insert(lcJid);
|
||||||
cnt->setSubscriptionState(Shared::SubscriptionState::unknown);
|
cnt->setSubscriptionState(Shared::SubscriptionState::unknown);
|
||||||
emit acc->addContact(lcJid, "", QMap<QString, QVariant>({
|
emit acc->addContact(lcJid, "", QMap<QString, QVariant>({
|
||||||
{"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}
|
{"state", QVariant::fromValue(Shared::SubscriptionState::unknown)},
|
||||||
|
{"encryption", QVariant::fromValue(Shared::EncryptionProtocol::none)}
|
||||||
}));
|
}));
|
||||||
handleNewContact(cnt);
|
handleNewContact(cnt);
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onRosterItemChanged(const QString& bareJid)
|
void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) {
|
||||||
{
|
|
||||||
QString lcJid = bareJid.toLower();
|
QString lcJid = bareJid.toLower();
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
if (itr == contacts.end()) {
|
if (itr == contacts.end()) {
|
||||||
@ -383,8 +348,7 @@ void Core::RosterHandler::onRosterItemChanged(const QString& bareJid)
|
|||||||
contact->setName(re.name());
|
contact->setName(re.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid)
|
void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) {
|
||||||
{
|
|
||||||
QString lcJid = bareJid.toLower();
|
QString lcJid = bareJid.toLower();
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
if (itr == contacts.end()) {
|
if (itr == contacts.end()) {
|
||||||
@ -394,22 +358,20 @@ void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid)
|
|||||||
Contact* contact = itr->second;
|
Contact* contact = itr->second;
|
||||||
contacts.erase(itr);
|
contacts.erase(itr);
|
||||||
QSet<QString> cGroups = contact->getGroups();
|
QSet<QString> cGroups = contact->getGroups();
|
||||||
for (QSet<QString>::const_iterator itr = cGroups.begin(), end = cGroups.end(); itr != end; ++itr) {
|
for (const QString& group : cGroups)
|
||||||
removeFromGroup(lcJid, *itr);
|
removeFromGroup(lcJid, group);
|
||||||
}
|
|
||||||
emit acc->removeContact(lcJid);
|
emit acc->removeContact(lcJid);
|
||||||
|
|
||||||
contact->deleteLater();
|
contact->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room)
|
void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) {
|
||||||
{
|
|
||||||
qDebug() << "room" << room->jid() << "added with name" << room->name()
|
qDebug() << "room" << room->jid() << "added with name" << room->name()
|
||||||
<< ", account" << acc->getName() << "joined:" << room->isJoined();
|
<< ", account" << acc->getName() << "joined:" << room->isJoined();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks)
|
void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) {
|
||||||
{
|
|
||||||
QList<QXmppBookmarkConference> confs = bookmarks.conferences();
|
QList<QXmppBookmarkConference> confs = bookmarks.conferences();
|
||||||
for (QList<QXmppBookmarkConference>::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) {
|
for (QList<QXmppBookmarkConference>::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) {
|
||||||
const QXmppBookmarkConference& c = *itr;
|
const QXmppBookmarkConference& c = *itr;
|
||||||
@ -419,54 +381,42 @@ void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks)
|
|||||||
if (cItr == conferences.end()) {
|
if (cItr == conferences.end()) {
|
||||||
addNewRoom(jid, c.nickName(), c.name(), c.autoJoin());
|
addNewRoom(jid, c.nickName(), c.name(), c.autoJoin());
|
||||||
} else {
|
} else {
|
||||||
if (c.autoJoin()) {
|
if (c.autoJoin())
|
||||||
cItr->second->setJoined(true);
|
cItr->second->setJoined(true);
|
||||||
} else {
|
else
|
||||||
cItr->second->setAutoJoin(false);
|
cItr->second->setAutoJoin(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucJoinedChanged(bool joined)
|
void Core::RosterHandler::onMucJoinedChanged(bool joined){
|
||||||
{
|
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->changeRoom(room->jid, {
|
emit acc->changeRoom(room->jid, {{"joined", joined}});
|
||||||
{"joined", joined}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin)
|
void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) {
|
||||||
{
|
|
||||||
storeConferences();
|
storeConferences();
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->changeRoom(room->jid, {
|
emit acc->changeRoom(room->jid, {{"autoJoin", autoJoin}});
|
||||||
{"autoJoin", autoJoin}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onMucNickNameChanged(const QString& nickName)
|
void Core::RosterHandler::onMucNickNameChanged(const QString& nickName){
|
||||||
{
|
|
||||||
storeConferences();
|
storeConferences();
|
||||||
Conference* room = static_cast<Conference*>(sender());
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
emit acc->changeRoom(room->jid, {
|
emit acc->changeRoom(room->jid, {{"nick", nickName}});
|
||||||
{"nick", nickName}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs)
|
Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs){
|
||||||
{
|
|
||||||
Shared::SubscriptionState state;
|
Shared::SubscriptionState state;
|
||||||
if (qs == QXmppRosterIq::Item::NotSet) {
|
if (qs == QXmppRosterIq::Item::NotSet)
|
||||||
state = Shared::SubscriptionState::unknown;
|
state = Shared::SubscriptionState::unknown;
|
||||||
} else {
|
else
|
||||||
state = static_cast<Shared::SubscriptionState>(qs);
|
state = static_cast<Shared::SubscriptionState>(qs);
|
||||||
}
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::storeConferences()
|
void Core::RosterHandler::storeConferences() {
|
||||||
{
|
|
||||||
QXmppBookmarkSet bms = acc->bm->bookmarks();
|
QXmppBookmarkSet bms = acc->bm->bookmarks();
|
||||||
QList<QXmppBookmarkConference> confs;
|
QList<QXmppBookmarkConference> confs;
|
||||||
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
|
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
|
||||||
@ -482,8 +432,7 @@ void Core::RosterHandler::storeConferences()
|
|||||||
acc->bm->setBookmarks(bms);
|
acc->bm->setBookmarks(bms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::clearConferences()
|
void Core::RosterHandler::clearConferences() {
|
||||||
{
|
|
||||||
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) {
|
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) {
|
||||||
itr->second->deleteLater();
|
itr->second->deleteLater();
|
||||||
emit acc->removeRoom(itr->first);
|
emit acc->removeRoom(itr->first);
|
||||||
@ -491,21 +440,20 @@ void Core::RosterHandler::clearConferences()
|
|||||||
conferences.clear();
|
conferences.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::removeRoomRequest(const QString& jid)
|
void Core::RosterHandler::removeRoomRequest(const QString& jid) {
|
||||||
{
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
std::map<QString, Conference*>::const_iterator itr = conferences.find(lcJid);
|
std::map<QString, Conference*>::const_iterator itr = conferences.find(lcJid);
|
||||||
if (itr == conferences.end()) {
|
if (itr == conferences.end())
|
||||||
qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping";
|
qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping";
|
||||||
}
|
|
||||||
itr->second->deleteLater();
|
itr->second->deleteLater();
|
||||||
conferences.erase(itr);
|
conferences.erase(itr);
|
||||||
emit acc->removeRoom(lcJid);
|
emit acc->removeRoom(lcJid);
|
||||||
storeConferences();
|
storeConferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin)
|
void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) {
|
||||||
{
|
SHARED_UNUSED(password);
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
std::map<QString, Conference*>::const_iterator cItr = conferences.find(lcJid);
|
std::map<QString, Conference*>::const_iterator cItr = conferences.find(lcJid);
|
||||||
if (cItr == conferences.end()) {
|
if (cItr == conferences.end()) {
|
||||||
@ -516,8 +464,7 @@ void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName)
|
void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) {
|
||||||
{
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
if (itr == contacts.end()) {
|
if (itr == contacts.end()) {
|
||||||
@ -541,45 +488,43 @@ void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
|
void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) {
|
||||||
{
|
|
||||||
QString lcJid = jid.toLower();
|
QString lcJid = jid.toLower();
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
if (itr == contacts.end()) {
|
if (itr == contacts.end()) {
|
||||||
qDebug() << "An attempt to remove non existing contact" << lcJid << "of account"
|
qDebug() << "An attempt to remove non existing contact" << lcJid << "of account"
|
||||||
<< acc->name << "from the group" << groupName << ", skipping";
|
<< acc->name << "from the group" << groupName << ", skipping";
|
||||||
} else {
|
return;
|
||||||
QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
|
}
|
||||||
QSet<QString> groups = item.groups();
|
|
||||||
QSet<QString>::const_iterator gItr = groups.find(groupName);
|
|
||||||
if (gItr != groups.end()) {
|
|
||||||
groups.erase(gItr);
|
|
||||||
item.setGroups(groups);
|
|
||||||
|
|
||||||
QXmppRosterIq iq;
|
QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
|
||||||
iq.setType(QXmppIq::Set);
|
QSet<QString> groups = item.groups();
|
||||||
iq.addItem(item);
|
QSet<QString>::const_iterator gItr = groups.find(groupName);
|
||||||
acc->client.sendPacket(iq);
|
if (gItr != groups.end()) {
|
||||||
} else {
|
groups.erase(gItr);
|
||||||
qDebug() << "An attempt to remove contact" << lcJid << "of account"
|
item.setGroups(groups);
|
||||||
<< acc->name << "from the group" << groupName << "but it's not in that group, skipping";
|
|
||||||
}
|
QXmppRosterIq iq;
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
iq.addItem(item);
|
||||||
|
acc->client.sendPacket(iq);
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to remove contact" << lcJid << "of account"
|
||||||
|
<< acc->name << "from the group" << groupName << "but it's not in that group, skipping";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path)
|
void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) {
|
||||||
{
|
|
||||||
RosterItem* item = static_cast<RosterItem*>(sender());
|
RosterItem* item = static_cast<RosterItem*>(sender());
|
||||||
QMap<QString, QVariant> cData({
|
QMap<QString, QVariant> cData({
|
||||||
{"avatarState", static_cast<uint>(type)},
|
{"avatarState", QVariant::fromValue(type)},
|
||||||
{"avatarPath", path}
|
{"avatarPath", path}
|
||||||
});
|
});
|
||||||
|
|
||||||
emit acc->changeContact(item->jid, cData);
|
emit acc->changeContact(item->jid, cData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterHandler::handleOffline()
|
void Core::RosterHandler::handleOffline() {
|
||||||
{
|
|
||||||
for (const std::pair<const QString, Conference*>& pair : conferences) {
|
for (const std::pair<const QString, Conference*>& pair : conferences) {
|
||||||
pair.second->clearArchiveRequests();
|
pair.second->clearArchiveRequests();
|
||||||
pair.second->downgradeDatabaseState();
|
pair.second->downgradeDatabaseState();
|
||||||
@ -589,3 +534,12 @@ void Core::RosterHandler::handleOffline()
|
|||||||
pair.second->downgradeDatabaseState();
|
pair.second->downgradeDatabaseState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) {
|
||||||
|
if (support == Shared::Support::supported) {
|
||||||
|
for (const std::pair<const QString, Contact*>& pair : contacts) {
|
||||||
|
if (pair.second->getPepSupport() == Shared::Support::unknown)
|
||||||
|
acc->dm->requestInfo(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_ROSTERHANDLER_H
|
#pragma once
|
||||||
#define CORE_ROSTERHANDLER_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@ -27,22 +26,24 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
#include <QXmppMucManager.h>
|
#include <QXmppMucManager.h>
|
||||||
#include <QXmppRosterIq.h>
|
#include <QXmppRosterIq.h>
|
||||||
|
|
||||||
|
#include <shared/enums.h>
|
||||||
#include <shared/message.h>
|
#include <shared/message.h>
|
||||||
|
#include <shared/trustsummary.h>
|
||||||
|
|
||||||
#include <core/contact.h>
|
#include <core/contact.h>
|
||||||
#include <core/conference.h>
|
#include <core/conference.h>
|
||||||
|
#include <core/delayManager/manager.h>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
|
||||||
class Account;
|
class Account;
|
||||||
|
|
||||||
class RosterHandler : public QObject
|
class RosterHandler : public QObject {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
RosterHandler(Account* account);
|
RosterHandler(Account* account);
|
||||||
@ -65,11 +66,15 @@ public:
|
|||||||
void storeConferences();
|
void storeConferences();
|
||||||
void clearConferences();
|
void clearConferences();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void clear();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRosterReceived();
|
void onRosterReceived();
|
||||||
void onRosterItemAdded(const QString& bareJid);
|
void onRosterItemAdded(const QString& bareJid);
|
||||||
void onRosterItemChanged(const QString& bareJid);
|
void onRosterItemChanged(const QString& bareJid);
|
||||||
void onRosterItemRemoved(const QString& bareJid);
|
void onRosterItemRemoved(const QString& bareJid);
|
||||||
|
void onTrustChanged(const QString& jid, const Shared::TrustSummary& trust);
|
||||||
|
|
||||||
void onMucRoomAdded(QXmppMucRoom* room);
|
void onMucRoomAdded(QXmppMucRoom* room);
|
||||||
void onMucJoinedChanged(bool joined);
|
void onMucJoinedChanged(bool joined);
|
||||||
@ -87,6 +92,8 @@ private slots:
|
|||||||
void onContactNameChanged(const QString& name);
|
void onContactNameChanged(const QString& name);
|
||||||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||||
|
void onContactEncryptionChanged(Shared::EncryptionProtocol value);
|
||||||
|
void onPepSupportedChanged(Shared::Support support);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin);
|
void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin);
|
||||||
@ -96,7 +103,6 @@ private:
|
|||||||
void handleNewRosterItem(Core::RosterItem* contact);
|
void handleNewRosterItem(Core::RosterItem* contact);
|
||||||
void handleNewContact(Core::Contact* contact);
|
void handleNewContact(Core::Contact* contact);
|
||||||
void handleNewConference(Core::Conference* contact);
|
void handleNewConference(Core::Conference* contact);
|
||||||
void careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data);
|
|
||||||
|
|
||||||
static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs);
|
static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs);
|
||||||
|
|
||||||
@ -110,5 +116,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_ROSTERHANDLER_H
|
|
||||||
|
463
core/handlers/trusthandler.cpp
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "trusthandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
#include "core/adapterfunctions.h"
|
||||||
|
|
||||||
|
Core::TrustHandler::TrustHandler(Account* account):
|
||||||
|
QObject(),
|
||||||
|
QXmppTrustStorage(),
|
||||||
|
acc(account),
|
||||||
|
db(acc->getName() + "/trust"),
|
||||||
|
protocols(db.createDirectory() + "/protocols"),
|
||||||
|
securityPolicies(db.addCache<QString, uint8_t>("securityPolicies")),
|
||||||
|
ownKeys(db.addCache<QString, QByteArray>("ownKeys")),
|
||||||
|
keysByProtocol()
|
||||||
|
{
|
||||||
|
if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) //never supposed to happen since I have just created a directory;
|
||||||
|
throw LMDBAL::Directory(protocols.fileName().toStdString());
|
||||||
|
|
||||||
|
QTextStream in(&protocols);
|
||||||
|
while(!in.atEnd()) {
|
||||||
|
QString protocol = in.readLine();
|
||||||
|
|
||||||
|
if (protocol.size() > 1) { //I'm afraid of reading some nonsence like separately standing \n or EF or BOM, so... let it be at least 2 chars long
|
||||||
|
KeyCache* cache = db.addCache<QString, Keys>(protocol.toStdString());
|
||||||
|
keysByProtocol.insert(std::make_pair(protocol, cache));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocols.close();
|
||||||
|
db.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::TrustHandler::~TrustHandler() {
|
||||||
|
protocols.close();
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) {
|
||||||
|
std::map<QString, KeyCache*>::iterator itr = keysByProtocol.find(encryption);
|
||||||
|
if (itr == keysByProtocol.end())
|
||||||
|
return createNewCache(encryption);
|
||||||
|
else
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) {
|
||||||
|
db.close();
|
||||||
|
KeyCache* cache = db.addCache<QString, Keys>(encryption.toStdString());
|
||||||
|
keysByProtocol.insert(std::make_pair(encryption, cache));
|
||||||
|
|
||||||
|
if (!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
|
||||||
|
throw LMDBAL::Directory(protocols.fileName().toStdString());
|
||||||
|
|
||||||
|
QTextStream out(&protocols);
|
||||||
|
out << encryption + "\n";
|
||||||
|
protocols.close();
|
||||||
|
|
||||||
|
db.open();
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::resetAll(const QString& encryption) {
|
||||||
|
securityPolicies->removeRecord(encryption);
|
||||||
|
ownKeys->removeRecord(encryption);
|
||||||
|
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
|
||||||
|
if (itr == keysByProtocol.end())
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
KeyCache* cache = itr->second;
|
||||||
|
std::map<QString, Keys> keys = cache->readAll(txn);
|
||||||
|
cache->drop(txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
for (const std::pair<const QString, Keys>& pair : keys) {
|
||||||
|
bool empty = true;
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : pair.second) {
|
||||||
|
if (trust.second != Shared::TrustLevel::undecided) {
|
||||||
|
empty = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty)
|
||||||
|
emit trustLevelsChanged(pair.first, getSummary(pair.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QXmpp::TrustLevel> Core::TrustHandler::trustLevel(
|
||||||
|
const QString& encryption,
|
||||||
|
const QString& keyOwnerJid,
|
||||||
|
const QByteArray& keyId
|
||||||
|
) {
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
Shared::TrustLevel level = Shared::TrustLevel::undecided;
|
||||||
|
try {
|
||||||
|
Keys map = cache->getRecord(keyOwnerJid);
|
||||||
|
Keys::const_iterator itr = map.find(keyId);
|
||||||
|
if (itr != map.end())
|
||||||
|
level = itr->second;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask(std::move(convert(level)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QHash<QString, QMultiHash<QString, QByteArray>>> Core::TrustHandler::setTrustLevel(
|
||||||
|
const QString& encryption,
|
||||||
|
const QList<QString>& keyOwnerJids,
|
||||||
|
QXmpp::TrustLevel oldTrustLevel,
|
||||||
|
QXmpp::TrustLevel newTrustLevel
|
||||||
|
) {
|
||||||
|
QHash<QString, QMultiHash<QString, QByteArray>> modifiedKeys;
|
||||||
|
Shared::TrustLevel oldLevel = convert(oldTrustLevel);
|
||||||
|
Shared::TrustLevel newLevel = convert(newTrustLevel);
|
||||||
|
std::set<QString> modifiedJids;
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
for (const QString& keyOwnerJid : keyOwnerJids) {
|
||||||
|
Keys map = cache->getRecord(keyOwnerJid, txn);
|
||||||
|
uint count = 0;
|
||||||
|
for (std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
|
||||||
|
Shared::TrustLevel& current = pair.second;
|
||||||
|
if (current == oldLevel) {
|
||||||
|
current = newLevel;
|
||||||
|
modifiedKeys[encryption].insert(keyOwnerJid, pair.first);
|
||||||
|
modifiedJids.insert(keyOwnerJid);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
cache->changeRecord(keyOwnerJid, map, txn);
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
for (const QString& jid : modifiedJids)
|
||||||
|
emit trustLevelsChanged(jid, getSummary(jid));
|
||||||
|
|
||||||
|
return Core::makeReadyTask(std::move(modifiedKeys));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QHash<QString, QMultiHash<QString, QByteArray>>> Core::TrustHandler::setTrustLevel(
|
||||||
|
const QString& encryption,
|
||||||
|
const QMultiHash<QString, QByteArray>& keyIds,
|
||||||
|
QXmpp::TrustLevel trustLevel
|
||||||
|
) {
|
||||||
|
QHash<QString, QMultiHash<QString, QByteArray>> modifiedKeys;
|
||||||
|
Shared::TrustLevel level = convert(trustLevel);
|
||||||
|
std::set<QString> modifiedJids;
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) {
|
||||||
|
const QString& keyOwnerJid = itr.key();
|
||||||
|
const QByteArray& keyId = itr.value();
|
||||||
|
try {
|
||||||
|
Keys map = cache->getRecord(keyOwnerJid, txn);
|
||||||
|
std::pair<Keys::iterator, bool> result = map.insert(std::make_pair(keyId, level));
|
||||||
|
bool changed = result.second;
|
||||||
|
if (!changed && result.first->second != level) {
|
||||||
|
result.first->second = level;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
modifiedKeys[encryption].insert(keyOwnerJid, keyId);
|
||||||
|
modifiedJids.insert(keyOwnerJid);
|
||||||
|
cache->changeRecord(keyOwnerJid, map, txn);
|
||||||
|
}
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
Keys map({{keyId, level}});
|
||||||
|
modifiedKeys[encryption].insert(keyOwnerJid, keyId);
|
||||||
|
modifiedJids.insert(keyOwnerJid);
|
||||||
|
cache->addRecord(keyOwnerJid, map, txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
for (const QString& jid : modifiedJids)
|
||||||
|
emit trustLevelsChanged(jid, getSummary(jid));
|
||||||
|
|
||||||
|
return Core::makeReadyTask(std::move(modifiedKeys));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<bool> Core::TrustHandler::hasKey(
|
||||||
|
const QString& encryption,
|
||||||
|
const QString& keyOwnerJid,
|
||||||
|
QXmpp::TrustLevels trustLevels
|
||||||
|
) {
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
bool found = false;
|
||||||
|
try {
|
||||||
|
Keys map = cache->getRecord(keyOwnerJid);
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
|
||||||
|
if (trustLevels.testFlag(convert(pair.second))) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask(std::move(found));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QHash<QString, QHash<QByteArray, QXmpp::TrustLevel>>> Core::TrustHandler::keys(
|
||||||
|
const QString& encryption,
|
||||||
|
const QList<QString>& keyOwnerJids,
|
||||||
|
QXmpp::TrustLevels trustLevels
|
||||||
|
) {
|
||||||
|
HSHBTL res;
|
||||||
|
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
for (const QString& keyOwnerJid : keyOwnerJids) {
|
||||||
|
try {
|
||||||
|
Keys map = cache->getRecord(keyOwnerJid);
|
||||||
|
QHash<QByteArray, QXmpp::TrustLevel>& pRes = res[keyOwnerJid];
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : map) {
|
||||||
|
QXmpp::TrustLevel level = convert(pair.second);
|
||||||
|
if (!trustLevels || trustLevels.testFlag(level)) {
|
||||||
|
pRes.insert(pair.first, level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
}
|
||||||
|
return Core::makeReadyTask(std::move(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QHash<QXmpp::TrustLevel, QMultiHash<QString, QByteArray>>> Core::TrustHandler::keys(
|
||||||
|
const QString& encryption,
|
||||||
|
QXmpp::TrustLevels trustLevels
|
||||||
|
) {
|
||||||
|
QHash<TL, MultySB> res;
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
std::map<QString, Keys> storage = cache->readAll();
|
||||||
|
for (const std::pair<const QString, Keys>& value : storage) {
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& pair : value.second) {
|
||||||
|
QXmpp::TrustLevel level = convert(pair.second);
|
||||||
|
if (!trustLevels || trustLevels.testFlag(level))
|
||||||
|
res[level].insert(value.first, pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Core::makeReadyTask(std::move(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption) {
|
||||||
|
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
|
||||||
|
if (itr == keysByProtocol.end())
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
KeyCache* cache = itr->second;
|
||||||
|
std::map<QString, Keys> keys = cache->readAll(txn);
|
||||||
|
cache->drop(txn);
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
for (const std::pair<const QString, Keys>& pair : keys) {
|
||||||
|
bool empty = true;
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : pair.second) {
|
||||||
|
if (trust.second != Shared::TrustLevel::undecided) {
|
||||||
|
empty = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty)
|
||||||
|
emit trustLevelsChanged(pair.first, getSummary(pair.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) {
|
||||||
|
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(encryption);
|
||||||
|
if (itr == keysByProtocol.end())
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
|
||||||
|
KeyCache* cache = itr->second;
|
||||||
|
try {
|
||||||
|
cache->removeRecord(keyOwnerJid);
|
||||||
|
emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change
|
||||||
|
} catch (const LMDBAL::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys
|
||||||
|
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::removeKeys(const QString& encryption, const QList<QByteArray>& keyIds) {
|
||||||
|
std::set<QByteArray> set;
|
||||||
|
for (const QByteArray& keyId : keyIds)
|
||||||
|
set.insert(keyId);
|
||||||
|
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
std::map<QString, Keys> data = cache->readAll(txn);
|
||||||
|
std::set<QString> modifiedJids;
|
||||||
|
|
||||||
|
for (std::map<QString, Keys>::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) {
|
||||||
|
Keys& byOwner = cItr->second;
|
||||||
|
for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) {
|
||||||
|
const QByteArray& keyId = itr->first;
|
||||||
|
if (set.erase(keyId)) {
|
||||||
|
byOwner.erase(itr++);
|
||||||
|
modifiedJids.insert(cItr->first);
|
||||||
|
} else
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
if (byOwner.size() > 0)
|
||||||
|
data.erase(cItr++);
|
||||||
|
else
|
||||||
|
++cItr;
|
||||||
|
}
|
||||||
|
if (modifiedJids.size() > 0)
|
||||||
|
cache->replaceAll(data, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
for (const QString& jid : modifiedJids)
|
||||||
|
emit trustLevelsChanged(jid, getSummary(jid));
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::addKeys(
|
||||||
|
const QString& encryption,
|
||||||
|
const QString& keyOwnerJid,
|
||||||
|
const QList<QByteArray>& keyIds,
|
||||||
|
QXmpp::TrustLevel trustLevel
|
||||||
|
) {
|
||||||
|
KeyCache* cache = getCache(encryption);
|
||||||
|
Shared::TrustLevel level = convert(trustLevel);
|
||||||
|
Keys data;
|
||||||
|
bool had = false;
|
||||||
|
LMDBAL::WriteTransaction txn = db.beginTransaction();
|
||||||
|
try {
|
||||||
|
data = cache->getRecord(keyOwnerJid, txn);
|
||||||
|
had = true;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
for (const QByteArray& keyId : keyIds) {
|
||||||
|
std::pair<Keys::iterator, bool> result = data.insert(std::make_pair(keyId, level));
|
||||||
|
if (!result.second)
|
||||||
|
result.first->second = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (had)
|
||||||
|
cache->changeRecord(keyOwnerJid, data, txn);
|
||||||
|
else
|
||||||
|
cache->addRecord(keyOwnerJid, data, txn);
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid));
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QByteArray> Core::TrustHandler::ownKey(const QString& encryption) {
|
||||||
|
QByteArray res;
|
||||||
|
try {
|
||||||
|
res = ownKeys->getRecord(encryption);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask(std::move(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::resetOwnKey(const QString& encryption) {
|
||||||
|
try {
|
||||||
|
ownKeys->removeRecord(encryption);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) {
|
||||||
|
ownKeys->forceRecord(encryption, keyId);
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<QXmpp::TrustSecurityPolicy> Core::TrustHandler::securityPolicy(const QString& encryption) {
|
||||||
|
QXmpp::TrustSecurityPolicy res;
|
||||||
|
try {
|
||||||
|
res = static_cast<QXmpp::TrustSecurityPolicy>(securityPolicies->getRecord(encryption));
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask(std::move(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::resetSecurityPolicy(const QString& encryption) {
|
||||||
|
try {
|
||||||
|
securityPolicies->removeRecord(encryption);
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
QXmppTask<void> Core::TrustHandler::setSecurityPolicy(
|
||||||
|
const QString& encryption,
|
||||||
|
QXmpp::TrustSecurityPolicy securityPolicy)
|
||||||
|
{
|
||||||
|
uint8_t pol = securityPolicy;
|
||||||
|
securityPolicies->forceRecord(encryption, pol);
|
||||||
|
|
||||||
|
return Core::makeReadyTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const {
|
||||||
|
const QString& prt = Shared::TrustSummary::protocolKeys.at(protocol);
|
||||||
|
std::map<QString, KeyCache*>::const_iterator itr = keysByProtocol.find(prt);
|
||||||
|
if (itr != keysByProtocol.end()) {
|
||||||
|
try {
|
||||||
|
Keys map = itr->second->getRecord(jid);
|
||||||
|
return map;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
return Keys();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Keys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const {
|
||||||
|
Shared::TrustSummary result;
|
||||||
|
for (const std::pair<const QString, KeyCache*>& pair : keysByProtocol) {
|
||||||
|
try {
|
||||||
|
Keys keys = pair.second->getRecord(jid);
|
||||||
|
Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first);
|
||||||
|
for (const std::pair<const QByteArray, Shared::TrustLevel>& trust : keys)
|
||||||
|
result.increment(protocol, trust.second);
|
||||||
|
|
||||||
|
} catch (const LMDBAL::NotFound& e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) {
|
||||||
|
switch (level) {
|
||||||
|
case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided;
|
||||||
|
case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted;
|
||||||
|
case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::manuallyDistrusted;
|
||||||
|
case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted;
|
||||||
|
case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted;
|
||||||
|
case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated;
|
||||||
|
default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) {
|
||||||
|
switch (level) {
|
||||||
|
case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided;
|
||||||
|
case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted;
|
||||||
|
case Shared::TrustLevel::manuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted;
|
||||||
|
case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted;
|
||||||
|
case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted;
|
||||||
|
case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated;
|
||||||
|
default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning
|
||||||
|
}
|
||||||
|
}
|
89
core/handlers/trusthandler.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CORE_TRUSTHANDLER_H
|
||||||
|
#define CORE_TRUSTHANDLER_H
|
||||||
|
|
||||||
|
#include <shared/enums.h>
|
||||||
|
#include <shared/trustsummary.h>
|
||||||
|
|
||||||
|
#include <QXmppTrustStorage.h>
|
||||||
|
#include <cache.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class TrustHandler : public QObject, public QXmppTrustStorage {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TrustHandler(Account* account);
|
||||||
|
~TrustHandler();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void trustLevelsChanged(const QString& jid, const Shared::TrustSummary& summary) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef QMultiHash<QString, QByteArray> MultySB;
|
||||||
|
typedef QHash<QString, MultySB> HashSM;
|
||||||
|
typedef const QList<QString>& CLSR;
|
||||||
|
typedef const QList<QByteArray>& CLBAR;
|
||||||
|
typedef const QString& CSR;
|
||||||
|
typedef QXmpp::TrustLevel TL;
|
||||||
|
typedef QHash<QString, QHash<QByteArray, TL>> HSHBTL;
|
||||||
|
|
||||||
|
typedef std::map<QByteArray, Shared::TrustLevel> Keys;
|
||||||
|
typedef LMDBAL::Cache<QString, Keys> KeyCache;
|
||||||
|
|
||||||
|
virtual QXmppTask<void> resetAll(CSR encryption) override;
|
||||||
|
virtual QXmppTask<TL> trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId) override;
|
||||||
|
virtual QXmppTask<HashSM> setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel) override;
|
||||||
|
virtual QXmppTask<HashSM> setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel) override;
|
||||||
|
virtual QXmppTask<bool> hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels) override;
|
||||||
|
virtual QXmppTask<HSHBTL> keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels) override;
|
||||||
|
virtual QXmppTask<QHash<TL, MultySB>> keys(CSR encryption, QXmpp::TrustLevels trustLevels) override;
|
||||||
|
virtual QXmppTask<void> removeKeys(CSR encryption) override;
|
||||||
|
virtual QXmppTask<void> removeKeys(CSR encryption, CSR keyOwnerJid) override;
|
||||||
|
virtual QXmppTask<void> removeKeys(CSR encryption, CLBAR keyIds) override;
|
||||||
|
virtual QXmppTask<void> addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel) override;
|
||||||
|
virtual QXmppTask<QByteArray> ownKey(CSR encryption) override;
|
||||||
|
virtual QXmppTask<void> resetOwnKey(CSR encryption) override;
|
||||||
|
virtual QXmppTask<void> setOwnKey(CSR encryption, const QByteArray& keyId) override;
|
||||||
|
virtual QXmppTask<QXmpp::TrustSecurityPolicy> securityPolicy(CSR encryption) override;
|
||||||
|
virtual QXmppTask<void> resetSecurityPolicy(CSR encryption) override;
|
||||||
|
virtual QXmppTask<void> setSecurityPolicy(const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) override;
|
||||||
|
|
||||||
|
static TL convert(Shared::TrustLevel level);
|
||||||
|
static Shared::TrustLevel convert(TL level);
|
||||||
|
|
||||||
|
Keys getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const;
|
||||||
|
Shared::TrustSummary getSummary(const QString& jid) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
KeyCache* createNewCache(const QString& encryption);
|
||||||
|
KeyCache* getCache(const QString& encryption);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
LMDBAL::Base db;
|
||||||
|
QFile protocols;
|
||||||
|
LMDBAL::Cache<QString, uint8_t>* securityPolicies;
|
||||||
|
LMDBAL::Cache<QString, QByteArray>* ownKeys;
|
||||||
|
std::map<QString, KeyCache*> keysByProtocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_TRUSTHANDLER_H
|
270
core/handlers/vcardhandler.cpp
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// 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 "vcardhandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
|
||||||
|
Core::VCardHandler::VCardHandler(Account* account):
|
||||||
|
QObject(),
|
||||||
|
acc(account),
|
||||||
|
avatarHash(),
|
||||||
|
avatarType()
|
||||||
|
{
|
||||||
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
path += "/" + acc->name;
|
||||||
|
QDir dir(path);
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
bool res = dir.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
qDebug() << "Couldn't create a cache directory for account" << acc->name;
|
||||||
|
throw 22;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile* avatar = new QFile(path + "/avatar.png");
|
||||||
|
QString type = "png";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpg");
|
||||||
|
type = "jpg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpeg");
|
||||||
|
type = "jpeg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.gif");
|
||||||
|
type = "gif";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatar->exists()) {
|
||||||
|
if (avatar->open(QFile::ReadOnly)) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(avatar);
|
||||||
|
avatarHash = sha1.result();
|
||||||
|
avatarType = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::VCardHandler::~VCardHandler() {}
|
||||||
|
|
||||||
|
void Core::VCardHandler::initialize() {
|
||||||
|
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
|
||||||
|
//for some reason it doesn't work, launching from common handler
|
||||||
|
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
|
||||||
|
|
||||||
|
if (avatarType.size() != 0) {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
} else {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) {
|
||||||
|
QString id = card.from();
|
||||||
|
QStringList comps = id.split("/");
|
||||||
|
QString jid = comps.front().toLower();
|
||||||
|
QString resource("");
|
||||||
|
if (comps.size() > 1) {
|
||||||
|
resource = comps.back();
|
||||||
|
}
|
||||||
|
RosterItem* item = acc->rh->getRosterItem(jid);
|
||||||
|
|
||||||
|
if (item == nullptr) {
|
||||||
|
if (jid == acc->getBareJid())
|
||||||
|
onOwnVCardReceived(card);
|
||||||
|
else
|
||||||
|
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard vCard;
|
||||||
|
item->handleResponseVCard(card, resource, vCard);
|
||||||
|
acc->delay->receivedVCard(id, vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) {
|
||||||
|
QByteArray ava = card.photo();
|
||||||
|
bool avaChanged = false;
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/";
|
||||||
|
if (ava.size() > 0) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(ava);
|
||||||
|
QString newHash(sha1.result());
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType newType = db.mimeTypeForData(ava);
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
if (avatarHash != newHash) {
|
||||||
|
QString oldPath = path + "avatar." + avatarType;
|
||||||
|
QFile oldAvatar(oldPath);
|
||||||
|
bool oldToRemove = false;
|
||||||
|
if (oldAvatar.exists()) {
|
||||||
|
if (oldAvatar.rename(oldPath + ".bak")) {
|
||||||
|
oldToRemove = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
||||||
|
if (oldToRemove) {
|
||||||
|
qDebug() << "rolling back to the old avatar";
|
||||||
|
if (!oldAvatar.rename(oldPath)) {
|
||||||
|
qDebug() << "Couldn't roll back to the old avatar in account" << acc->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oldAvatar(path + "avatar." + avatarType);
|
||||||
|
if (!oldAvatar.remove()) {
|
||||||
|
qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing";
|
||||||
|
} else {
|
||||||
|
avatarType = "";
|
||||||
|
avatarHash = "";
|
||||||
|
avaChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avaChanged) {
|
||||||
|
QMap<QString, QVariant> change;
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
change.insert("avatarPath", path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
acc->presence.setPhotoHash("");
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
||||||
|
change.insert("avatarPath", "");
|
||||||
|
}
|
||||||
|
acc->client.setClientPresence(acc->presence);
|
||||||
|
emit acc->changed(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard vCard;
|
||||||
|
initializeVCard(vCard, card);
|
||||||
|
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::valid);
|
||||||
|
vCard.setAvatarPath(path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acc->delay->isOwnVCardPending())
|
||||||
|
acc->delay->receivedOwnVCard(vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) {
|
||||||
|
if (!acc->delay->isOwnVCardPending()) {
|
||||||
|
switch (p_presence.vCardUpdateType()) {
|
||||||
|
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||||
|
if (avatarType.size() > 0)
|
||||||
|
acc->delay->getOwnVCard();
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||||
|
if (avatarHash != p_presence.photoHash())
|
||||||
|
acc->delay->getOwnVCard();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::uploadVCard(const Shared::VCard& card) {
|
||||||
|
QXmppVCardIq iq;
|
||||||
|
initializeQXmppVCard(iq, card);
|
||||||
|
|
||||||
|
if (card.getAvatarType() != Shared::Avatar::empty) {
|
||||||
|
QString newPath = card.getAvatarPath();
|
||||||
|
QString oldPath = getAvatarPath();
|
||||||
|
QByteArray data;
|
||||||
|
QString type;
|
||||||
|
if (newPath != oldPath) {
|
||||||
|
QFile avatar(newPath);
|
||||||
|
if (!avatar.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "An attempt to upload new vCard to account" << acc->name
|
||||||
|
<< "but it wasn't possible to read file" << newPath
|
||||||
|
<< "which was supposed to be new avatar, uploading old avatar";
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
||||||
|
} else {
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = avatar.readAll();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly))
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
||||||
|
else
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() > 0) {
|
||||||
|
QMimeDatabase db;
|
||||||
|
type = db.mimeTypeForData(data).name();
|
||||||
|
iq.setPhoto(data);
|
||||||
|
iq.setPhotoType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc->vm->setClientVCard(iq);
|
||||||
|
onOwnVCardReceived(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::VCardHandler::getAvatarPath() const {
|
||||||
|
if (avatarType.size() == 0)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType;
|
||||||
|
}
|
63
core/handlers/vcardhandler.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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_VCARDHANDLER_H
|
||||||
|
#define CORE_VCARDHANDLER_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <QXmppPresence.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
#include <core/adapterfunctions.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class VCardHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VCardHandler(Account* account);
|
||||||
|
~VCardHandler();
|
||||||
|
|
||||||
|
void handlePresenceOfMyAccountChange(const QXmppPresence& p_presence);
|
||||||
|
void uploadVCard(const Shared::VCard& card);
|
||||||
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onVCardReceived(const QXmppVCardIq& card);
|
||||||
|
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
|
||||||
|
QString avatarHash;
|
||||||
|
QString avatarType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_VCARDHANDLER_H
|
163
core/main.cpp
@ -1,163 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../shared/global.h"
|
|
||||||
#include "../shared/messageinfo.h"
|
|
||||||
#include "../ui/squawk.h"
|
|
||||||
#include "signalcatcher.h"
|
|
||||||
#include "squawk.h"
|
|
||||||
#include <QLibraryInfo>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
|
||||||
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
|
||||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
|
||||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
|
||||||
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
|
||||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
|
||||||
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
|
||||||
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
SignalCatcher sc(&app);
|
|
||||||
|
|
||||||
QApplication::setApplicationName("squawk");
|
|
||||||
QApplication::setApplicationDisplayName("Squawk");
|
|
||||||
QApplication::setApplicationVersion("0.1.5");
|
|
||||||
|
|
||||||
QTranslator qtTranslator;
|
|
||||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
|
||||||
app.installTranslator(&qtTranslator);
|
|
||||||
|
|
||||||
QTranslator myappTranslator;
|
|
||||||
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
|
||||||
bool found = false;
|
|
||||||
for (QString share : shares) {
|
|
||||||
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
app.installTranslator(&myappTranslator);
|
|
||||||
|
|
||||||
QIcon icon;
|
|
||||||
icon.addFile(":images/logo.svg", QSize(16, 16));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(24, 24));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(32, 32));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(48, 48));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(64, 64));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(96, 96));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(128, 128));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(256, 256));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(512, 512));
|
|
||||||
QApplication::setWindowIcon(icon);
|
|
||||||
|
|
||||||
new Shared::Global(); //translates enums
|
|
||||||
|
|
||||||
Squawk w;
|
|
||||||
w.show();
|
|
||||||
|
|
||||||
Core::Squawk* squawk = new Core::Squawk();
|
|
||||||
QThread* coreThread = new QThread();
|
|
||||||
squawk->moveToThread(coreThread);
|
|
||||||
|
|
||||||
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
|
||||||
QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop);
|
|
||||||
QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit);
|
|
||||||
QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater);
|
|
||||||
|
|
||||||
QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
|
||||||
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
|
||||||
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
|
||||||
QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage);
|
|
||||||
QObject::connect(&w, &Squawk::resendMessage, squawk,&Core::Squawk::resendMessage);
|
|
||||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
|
||||||
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
|
||||||
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
|
||||||
QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined);
|
|
||||||
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
|
||||||
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
|
||||||
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
|
||||||
QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest);
|
|
||||||
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
|
||||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
|
||||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
|
||||||
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
|
||||||
QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid);
|
|
||||||
|
|
||||||
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup);
|
|
||||||
QObject::connect(squawk, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&w, qOverload<const QString&, const QString&>(&Squawk::removeContact));
|
|
||||||
QObject::connect(squawk, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&w, qOverload<const QString&, const QString&, const QString&>(&Squawk::removeContact));
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeMessage, &w, &Squawk::changeMessage);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
|
||||||
|
|
||||||
coreThread->start();
|
|
||||||
|
|
||||||
int result = app.exec();
|
|
||||||
|
|
||||||
w.writeSettings();
|
|
||||||
coreThread->wait(500); //TODO hate doing that but settings for some reason don't get saved to the disk
|
|
||||||
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ if (WITH_KWALLET)
|
|||||||
target_sources(squawk PRIVATE
|
target_sources(squawk PRIVATE
|
||||||
kwallet.cpp
|
kwallet.cpp
|
||||||
kwallet.h
|
kwallet.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(wrappers)
|
add_subdirectory(wrappers)
|
||||||
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)
|
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||||
|
@ -28,7 +28,8 @@ Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0;
|
|||||||
Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0;
|
Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0;
|
||||||
|
|
||||||
Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial;
|
Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial;
|
||||||
QLibrary Core::PSE::KWallet::lib("kwalletWrapper");
|
|
||||||
|
QLibrary Core::PSE::KWallet::lib(QString("%1/kwalletWrapper").arg(PLUGIN_PATH));
|
||||||
|
|
||||||
Core::PSE::KWallet::KWallet():
|
Core::PSE::KWallet::KWallet():
|
||||||
QObject(),
|
QObject(),
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
add_library(kwalletWrapper SHARED kwallet.cpp)
|
add_library(kwalletWrapper SHARED kwallet.cpp)
|
||||||
target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet)
|
target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet)
|
||||||
|
|
||||||
|
install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk)
|
||||||
|
@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 <KF5/KWallet/KWallet>
|
#include <KF5/KWallet/KWallet>
|
||||||
|
|
||||||
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
|
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
|
||||||
|
@ -27,7 +27,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
|
|||||||
account(pAccount),
|
account(pAccount),
|
||||||
name(),
|
name(),
|
||||||
archiveState(empty),
|
archiveState(empty),
|
||||||
archive(new Archive(jid)),
|
archive(new Archive(account, jid)),
|
||||||
syncronizing(false),
|
syncronizing(false),
|
||||||
requestedCount(0),
|
requestedCount(0),
|
||||||
requestedBefore(),
|
requestedBefore(),
|
||||||
@ -38,42 +38,36 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
|
|||||||
toCorrect(),
|
toCorrect(),
|
||||||
muc(false)
|
muc(false)
|
||||||
{
|
{
|
||||||
archive->open(account);
|
archive->open();
|
||||||
|
|
||||||
if (archive->size() != 0) {
|
if (archive->size() != 0) {
|
||||||
if (archive->isFromTheBeginning()) {
|
if (archive->isFromTheBeginning())
|
||||||
archiveState = beginning;
|
archiveState = beginning;
|
||||||
} else {
|
else
|
||||||
archiveState = chunk;
|
archiveState = chunk;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::RosterItem::~RosterItem()
|
Core::RosterItem::~RosterItem() {
|
||||||
{
|
|
||||||
delete archive;
|
delete archive;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const
|
Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const {
|
||||||
{
|
|
||||||
return archiveState;
|
return archiveState;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::getName() const
|
QString Core::RosterItem::getName() const {
|
||||||
{
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::setName(const QString& n)
|
void Core::RosterItem::setName(const QString& n) {
|
||||||
{
|
|
||||||
if (name != n) {
|
if (name != n) {
|
||||||
name = n;
|
name = n;
|
||||||
emit nameChanged(name);
|
emit nameChanged(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::addMessageToArchive(const Shared::Message& msg)
|
void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) {
|
||||||
{
|
|
||||||
if (msg.storable()) {
|
if (msg.storable()) {
|
||||||
hisoryCache.push_back(msg);
|
hisoryCache.push_back(msg);
|
||||||
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(msg.getId());
|
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(msg.getId());
|
||||||
@ -87,8 +81,7 @@ void Core::RosterItem::addMessageToArchive(const Shared::Message& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg)
|
void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) {
|
||||||
{
|
|
||||||
if (msg.storable()) {
|
if (msg.storable()) {
|
||||||
QDateTime thisTime = msg.getTime();
|
QDateTime thisTime = msg.getTime();
|
||||||
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(originalId);
|
std::map<QString, Shared::Message>::iterator itr = toCorrect.find(originalId);
|
||||||
@ -109,8 +102,7 @@ void Core::RosterItem::correctMessageInArchive(const QString& originalId, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::requestHistory(int count, const QString& before)
|
void Core::RosterItem::requestHistory(int count, const QString& before) {
|
||||||
{
|
|
||||||
if (syncronizing) {
|
if (syncronizing) {
|
||||||
requestCache.emplace_back(count, before);
|
requestCache.emplace_back(count, before);
|
||||||
} else {
|
} else {
|
||||||
@ -118,21 +110,25 @@ void Core::RosterItem::requestHistory(int count, const QString& before)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::nextRequest()
|
void Core::RosterItem::nextRequest() {
|
||||||
{
|
|
||||||
if (syncronizing) {
|
if (syncronizing) {
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
bool last = false;
|
bool last = false;
|
||||||
if (archiveState == beginning || archiveState == complete) {
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
QString firstId = archive->oldestId();
|
try {
|
||||||
if (responseCache.size() == 0) {
|
QString firstId = archive->oldestId();
|
||||||
if (requestedBefore == firstId) {
|
if (responseCache.size() == 0) {
|
||||||
last = true;
|
if (requestedBefore == firstId) {
|
||||||
}
|
last = true;
|
||||||
} else {
|
}
|
||||||
if (responseCache.front().getId() == firstId) {
|
} else {
|
||||||
last = true;
|
if (responseCache.front().getId() == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//} catch (const Archive::Empty& e) {
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
last = true;
|
||||||
}
|
}
|
||||||
} else if (archiveState == empty && responseCache.size() == 0) {
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
last = true;
|
last = true;
|
||||||
@ -153,8 +149,7 @@ void Core::RosterItem::nextRequest()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::performRequest(int count, const QString& before)
|
void Core::RosterItem::performRequest(int count, const QString& before) {
|
||||||
{
|
|
||||||
syncronizing = true;
|
syncronizing = true;
|
||||||
requestedCount = count;
|
requestedCount = count;
|
||||||
requestedBefore = before;
|
requestedBefore = before;
|
||||||
@ -171,8 +166,13 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
}
|
}
|
||||||
Shared::Message msg = archive->newest();
|
try {
|
||||||
emit needHistory("", getId(msg), msg.getTime());
|
Shared::Message msg = archive->newest();
|
||||||
|
emit needHistory("", getId(msg), msg.getTime());
|
||||||
|
//} catch (const Archive::Empty& e) {
|
||||||
|
} catch (const LMDBAL::NotFound& e) { //this can happen when the only message in archive is not server stored (error, for example)
|
||||||
|
emit needHistory(before, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case end:
|
case end:
|
||||||
@ -188,14 +188,14 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
|
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
|
||||||
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
||||||
found = true;
|
found = true;
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
requestCache.emplace_back(requestedCount, before);
|
|
||||||
requestedCount = -1;
|
|
||||||
emit needHistory(getId(archive->oldest()), "");
|
|
||||||
} catch (const Archive::Empty& e) {
|
|
||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
emit needHistory(getId(archive->oldest()), "");
|
emit needHistory(getId(archive->oldest()), "");
|
||||||
|
// } catch (const Archive::Empty& e) {
|
||||||
|
// requestCache.emplace_back(requestedCount, before);
|
||||||
|
// requestedCount = -1;
|
||||||
|
// emit needHistory(getId(archive->oldest()), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
@ -228,18 +228,17 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
try {
|
try {
|
||||||
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
|
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
|
||||||
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
qDebug("requesting id hasn't been found in archive, skipping");
|
|
||||||
} catch (const Archive::Empty& e) {
|
|
||||||
qDebug("requesting id hasn't been found in archive, skipping");
|
qDebug("requesting id hasn't been found in archive, skipping");
|
||||||
|
// } catch (const Archive::Empty& e) {
|
||||||
|
// qDebug("requesting id hasn't been found in archive, skipping");
|
||||||
}
|
}
|
||||||
nextRequest();
|
nextRequest();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::getId(const Shared::Message& msg)
|
QString Core::RosterItem::getId(const Shared::Message& msg) {
|
||||||
{
|
|
||||||
QString id;
|
QString id;
|
||||||
if (muc) {
|
if (muc) {
|
||||||
id = msg.getStanzaId();
|
id = msg.getStanzaId();
|
||||||
@ -249,8 +248,7 @@ QString Core::RosterItem::getId(const Shared::Message& msg)
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) {
|
||||||
{
|
|
||||||
if (msg.getId().size() > 0) {
|
if (msg.getId().size() > 0) {
|
||||||
if (msg.storable()) {
|
if (msg.storable()) {
|
||||||
switch (archiveState) {
|
switch (archiveState) {
|
||||||
@ -291,8 +289,7 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
|
bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (Shared::Message& msg : appendCache) {
|
for (Shared::Message& msg : appendCache) {
|
||||||
if (msg.getId() == id) {
|
if (msg.getId() == id) {
|
||||||
@ -316,7 +313,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVar
|
|||||||
try {
|
try {
|
||||||
archive->changeMessage(id, data);
|
archive->changeMessage(id, data);
|
||||||
found = true;
|
found = true;
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found";
|
qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,8 +330,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVar
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId)
|
void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId) {
|
||||||
{
|
|
||||||
unsigned int added(0);
|
unsigned int added(0);
|
||||||
if (hisoryCache.size() > 0) {
|
if (hisoryCache.size() > 0) {
|
||||||
added = archive->addElements(hisoryCache);
|
added = archive->addElements(hisoryCache);
|
||||||
@ -393,10 +389,8 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
|
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
|
||||||
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
|
||||||
found = true;
|
found = true;
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
// } catch (const Archive::Empty& e) {
|
||||||
} catch (const Archive::Empty& e) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!found || requestedCount > int(responseCache.size())) {
|
if (!found || requestedCount > int(responseCache.size())) {
|
||||||
if (archiveState == complete) {
|
if (archiveState == complete) {
|
||||||
@ -421,51 +415,43 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::getServer() const
|
QString Core::RosterItem::getServer() const {
|
||||||
{
|
|
||||||
QStringList lst = jid.split("@");
|
QStringList lst = jid.split("@");
|
||||||
return lst.back();
|
return lst.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::isMuc() const
|
bool Core::RosterItem::isMuc() const {
|
||||||
{
|
|
||||||
return muc;
|
return muc;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::avatarPath(const QString& resource) const
|
QString Core::RosterItem::avatarPath(const QString& resource) const {
|
||||||
{
|
|
||||||
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
|
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::folderPath() const
|
QString Core::RosterItem::folderPath() const {
|
||||||
{
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
path += "/" + account + "/" + jid;
|
path += "/" + account + "/" + jid;
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
|
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) {
|
||||||
{
|
|
||||||
bool result = archive->setAvatar(data, info, false, resource);
|
bool result = archive->setAvatar(data, info, false, resource);
|
||||||
if (resource.size() == 0 && result) {
|
if (resource.size() == 0 && result) {
|
||||||
if (data.size() == 0) {
|
if (data.size() == 0)
|
||||||
emit avatarChanged(Shared::Avatar::empty, "");
|
emit avatarChanged(Shared::Avatar::empty, "");
|
||||||
} else {
|
else
|
||||||
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
|
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) {
|
||||||
{
|
|
||||||
Archive::AvatarInfo info;
|
Archive::AvatarInfo info;
|
||||||
return setAutoGeneratedAvatar(info, resource);
|
return setAutoGeneratedAvatar(info, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource)
|
bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) {
|
||||||
{
|
|
||||||
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
||||||
QPainter painter(&image);
|
QPainter painter(&image);
|
||||||
quint8 colorIndex = rand() % Shared::colorPalette.size();
|
quint8 colorIndex = rand() % Shared::colorPalette.size();
|
||||||
@ -475,11 +461,11 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q
|
|||||||
f.setBold(true);
|
f.setBold(true);
|
||||||
f.setPixelSize(72);
|
f.setPixelSize(72);
|
||||||
painter.setFont(f);
|
painter.setFont(f);
|
||||||
if (bg.lightnessF() > 0.5) {
|
if (bg.lightnessF() > 0.5)
|
||||||
painter.setPen(Qt::black);
|
painter.setPen(Qt::black);
|
||||||
} else {
|
else
|
||||||
painter.setPen(Qt::white);
|
painter.setPen(Qt::white);
|
||||||
}
|
|
||||||
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper());
|
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper());
|
||||||
QByteArray arr;
|
QByteArray arr;
|
||||||
QBuffer stream(&arr);
|
QBuffer stream(&arr);
|
||||||
@ -487,25 +473,22 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q
|
|||||||
image.save(&stream, "PNG");
|
image.save(&stream, "PNG");
|
||||||
stream.close();
|
stream.close();
|
||||||
bool result = archive->setAvatar(arr, info, true, resource);
|
bool result = archive->setAvatar(arr, info, true, resource);
|
||||||
if (resource.size() == 0 && result) {
|
if (resource.size() == 0 && result)
|
||||||
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
|
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const
|
bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const {
|
||||||
{
|
|
||||||
return archive->readAvatarInfo(target, resource);
|
return archive->readAvatarInfo(target, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
|
void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) {
|
||||||
{
|
|
||||||
Archive::AvatarInfo info;
|
Archive::AvatarInfo info;
|
||||||
Archive::AvatarInfo newInfo;
|
Archive::AvatarInfo newInfo;
|
||||||
bool hasAvatar = readAvatarInfo(info, resource);
|
bool hasAvatar = readAvatarInfo(info, resource);
|
||||||
|
|
||||||
QByteArray ava = card.photo();
|
QByteArray ava = card.photo();
|
||||||
Shared::VCard vCard;
|
|
||||||
initializeVCard(vCard, card);
|
initializeVCard(vCard, card);
|
||||||
Shared::Avatar type = Shared::Avatar::empty;
|
Shared::Avatar type = Shared::Avatar::empty;
|
||||||
QString path = "";
|
QString path = "";
|
||||||
@ -525,9 +508,9 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!hasAvatar || !info.autogenerated) {
|
if (!hasAvatar || !info.autogenerated)
|
||||||
setAutoGeneratedAvatar(resource);
|
setAutoGeneratedAvatar(resource);
|
||||||
}
|
|
||||||
type = Shared::Avatar::autocreated;
|
type = Shared::Avatar::autocreated;
|
||||||
path = avatarPath(resource) + ".png";
|
path = avatarPath(resource) + ".png";
|
||||||
}
|
}
|
||||||
@ -535,15 +518,11 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
vCard.setAvatarType(type);
|
vCard.setAvatarType(type);
|
||||||
vCard.setAvatarPath(path);
|
vCard.setAvatarPath(path);
|
||||||
|
|
||||||
if (resource.size() == 0) {
|
if (resource.size() == 0)
|
||||||
emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath());
|
emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath());
|
||||||
}
|
|
||||||
|
|
||||||
return vCard;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::clearArchiveRequests()
|
void Core::RosterItem::clearArchiveRequests() {
|
||||||
{
|
|
||||||
syncronizing = false;
|
syncronizing = false;
|
||||||
requestedCount = 0;
|
requestedCount = 0;
|
||||||
requestedBefore = "";
|
requestedBefore = "";
|
||||||
@ -559,30 +538,72 @@ void Core::RosterItem::clearArchiveRequests()
|
|||||||
requestCache.clear();
|
requestCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::downgradeDatabaseState()
|
void Core::RosterItem::downgradeDatabaseState() {
|
||||||
{
|
if (archiveState == ArchiveState::complete)
|
||||||
if (archiveState == ArchiveState::complete) {
|
|
||||||
archiveState = ArchiveState::beginning;
|
archiveState = ArchiveState::beginning;
|
||||||
}
|
|
||||||
|
|
||||||
if (archiveState == ArchiveState::end) {
|
|
||||||
|
if (archiveState == ArchiveState::end)
|
||||||
archiveState = ArchiveState::chunk;
|
archiveState = ArchiveState::chunk;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message Core::RosterItem::getMessage(const QString& id)
|
Shared::Message Core::RosterItem::getMessage(const QString& id) {
|
||||||
{
|
|
||||||
for (const Shared::Message& msg : appendCache) {
|
for (const Shared::Message& msg : appendCache) {
|
||||||
if (msg.getId() == id) {
|
if (msg.getId() == id)
|
||||||
return msg;
|
return msg;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Shared::Message& msg : hisoryCache) {
|
for (Shared::Message& msg : hisoryCache) {
|
||||||
if (msg.getId() == id) {
|
if (msg.getId() == id)
|
||||||
return msg;
|
return msg;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return archive->getElement(id);
|
return archive->getElement(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shared::EncryptionProtocol Core::RosterItem::encryption() const {
|
||||||
|
return archive->encryption();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterItem::setEncryption(Shared::EncryptionProtocol value) {
|
||||||
|
bool changed = archive->setEncryption(value);
|
||||||
|
if (changed)
|
||||||
|
emit encryptionChanged(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> Core::RosterItem::getInfo() const {
|
||||||
|
QMap<QString, QVariant> result({
|
||||||
|
{"name", name},
|
||||||
|
{"encryption", QVariant::fromValue(encryption())},
|
||||||
|
});
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
bool hasAvatar = readAvatarInfo(info);
|
||||||
|
careAboutAvatar(hasAvatar, info, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Core::RosterItem::careAboutAvatar (
|
||||||
|
bool hasAvatar,
|
||||||
|
const Archive::AvatarInfo& info,
|
||||||
|
QMap<QString, QVariant>& output,
|
||||||
|
const QString& resource,
|
||||||
|
const QString& subject
|
||||||
|
) const {
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated)
|
||||||
|
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
|
||||||
|
else
|
||||||
|
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
|
||||||
|
|
||||||
|
output.insert("avatarPath", avatarPath(resource) + "." + info.type);
|
||||||
|
} else {
|
||||||
|
output.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
|
||||||
|
output.insert("avatarPath", "");
|
||||||
|
if (subject.size() == 0)
|
||||||
|
emit requestVCard(jid);
|
||||||
|
else
|
||||||
|
emit requestVCard(subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CORE_ROSTERITEM_H
|
#pragma once
|
||||||
#define CORE_ROSTERITEM_H
|
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@ -34,7 +33,8 @@
|
|||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/vcard.h"
|
#include "shared/vcard.h"
|
||||||
#include "archive.h"
|
#include "components/archive.h"
|
||||||
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -61,6 +61,8 @@ public:
|
|||||||
void setName(const QString& n);
|
void setName(const QString& n);
|
||||||
QString getServer() const;
|
QString getServer() const;
|
||||||
bool isMuc() const;
|
bool isMuc() const;
|
||||||
|
Shared::EncryptionProtocol encryption() const;
|
||||||
|
void setEncryption(Shared::EncryptionProtocol value);
|
||||||
|
|
||||||
void addMessageToArchive(const Shared::Message& msg);
|
void addMessageToArchive(const Shared::Message& msg);
|
||||||
void correctMessageInArchive(const QString& originalId, const Shared::Message& msg);
|
void correctMessageInArchive(const QString& originalId, const Shared::Message& msg);
|
||||||
@ -71,22 +73,24 @@ public:
|
|||||||
QString folderPath() const;
|
QString folderPath() const;
|
||||||
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
||||||
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
||||||
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
|
virtual void handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& out);
|
||||||
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
||||||
|
|
||||||
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void clearArchiveRequests();
|
void clearArchiveRequests();
|
||||||
void downgradeDatabaseState();
|
void downgradeDatabaseState();
|
||||||
|
virtual QMap<QString, QVariant> getInfo() const;
|
||||||
|
|
||||||
Shared::Message getMessage(const QString& id);
|
Shared::Message getMessage(const QString& id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nameChanged(const QString& name);
|
void nameChanged(const QString& name) const;
|
||||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
void subscriptionStateChanged(Shared::SubscriptionState state) const;
|
||||||
void historyResponse(const std::list<Shared::Message>& messages, bool last);
|
void historyResponse(const std::list<Shared::Message>& messages, bool last) const;
|
||||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()) const;
|
||||||
void avatarChanged(Shared::Avatar, const QString& path);
|
void avatarChanged(Shared::Avatar, const QString& path) const;
|
||||||
void requestVCard(const QString& jid);
|
void requestVCard(const QString& jid) const;
|
||||||
|
void encryptionChanged(Shared::EncryptionProtocol value) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const QString jid;
|
const QString jid;
|
||||||
@ -95,6 +99,13 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
|
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
|
||||||
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
|
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
|
||||||
|
void careAboutAvatar(
|
||||||
|
bool hasAvatar,
|
||||||
|
const Archive::AvatarInfo& info,
|
||||||
|
QMap<QString, QVariant>& output,
|
||||||
|
const QString& resource = "",
|
||||||
|
const QString& subject = ""
|
||||||
|
) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString name;
|
QString name;
|
||||||
@ -118,5 +129,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_ROSTERITEM_H
|
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "shared/defines.h"
|
||||||
|
|
||||||
int SignalCatcher::sigintFd[2] = {0,0};
|
int SignalCatcher::sigintFd[2] = {0,0};
|
||||||
|
|
||||||
SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
||||||
@ -28,14 +30,10 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
|||||||
app(p_app)
|
app(p_app)
|
||||||
{
|
{
|
||||||
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
|
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
|
||||||
{
|
|
||||||
qFatal("Couldn't create INT socketpair");
|
qFatal("Couldn't create INT socketpair");
|
||||||
}
|
|
||||||
|
|
||||||
if (setup_unix_signal_handlers() != 0)
|
if (setup_unix_signal_handlers() != 0)
|
||||||
{
|
|
||||||
qFatal("Couldn't install unix handlers");
|
qFatal("Couldn't install unix handlers");
|
||||||
}
|
|
||||||
|
|
||||||
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
|
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
|
||||||
connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt);
|
connect(snInt, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt);
|
||||||
@ -44,25 +42,25 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
|||||||
SignalCatcher::~SignalCatcher()
|
SignalCatcher::~SignalCatcher()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void SignalCatcher::handleSigInt()
|
void SignalCatcher::handleSigInt() {
|
||||||
{
|
|
||||||
snInt->setEnabled(false);
|
snInt->setEnabled(false);
|
||||||
char tmp;
|
char tmp;
|
||||||
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
|
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
|
||||||
|
SHARED_UNUSED(s);
|
||||||
|
|
||||||
app->quit();
|
emit interrupt();
|
||||||
|
|
||||||
snInt->setEnabled(true);
|
snInt->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalCatcher::intSignalHandler(int unused)
|
void SignalCatcher::intSignalHandler(int unused) {
|
||||||
{
|
|
||||||
char a = 1;
|
char a = 1;
|
||||||
ssize_t s = ::write(sigintFd[0], &a, sizeof(a));
|
ssize_t s = ::write(sigintFd[0], &a, sizeof(a));
|
||||||
|
SHARED_UNUSED(s);
|
||||||
|
SHARED_UNUSED(unused);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SignalCatcher::setup_unix_signal_handlers()
|
int SignalCatcher::setup_unix_signal_handlers() {
|
||||||
{
|
|
||||||
struct sigaction s_int;
|
struct sigaction s_int;
|
||||||
|
|
||||||
s_int.sa_handler = SignalCatcher::intSignalHandler;
|
s_int.sa_handler = SignalCatcher::intSignalHandler;
|
||||||
|
@ -33,6 +33,9 @@ public:
|
|||||||
|
|
||||||
static void intSignalHandler(int unused);
|
static void intSignalHandler(int unused);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void interrupt();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleSigInt();
|
void handleSigInt();
|
||||||
|
|
||||||
|
584
core/squawk.cpp
@ -22,15 +22,21 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#ifdef WITH_SIMPLE_CRYPT
|
||||||
|
#include "external/simpleCrypt/simplecrypt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
Core::Squawk::Squawk(QObject* parent):
|
Core::Squawk::Squawk(QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
accounts(),
|
accounts(),
|
||||||
amap(),
|
amap(),
|
||||||
|
state(Shared::Availability::offline),
|
||||||
network(),
|
network(),
|
||||||
waitingForAccounts(0)
|
isInitialized(false),
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
,kwallet()
|
kwallet(),
|
||||||
#endif
|
#endif
|
||||||
|
clientCache()
|
||||||
{
|
{
|
||||||
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
|
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
|
||||||
connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError);
|
connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError);
|
||||||
@ -41,15 +47,14 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
||||||
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
||||||
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword);
|
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword);
|
||||||
|
|
||||||
Shared::Global::setSupported("KWallet", true);
|
Shared::Global::setSupported("KWallet", true);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Squawk::~Squawk()
|
Core::Squawk::~Squawk() {
|
||||||
{
|
|
||||||
Accounts::const_iterator itr = accounts.begin();
|
Accounts::const_iterator itr = accounts.begin();
|
||||||
Accounts::const_iterator end = accounts.end();
|
Accounts::const_iterator end = accounts.end();
|
||||||
for (; itr != end; ++itr) {
|
for (; itr != end; ++itr) {
|
||||||
@ -57,69 +62,79 @@ Core::Squawk::~Squawk()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onWalletOpened(bool success)
|
void Core::Squawk::onWalletOpened(bool success) {
|
||||||
{
|
|
||||||
qDebug() << "KWallet opened: " << success;
|
qDebug() << "KWallet opened: " << success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::stop()
|
void Core::Squawk::stop() {
|
||||||
{
|
|
||||||
qDebug("Stopping squawk core..");
|
qDebug("Stopping squawk core..");
|
||||||
network.stop();
|
network.stop();
|
||||||
QSettings settings;
|
clientCache.close();
|
||||||
settings.beginGroup("core");
|
|
||||||
settings.beginWriteArray("accounts");
|
|
||||||
SimpleCrypt crypto(passwordHash);
|
|
||||||
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
Account* acc = accounts[i];
|
|
||||||
|
|
||||||
Shared::AccountPassword ap = acc->getPasswordType();
|
if (isInitialized) {
|
||||||
QString password;
|
QSettings settings;
|
||||||
|
settings.beginGroup("core");
|
||||||
|
settings.beginWriteArray("accounts");
|
||||||
|
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
Account* acc = accounts[i];
|
||||||
|
|
||||||
switch (ap) {
|
Shared::AccountPassword ap = acc->getPasswordType();
|
||||||
case Shared::AccountPassword::plain:
|
QString password;
|
||||||
password = acc->getPassword();
|
|
||||||
break;
|
switch (ap) {
|
||||||
case Shared::AccountPassword::jammed:
|
case Shared::AccountPassword::plain:
|
||||||
password = crypto.encryptToString(acc->getPassword());
|
password = acc->getPassword();
|
||||||
break;
|
break;
|
||||||
default:
|
case Shared::AccountPassword::jammed:
|
||||||
break;
|
#ifdef WITH_SIMPLE_CRYPT2
|
||||||
|
password = SimpleCrypt(passwordHash).encryptToString(acc->getPassword());
|
||||||
|
#else
|
||||||
|
qDebug() << "The password for account" << acc->getName() << "is set to be jammed, but Squawk was compiled without SimpleCrypt support";
|
||||||
|
qDebug("Can not encode password, setting this account to always ask password mode");
|
||||||
|
ap = Shared::AccountPassword::alwaysAsk;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setValue("name", acc->getName());
|
||||||
|
settings.setValue("server", acc->getServer());
|
||||||
|
settings.setValue("login", acc->getLogin());
|
||||||
|
settings.setValue("password", password);
|
||||||
|
settings.setValue("resource", acc->getResource());
|
||||||
|
settings.setValue("passwordType", static_cast<int>(ap));
|
||||||
|
settings.setValue("active", acc->getActive());
|
||||||
}
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
settings.setValue("name", acc->getName());
|
settings.sync();
|
||||||
settings.setValue("server", acc->getServer());
|
|
||||||
settings.setValue("login", acc->getLogin());
|
|
||||||
settings.setValue("password", password);
|
|
||||||
settings.setValue("resource", acc->getResource());
|
|
||||||
settings.setValue("passwordType", static_cast<int>(ap));
|
|
||||||
}
|
}
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
|
|
||||||
settings.sync();
|
|
||||||
|
|
||||||
emit quit();
|
emit quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::start()
|
void Core::Squawk::start() {
|
||||||
{
|
|
||||||
qDebug("Starting squawk core..");
|
qDebug("Starting squawk core..");
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
|
isInitialized = true;
|
||||||
network.start();
|
network.start();
|
||||||
|
clientCache.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map) {
|
||||||
{
|
|
||||||
QString name = map.value("name").toString();
|
QString name = map.value("name").toString();
|
||||||
QString login = map.value("login").toString();
|
QString login = map.value("login").toString();
|
||||||
QString server = map.value("server").toString();
|
QString server = map.value("server").toString();
|
||||||
QString password = map.value("password").toString();
|
QString password = map.value("password").toString();
|
||||||
QString resource = map.value("resource").toString();
|
QString resource = map.value("resource").toString();
|
||||||
|
int passwordType = map.value("passwordType").toInt();
|
||||||
|
bool active = map.value("active").toBool();
|
||||||
|
|
||||||
addAccount(login, server, password, name, resource, Shared::AccountPassword::plain);
|
addAccount(login, server, password, name, resource, active, Shared::Global::fromInt<Shared::AccountPassword>(passwordType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addAccount(
|
void Core::Squawk::addAccount(
|
||||||
@ -128,12 +143,14 @@ void Core::Squawk::addAccount(
|
|||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
Shared::AccountPassword passwordType
|
bool active,
|
||||||
)
|
Shared::AccountPassword passwordType)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
if (amap.count(name) > 0) {
|
||||||
|
qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring";
|
||||||
Account* acc = new Account(login, server, password, name, &network);
|
return;
|
||||||
|
}
|
||||||
|
Account* acc = new Account(login, server, password, name, active, &network);
|
||||||
acc->setResource(resource);
|
acc->setResource(resource);
|
||||||
acc->setPasswordType(passwordType);
|
acc->setPasswordType(passwordType);
|
||||||
accounts.push_back(acc);
|
accounts.push_back(acc);
|
||||||
@ -142,6 +159,8 @@ void Core::Squawk::addAccount(
|
|||||||
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
||||||
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
||||||
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
||||||
|
connect(acc, &Account::needPassword, this, &Squawk::onAccountNeedPassword);
|
||||||
|
|
||||||
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
||||||
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
||||||
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
||||||
@ -165,10 +184,12 @@ void Core::Squawk::addAccount(
|
|||||||
connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence);
|
connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence);
|
||||||
connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence);
|
connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence);
|
||||||
|
|
||||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
connect(acc, &Account::infoReady, this, &Squawk::responseInfo);
|
||||||
|
|
||||||
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
|
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
|
||||||
|
|
||||||
|
connect(acc, &Account::infoDiscovered, this, &Squawk::onAccountInfoDiscovered);
|
||||||
|
|
||||||
QMap<QString, QVariant> map = {
|
QMap<QString, QVariant> map = {
|
||||||
{"login", login},
|
{"login", login},
|
||||||
{"server", server},
|
{"server", server},
|
||||||
@ -179,48 +200,75 @@ void Core::Squawk::addAccount(
|
|||||||
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
||||||
{"error", ""},
|
{"error", ""},
|
||||||
{"avatarPath", acc->getAvatarPath()},
|
{"avatarPath", acc->getAvatarPath()},
|
||||||
{"passwordType", QVariant::fromValue(passwordType)}
|
{"passwordType", QVariant::fromValue(passwordType)},
|
||||||
|
{"active", active}
|
||||||
};
|
};
|
||||||
|
|
||||||
emit newAccount(map);
|
emit newAccount(map);
|
||||||
|
|
||||||
|
switch (passwordType) {
|
||||||
|
case Shared::AccountPassword::alwaysAsk:
|
||||||
|
case Shared::AccountPassword::kwallet:
|
||||||
|
if (password == "")
|
||||||
|
acc->invalidatePassword();
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != Shared::Availability::offline) {
|
||||||
|
acc->setAvailability(state);
|
||||||
|
if (acc->getActive())
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::changeState(Shared::Availability p_state)
|
void Core::Squawk::changeState(Shared::Availability p_state) {
|
||||||
{
|
|
||||||
if (state != p_state) {
|
if (state != p_state) {
|
||||||
|
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
||||||
|
Account* acc = *itr;
|
||||||
|
acc->setAvailability(p_state);
|
||||||
|
if (state == Shared::Availability::offline && acc->getActive())
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
state = p_state;
|
state = p_state;
|
||||||
}
|
|
||||||
|
|
||||||
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
emit stateChanged(p_state);
|
||||||
(*itr)->setAvailability(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::connectAccount(const QString& account)
|
void Core::Squawk::connectAccount(const QString& account) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to connect non existing account, skipping");
|
qDebug("An attempt to connect non existing account, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->connect();
|
itr->second->setActive(true);
|
||||||
|
if (state != Shared::Availability::offline)
|
||||||
|
itr->second->connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::disconnectAccount(const QString& account)
|
void Core::Squawk::disconnectAccount(const QString& account) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to connect non existing account, skipping");
|
qDebug("An attempt to connect non existing account, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr->second->setActive(false);
|
||||||
itr->second->disconnect();
|
itr->second->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
|
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}});
|
QMap<QString, QVariant> changes = {
|
||||||
|
{"state", QVariant::fromValue(p_state)}
|
||||||
|
};
|
||||||
|
if (acc->getLastError() == Account::Error::none)
|
||||||
|
changes.insert("error", "");
|
||||||
|
|
||||||
|
emit changeAccount(acc->getName(), changes);
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (p_state == Shared::ConnectionState::connected) {
|
if (p_state == Shared::ConnectionState::connected) {
|
||||||
@ -229,103 +277,87 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Accounts::const_iterator itr = accounts.begin();
|
|
||||||
bool es = true;
|
|
||||||
bool ea = true;
|
|
||||||
Shared::ConnectionState cs = (*itr)->getState();
|
|
||||||
Shared::Availability av = (*itr)->getAvailability();
|
|
||||||
itr++;
|
|
||||||
for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) {
|
|
||||||
Account* item = *itr;
|
|
||||||
if (item->getState() != cs) {
|
|
||||||
es = false;
|
|
||||||
}
|
|
||||||
if (item->getAvailability() != av) {
|
|
||||||
ea = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (es) {
|
|
||||||
if (cs == Shared::ConnectionState::disconnected) {
|
|
||||||
state = Shared::Availability::offline;
|
|
||||||
emit stateChanged(state);
|
|
||||||
} else if (ea) {
|
|
||||||
state = av;
|
|
||||||
emit stateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit addContact(acc->getName(), jid, group, data);
|
emit addContact(acc->getName(), jid, group, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddGroup(const QString& name)
|
void Core::Squawk::onAccountAddGroup(const QString& name) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit addGroup(acc->getName(), name);
|
emit addGroup(acc->getName(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemoveGroup(const QString& name)
|
void Core::Squawk::onAccountRemoveGroup(const QString& name) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removeGroup(acc->getName(), name);
|
emit removeGroup(acc->getName(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeContact(acc->getName(), jid, data);
|
emit changeContact(acc->getName(), jid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemoveContact(const QString& jid)
|
void Core::Squawk::onAccountRemoveContact(const QString& jid) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removeContact(acc->getName(), jid);
|
emit removeContact(acc->getName(), jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group)
|
void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removeContact(acc->getName(), jid, group);
|
emit removeContact(acc->getName(), jid, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit addPresence(acc->getName(), jid, name, data);
|
emit addPresence(acc->getName(), jid, name, data);
|
||||||
|
|
||||||
|
//it's equal if a MUC sends its status with presence of the same jid (ex: muc@srv.im/muc@srv.im), it's not a client, so, no need to request
|
||||||
|
if (jid != name) {
|
||||||
|
const Shared::ClientId& id = data["client"].value<Shared::ClientId>();
|
||||||
|
if (!id.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!clientCache.checkClient(id))
|
||||||
|
acc->discoverInfo(jid + "/" + name, id.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name)
|
void Core::Squawk::onAccountInfoDiscovered(
|
||||||
|
const QString& address,
|
||||||
|
const QString& node,
|
||||||
|
const std::set<Shared::Identity>& identities,
|
||||||
|
const std::set<QString>& features)
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
|
|
||||||
|
if (!clientCache.registerClientInfo(address, node, identities, features)) {
|
||||||
|
qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) {
|
||||||
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removePresence(acc->getName(), jid, name);
|
emit removePresence(acc->getName(), jid, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state)
|
void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}});
|
emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), data);
|
emit changeAccount(acc->getName(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountMessage(const Shared::Message& data)
|
void Core::Squawk::onAccountMessage(const Shared::Message& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit accountMessage(acc->getName(), data);
|
emit accountMessage(acc->getName(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data)
|
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to send a message with non existing account" << account << ", skipping";
|
qDebug() << "An attempt to send a message with non existing account" << account << ", skipping";
|
||||||
@ -335,8 +367,17 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
|
|||||||
itr->second->sendMessage(data);
|
itr->second->sendMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id)
|
void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) {
|
||||||
{
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itr->second->replaceMessage(originalId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) {
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping";
|
qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping";
|
||||||
@ -346,8 +387,7 @@ void Core::Squawk::resendMessage(const QString& account, const QString& jid, con
|
|||||||
itr->second->resendMessage(jid, id);
|
itr->second->resendMessage(jid, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)
|
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to request an archive of non existing account, skipping");
|
qDebug("An attempt to request an archive of non existing account, skipping");
|
||||||
@ -356,14 +396,12 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in
|
|||||||
itr->second->requestArchive(jid, count, before);
|
itr->second->requestArchive(jid, count, before);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last)
|
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit responseArchive(acc->getName(), jid, list, last);
|
emit responseArchive(acc->getName(), jid, list, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
|
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(name);
|
AccountsMap::const_iterator itr = amap.find(name);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to modify non existing account, skipping");
|
qDebug("An attempt to modify non existing account, skipping");
|
||||||
@ -374,6 +412,7 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
Shared::ConnectionState st = acc->getState();
|
Shared::ConnectionState st = acc->getState();
|
||||||
QMap<QString, QVariant>::const_iterator mItr;
|
QMap<QString, QVariant>::const_iterator mItr;
|
||||||
bool needToReconnect = false;
|
bool needToReconnect = false;
|
||||||
|
bool wentReconnecting = false;
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end()) {
|
||||||
@ -399,34 +438,37 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
bool activeChanged = false;
|
||||||
acc->reconnect();
|
mItr = map.find("active");
|
||||||
|
if (mItr == map.end() || mItr->toBool() == acc->getActive()) {
|
||||||
|
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
||||||
|
acc->reconnect();
|
||||||
|
wentReconnecting = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc->setActive(mItr->toBool());
|
||||||
|
activeChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end())
|
||||||
acc->setLogin(mItr->toString());
|
acc->setLogin(mItr->toString());
|
||||||
}
|
|
||||||
|
|
||||||
mItr = map.find("password");
|
mItr = map.find("password");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end())
|
||||||
acc->setPassword(mItr->toString());
|
acc->setPassword(mItr->toString());
|
||||||
}
|
|
||||||
|
|
||||||
mItr = map.find("resource");
|
mItr = map.find("resource");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end())
|
||||||
acc->setResource(mItr->toString());
|
acc->setResource(mItr->toString());
|
||||||
}
|
|
||||||
|
|
||||||
mItr = map.find("server");
|
mItr = map.find("server");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end())
|
||||||
acc->setServer(mItr->toString());
|
acc->setServer(mItr->toString());
|
||||||
}
|
|
||||||
|
|
||||||
mItr = map.find("passwordType");
|
mItr = map.find("passwordType");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end())
|
||||||
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
|
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (acc->getPasswordType() == Shared::AccountPassword::kwallet
|
if (acc->getPasswordType() == Shared::AccountPassword::kwallet
|
||||||
@ -437,17 +479,25 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (state != Shared::Availability::offline) {
|
||||||
|
if (activeChanged && acc->getActive())
|
||||||
|
acc->connect();
|
||||||
|
else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication)
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
|
|
||||||
emit changeAccount(name, map);
|
emit changeAccount(name, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountError(const QString& text)
|
void Core::Squawk::onAccountError(const QString& text) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"error", text}});
|
emit changeAccount(acc->getName(), {{"error", text}});
|
||||||
|
|
||||||
|
if (acc->getLastError() == Account::Error::authentication)
|
||||||
|
emit requestPassword(acc->getName(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeAccountRequest(const QString& name)
|
void Core::Squawk::removeAccountRequest(const QString& name) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(name);
|
AccountsMap::const_iterator itr = amap.find(name);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to remove non existing account " << name << " from core, skipping";
|
qDebug() << "An attempt to remove non existing account " << name << " from core, skipping";
|
||||||
@ -455,9 +505,8 @@ void Core::Squawk::removeAccountRequest(const QString& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Account* acc = itr->second;
|
Account* acc = itr->second;
|
||||||
if (acc->getState() != Shared::ConnectionState::disconnected) {
|
if (acc->getState() != Shared::ConnectionState::disconnected)
|
||||||
acc->disconnect();
|
acc->disconnect();
|
||||||
}
|
|
||||||
|
|
||||||
for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) {
|
for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) {
|
||||||
if (*aItr == acc) {
|
if (*aItr == acc) {
|
||||||
@ -477,8 +526,7 @@ void Core::Squawk::removeAccountRequest(const QString& name)
|
|||||||
acc->deleteLater();
|
acc->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason)
|
void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to subscribe to the contact with non existing account, skipping");
|
qDebug("An attempt to subscribe to the contact with non existing account, skipping");
|
||||||
@ -488,8 +536,7 @@ void Core::Squawk::subscribeContact(const QString& account, const QString& jid,
|
|||||||
itr->second->subscribeToContact(jid, reason);
|
itr->second->subscribeToContact(jid, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason)
|
void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to subscribe to the contact with non existing account, skipping");
|
qDebug("An attempt to subscribe to the contact with non existing account, skipping");
|
||||||
@ -499,8 +546,7 @@ void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid
|
|||||||
itr->second->unsubscribeFromContact(jid, reason);
|
itr->second->unsubscribeFromContact(jid, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeContactRequest(const QString& account, const QString& jid)
|
void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to remove contact from non existing account, skipping");
|
qDebug("An attempt to remove contact from non existing account, skipping");
|
||||||
@ -510,8 +556,7 @@ void Core::Squawk::removeContactRequest(const QString& account, const QString& j
|
|||||||
itr->second->removeContactRequest(jid);
|
itr->second->removeContactRequest(jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups)
|
void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to add contact to a non existing account, skipping");
|
qDebug("An attempt to add contact to a non existing account, skipping");
|
||||||
@ -521,26 +566,22 @@ void Core::Squawk::addContactRequest(const QString& account, const QString& jid,
|
|||||||
itr->second->addContactRequest(jid, name, groups);
|
itr->second->addContactRequest(jid, name, groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit addRoom(acc->getName(), jid, data);
|
emit addRoom(acc->getName(), jid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeRoom(acc->getName(), jid, data);
|
emit changeRoom(acc->getName(), jid, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemoveRoom(const QString jid)
|
void Core::Squawk::onAccountRemoveRoom(const QString jid) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removeRoom(acc->getName(), jid);
|
emit removeRoom(acc->getName(), jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined)
|
void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping";
|
qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping";
|
||||||
@ -549,8 +590,7 @@ void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, boo
|
|||||||
itr->second->setRoomJoined(jid, joined);
|
itr->second->setRoomJoined(jid, joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined)
|
void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping";
|
qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping";
|
||||||
@ -559,32 +599,36 @@ void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, b
|
|||||||
itr->second->setRoomAutoJoin(jid, joined);
|
itr->second->setRoomAutoJoin(jid, joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data)
|
void Core::Squawk::setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value) {
|
||||||
{
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug() << "An attempt to set encryption to the contact" << jid << "of non existing account" << account << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itr->second->setContactEncryption(jid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data) {
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit addRoomParticipant(acc->getName(), jid, nick, data);
|
emit addRoomParticipant(acc->getName(), jid, nick, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeRoomParticipant(acc->getName(), jid, nick, data);
|
emit changeRoomParticipant(acc->getName(), jid, nick, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick)
|
void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit removeRoomParticipant(acc->getName(), jid, nick);
|
emit removeRoomParticipant(acc->getName(), jid, nick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data) {
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeMessage(acc->getName(), jid, id, data);
|
emit changeMessage(acc->getName(), jid, id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid)
|
void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping";
|
qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping";
|
||||||
@ -593,8 +637,7 @@ void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid)
|
|||||||
itr->second->removeRoomRequest(jid);
|
itr->second->removeRoomRequest(jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin)
|
void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping";
|
qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping";
|
||||||
@ -603,13 +646,11 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co
|
|||||||
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::fileDownloadRequest(const QString& url)
|
void Core::Squawk::fileDownloadRequest(const QString& url) {
|
||||||
{
|
|
||||||
network.downladFile(url);
|
network.downladFile(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||||
@ -618,8 +659,7 @@ void Core::Squawk::addContactToGroupRequest(const QString& account, const QStrin
|
|||||||
itr->second->addContactToGroupRequest(jid, groupName);
|
itr->second->addContactToGroupRequest(jid, groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||||
@ -628,8 +668,7 @@ void Core::Squawk::removeContactFromGroupRequest(const QString& account, const Q
|
|||||||
itr->second->removeContactFromGroupRequest(jid, groupName);
|
itr->second->removeContactFromGroupRequest(jid, groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName)
|
void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping";
|
qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping";
|
||||||
@ -638,138 +677,114 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j
|
|||||||
itr->second->renameContactRequest(jid, newName);
|
itr->second->renameContactRequest(jid, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::requestVCard(const QString& account, const QString& jid)
|
void Core::Squawk::requestInfo(const QString& account, const QString& jid) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping";
|
qDebug() << "An attempt to request info about" << jid << "of non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->requestVCard(jid);
|
itr->second->requestInfo(jid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card)
|
void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) {
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping";
|
qDebug() << "An attempt to update info to non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->uploadVCard(card);
|
itr->second->updateInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
void Core::Squawk::readSettings() {
|
||||||
{
|
QSettings settings;
|
||||||
|
settings.beginGroup("core");
|
||||||
|
int size = settings.beginReadArray("accounts");
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
Shared::AccountPassword passwordType =
|
||||||
|
Shared::Global::fromInt<Shared::AccountPassword>(
|
||||||
|
settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt()
|
||||||
|
);
|
||||||
|
|
||||||
|
QString name = settings.value("name").toString();
|
||||||
|
QString password = settings.value("password", "").toString();
|
||||||
|
if (passwordType == Shared::AccountPassword::jammed) {
|
||||||
|
#ifdef WITH_SIMPLE_CRYPT
|
||||||
|
SimpleCrypt crypto(passwordHash);
|
||||||
|
password = crypto.decryptToString(password);
|
||||||
|
#else
|
||||||
|
qDebug() << "The password for account" << name << "is jammed, but Squawk was compiled without SimpleCrypt support";
|
||||||
|
qDebug("Can not decode password, setting this account to always ask password mode");
|
||||||
|
passwordType = Shared::AccountPassword::alwaysAsk;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccount(
|
||||||
|
settings.value("login").toString(),
|
||||||
|
settings.value("server").toString(),
|
||||||
|
password,
|
||||||
|
name,
|
||||||
|
settings.value("resource").toString(),
|
||||||
|
settings.value("active").toBool(),
|
||||||
|
passwordType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
qDebug() << "Squawk core is ready";
|
||||||
|
emit ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountNeedPassword() {
|
||||||
|
Account* acc = static_cast<Account*>(sender());
|
||||||
|
switch (acc->getPasswordType()) {
|
||||||
|
case Shared::AccountPassword::alwaysAsk:
|
||||||
|
emit requestPassword(acc->getName(), false);
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::kwallet: {
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
|
kwallet.requestReadPassword(acc->getName());
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
emit requestPassword(acc->getName(), false);
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break; //should never happen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onWalletRejectPassword(const QString& login) {
|
||||||
|
emit requestPassword(login, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::responsePassword(const QString& account, const QString& password) {
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->setPassword(password);
|
Account* acc = itr->second;
|
||||||
|
acc->setPassword(password);
|
||||||
emit changeAccount(account, {{"password", password}});
|
emit changeAccount(account, {{"password", password}});
|
||||||
accountReady();
|
if (state != Shared::Availability::offline && acc->getActive())
|
||||||
|
acc->connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::readSettings()
|
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) {
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("core");
|
|
||||||
int size = settings.beginReadArray("accounts");
|
|
||||||
waitingForAccounts = size;
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
parseAccount(
|
|
||||||
settings.value("login").toString(),
|
|
||||||
settings.value("server").toString(),
|
|
||||||
settings.value("password", "").toString(),
|
|
||||||
settings.value("name").toString(),
|
|
||||||
settings.value("resource").toString(),
|
|
||||||
Shared::Global::fromInt<Shared::AccountPassword>(settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::accountReady()
|
|
||||||
{
|
|
||||||
--waitingForAccounts;
|
|
||||||
|
|
||||||
if (waitingForAccounts == 0) {
|
|
||||||
emit ready();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::parseAccount(
|
|
||||||
const QString& login,
|
|
||||||
const QString& server,
|
|
||||||
const QString& password,
|
|
||||||
const QString& name,
|
|
||||||
const QString& resource,
|
|
||||||
Shared::AccountPassword passwordType
|
|
||||||
)
|
|
||||||
{
|
|
||||||
switch (passwordType) {
|
|
||||||
case Shared::AccountPassword::plain:
|
|
||||||
addAccount(login, server, password, name, resource, passwordType);
|
|
||||||
accountReady();
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::jammed: {
|
|
||||||
SimpleCrypt crypto(passwordHash);
|
|
||||||
QString decrypted = crypto.decryptToString(password);
|
|
||||||
addAccount(login, server, decrypted, name, resource, passwordType);
|
|
||||||
accountReady();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::alwaysAsk:
|
|
||||||
addAccount(login, server, QString(), name, resource, passwordType);
|
|
||||||
emit requestPassword(name);
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::kwallet: {
|
|
||||||
addAccount(login, server, QString(), name, resource, passwordType);
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
|
||||||
kwallet.requestReadPassword(name);
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
emit requestPassword(name);
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onWalletRejectPassword(const QString& login)
|
|
||||||
{
|
|
||||||
emit requestPassword(login);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onWalletResponsePassword(const QString& login, const QString& password)
|
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(login);
|
|
||||||
if (itr == amap.end()) {
|
|
||||||
qDebug() << "An attempt to set password to non existing account" << login << ", skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
itr->second->setPassword(password);
|
|
||||||
emit changeAccount(login, {{"password", password}});
|
|
||||||
accountReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
|
||||||
{
|
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit fileError({{acc->getName(), jid, id}}, errorText, true);
|
emit fileError({{acc->getName(), jid, id}}, errorText, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onLocalPathInvalid(const QString& path)
|
void Core::Squawk::onLocalPathInvalid(const QString& path) {
|
||||||
{
|
|
||||||
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
|
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
|
||||||
|
|
||||||
QMap<QString, QVariant> data({
|
QMap<QString, QVariant> data({{"attachPath", ""}});
|
||||||
{"attachPath", ""}
|
|
||||||
});
|
|
||||||
for (const Shared::MessageInfo& info : list) {
|
for (const Shared::MessageInfo& info : list) {
|
||||||
AccountsMap::const_iterator itr = amap.find(info.account);
|
AccountsMap::const_iterator itr = amap.find(info.account);
|
||||||
if (itr != amap.end()) {
|
if (itr != amap.end()) {
|
||||||
@ -779,3 +794,8 @@ void Core::Squawk::onLocalPathInvalid(const QString& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::changeDownloadsPath(const QString& path) {
|
||||||
|
network.moveFilesDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -31,17 +31,18 @@
|
|||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "networkaccess.h"
|
#include "shared/info.h"
|
||||||
#include "external/simpleCrypt/simplecrypt.h"
|
#include "shared/clientinfo.h"
|
||||||
|
|
||||||
|
#include <core/components/clientcache.h>
|
||||||
|
#include <core/components/networkaccess.h>
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
#include "passwordStorageEngines/kwallet.h"
|
#include "passwordStorageEngines/kwallet.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Core
|
namespace Core {
|
||||||
{
|
class Squawk : public QObject {
|
||||||
class Squawk : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -82,11 +83,11 @@ signals:
|
|||||||
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseInfo(const Shared::Info& info);
|
||||||
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, bool authernticationError);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
@ -101,6 +102,7 @@ public slots:
|
|||||||
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 replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
||||||
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
||||||
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);
|
||||||
|
|
||||||
@ -111,6 +113,7 @@ public slots:
|
|||||||
void removeContactRequest(const QString& account, const QString& jid);
|
void removeContactRequest(const QString& account, const QString& jid);
|
||||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||||
|
void setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value);
|
||||||
|
|
||||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
@ -119,10 +122,11 @@ public slots:
|
|||||||
|
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
|
|
||||||
void requestVCard(const QString& account, const QString& jid);
|
void requestInfo(const QString& account, const QString& jid);
|
||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
void updateInfo(const QString& account, const Shared::Info& info);
|
||||||
void responsePassword(const QString& account, const QString& password);
|
void responsePassword(const QString& account, const QString& password);
|
||||||
void onLocalPathInvalid(const QString& path);
|
void onLocalPathInvalid(const QString& path);
|
||||||
|
void changeDownloadsPath(const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::deque<Account*> Accounts;
|
typedef std::deque<Account*> Accounts;
|
||||||
@ -132,12 +136,14 @@ private:
|
|||||||
AccountsMap amap;
|
AccountsMap amap;
|
||||||
Shared::Availability state;
|
Shared::Availability state;
|
||||||
NetworkAccess network;
|
NetworkAccess network;
|
||||||
uint8_t waitingForAccounts;
|
bool isInitialized;
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
PSE::KWallet kwallet;
|
PSE::KWallet kwallet;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ClientCache clientCache;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void addAccount(
|
void addAccount(
|
||||||
const QString& login,
|
const QString& login,
|
||||||
@ -145,6 +151,7 @@ private slots:
|
|||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
Shared::AccountPassword passwordType
|
Shared::AccountPassword passwordType
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -169,24 +176,17 @@ private slots:
|
|||||||
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
||||||
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 onAccountNeedPassword();
|
||||||
|
|
||||||
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
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 onWalletRejectPassword(const QString& login);
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
|
||||||
|
void onAccountInfoDiscovered(const QString& address, const QString& node, const std::set<Shared::Identity>& identities, const std::set<QString>& features);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSettings();
|
void readSettings();
|
||||||
void accountReady();
|
|
||||||
void parseAccount(
|
|
||||||
const QString& login,
|
|
||||||
const QString& server,
|
|
||||||
const QString& password,
|
|
||||||
const QString& name,
|
|
||||||
const QString& resource,
|
|
||||||
Shared::AccountPassword passwordType
|
|
||||||
);
|
|
||||||
|
|
||||||
static const quint64 passwordHash = 0x08d054225ac4871d;
|
static const quint64 passwordHash = 0x08d054225ac4871d;
|
||||||
};
|
};
|
||||||
|
184
core/storage.cpp
@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include "storage.h"
|
|
||||||
|
|
||||||
Core::Storage::Storage(const QString& p_name):
|
|
||||||
name(p_name),
|
|
||||||
opened(false),
|
|
||||||
environment(),
|
|
||||||
base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Storage::~Storage()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Storage::open()
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
mdb_env_create(&environment);
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
|
||||||
path += "/" + name;
|
|
||||||
QDir cache(path);
|
|
||||||
|
|
||||||
if (!cache.exists()) {
|
|
||||||
bool res = cache.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
throw Archive::Directory(path.toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 1);
|
|
||||||
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
|
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
opened = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Storage::close()
|
|
||||||
{
|
|
||||||
if (opened) {
|
|
||||||
mdb_dbi_close(environment, base);
|
|
||||||
mdb_env_close(environment);
|
|
||||||
opened = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Storage::addRecord(const QString& key, const QString& value)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addRecord", name.toStdString());
|
|
||||||
}
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
const std::string& val = value.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = val.size();
|
|
||||||
lmdbData.mv_data = (char*)val.c_str();
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
int rc;
|
|
||||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
|
||||||
if (rc != 0) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
if (rc == MDB_KEYEXIST) {
|
|
||||||
throw Archive::Exist(name.toStdString(), id);
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Storage::changeRecord(const QString& key, const QString& value)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("changeRecord", name.toStdString());
|
|
||||||
}
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
const std::string& val = value.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
lmdbData.mv_size = val.size();
|
|
||||||
lmdbData.mv_data = (char*)val.c_str();
|
|
||||||
MDB_txn *txn;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
int rc;
|
|
||||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
|
|
||||||
if (rc != 0) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
if (rc) {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Storage::getRecord(const QString& key) const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addElement", name.toStdString());
|
|
||||||
}
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
int rc;
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
|
||||||
rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
|
|
||||||
if (rc) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
if (rc == MDB_NOTFOUND) {
|
|
||||||
throw Archive::NotFound(id, name.toStdString());
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
|
|
||||||
QString value(sId.c_str());
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Storage::removeRecord(const QString& key)
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Archive::Closed("addElement", name.toStdString());
|
|
||||||
}
|
|
||||||
const std::string& id = key.toStdString();
|
|
||||||
|
|
||||||
MDB_val lmdbKey;
|
|
||||||
lmdbKey.mv_size = id.size();
|
|
||||||
lmdbKey.mv_data = (char*)id.c_str();
|
|
||||||
|
|
||||||
MDB_txn *txn;
|
|
||||||
int rc;
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
|
||||||
rc = mdb_del(txn, base, &lmdbKey, NULL);
|
|
||||||
if (rc) {
|
|
||||||
mdb_txn_abort(txn);
|
|
||||||
if (rc == MDB_NOTFOUND) {
|
|
||||||
throw Archive::NotFound(id, name.toStdString());
|
|
||||||
} else {
|
|
||||||
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,491 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "urlstorage.h"
|
|
||||||
|
|
||||||
Core::UrlStorage::UrlStorage(const QString& p_name):
|
|
||||||
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;
|
|
||||||
}
|
|
1
external/lmdbal
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit d62eddc47edbec9f8c071459e045578f61ab58df
|
2
external/qxmpp
vendored
@ -1 +1 @@
|
|||||||
Subproject commit fe83e9c3d42c3becf682e2b5ecfc9d77b24c614f
|
Subproject commit 0cd7379bd78aa01af7e84f2fad6269ef0c0ba49c
|
2
external/simpleCrypt/CMakeLists.txt
vendored
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(simplecrypt LANGUAGES CXX)
|
project(simplecrypt LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
2
external/simpleCrypt/simplecrypt.cpp
vendored
@ -19,7 +19,7 @@
|
|||||||
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
2
external/simpleCrypt/simplecrypt.h
vendored
@ -19,7 +19,7 @@
|
|||||||
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
14
main/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
set(SOURCE_FILES
|
||||||
|
main.cpp
|
||||||
|
application.cpp
|
||||||
|
dialogqueue.cpp
|
||||||
|
root.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADER_FILES
|
||||||
|
application.h
|
||||||
|
dialogqueue.h
|
||||||
|
root.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(squawk PRIVATE ${SOURCE_FILES})
|
666
main/application.cpp
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
// 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 "application.h"
|
||||||
|
|
||||||
|
Application::Application(Core::Squawk* p_core):
|
||||||
|
QObject(),
|
||||||
|
availability(Shared::Availability::offline),
|
||||||
|
core(p_core),
|
||||||
|
squawk(nullptr),
|
||||||
|
notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"),
|
||||||
|
roster(),
|
||||||
|
conversations(),
|
||||||
|
dialogueQueue(roster),
|
||||||
|
nowQuitting(false),
|
||||||
|
destroyingSquawk(false),
|
||||||
|
storage(),
|
||||||
|
trayIcon(nullptr),
|
||||||
|
actionQuit(Shared::icon("application-exit"), tr("Quit")),
|
||||||
|
actionToggle(QApplication::windowIcon(), tr("Minimize to tray")),
|
||||||
|
expandedPaths()
|
||||||
|
{
|
||||||
|
connect(&actionQuit, &QAction::triggered, this, &Application::quit);
|
||||||
|
connect(&actionToggle, &QAction::triggered, this, &Application::toggleSquawk);
|
||||||
|
|
||||||
|
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
||||||
|
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
||||||
|
connect(&roster, &Models::Roster::addedElement, this, &Application::onAddedElement);
|
||||||
|
|
||||||
|
|
||||||
|
//connecting myself to the backend
|
||||||
|
connect(this, &Application::changeState, core, &Core::Squawk::changeState);
|
||||||
|
connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined);
|
||||||
|
connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin);
|
||||||
|
connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact);
|
||||||
|
connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact);
|
||||||
|
connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage);
|
||||||
|
connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage);
|
||||||
|
connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage);
|
||||||
|
connect(this, &Application::setEncryption, core, &Core::Squawk::setContactEncryption);
|
||||||
|
connect(&roster, &Models::Roster::requestArchive,
|
||||||
|
std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3));
|
||||||
|
|
||||||
|
connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest);
|
||||||
|
connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid);
|
||||||
|
|
||||||
|
|
||||||
|
//coonecting backend to myself
|
||||||
|
connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage);
|
||||||
|
connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive);
|
||||||
|
connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount);
|
||||||
|
connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount);
|
||||||
|
connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addContact, &roster, &Models::Roster::addContact);
|
||||||
|
connect(core, &Core::Squawk::addGroup, this, &Application::addGroup);
|
||||||
|
connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup);
|
||||||
|
connect(core, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact);
|
||||||
|
connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence);
|
||||||
|
connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom);
|
||||||
|
connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom);
|
||||||
|
connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom);
|
||||||
|
connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant);
|
||||||
|
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false));
|
||||||
|
connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true));
|
||||||
|
connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress);
|
||||||
|
connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword);
|
||||||
|
connect(core, &Core::Squawk::ready, this, &Application::readSettings);
|
||||||
|
|
||||||
|
QDBusConnection sys = QDBusConnection::sessionBus();
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"NotificationClosed",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationClosed(quint32, quint32))
|
||||||
|
);
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"ActionInvoked",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationInvoked(quint32, const QString&))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::~Application() {}
|
||||||
|
|
||||||
|
void Application::quit() {
|
||||||
|
if (!nowQuitting) {
|
||||||
|
nowQuitting = true;
|
||||||
|
emit quitting();
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people
|
||||||
|
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
||||||
|
disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
itr->second->close();
|
||||||
|
}
|
||||||
|
conversations.clear();
|
||||||
|
dialogueQueue.quit();
|
||||||
|
|
||||||
|
if (squawk != nullptr)
|
||||||
|
squawk->close();
|
||||||
|
|
||||||
|
if (trayIcon != nullptr) {
|
||||||
|
trayIcon->deleteLater();
|
||||||
|
trayIcon = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!destroyingSquawk)
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::checkForTheLastWindow() {
|
||||||
|
if (QApplication::topLevelWidgets().size() > 0)
|
||||||
|
emit readyToQuit();
|
||||||
|
else
|
||||||
|
connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::createMainWindow() {
|
||||||
|
if (squawk == nullptr) {
|
||||||
|
squawk = new Squawk(roster);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::notify, this, &Application::notify);
|
||||||
|
connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription);
|
||||||
|
connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation);
|
||||||
|
connect(squawk, &Squawk::openConversation, this, &Application::openConversation);
|
||||||
|
connect(squawk, &Squawk::changeState, this, &Application::setState);
|
||||||
|
connect(squawk, &Squawk::changeTray, this, &Application::onChangeTray);
|
||||||
|
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
|
||||||
|
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
|
||||||
|
connect(squawk, &Squawk::quit, this, &Application::quit);
|
||||||
|
connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest);
|
||||||
|
connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest);
|
||||||
|
connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount);
|
||||||
|
connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
|
connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest);
|
||||||
|
connect(squawk, &Squawk::requestInfo, core, &Core::Squawk::requestInfo);
|
||||||
|
connect(squawk, &Squawk::updateInfo, core, &Core::Squawk::updateInfo);
|
||||||
|
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo);
|
||||||
|
|
||||||
|
dialogueQueue.setParentWidnow(squawk);
|
||||||
|
squawk->stateChanged(availability);
|
||||||
|
squawk->raise();
|
||||||
|
squawk->show();
|
||||||
|
squawk->activateWindow();
|
||||||
|
|
||||||
|
for (const std::list<QString>& entry : expandedPaths) {
|
||||||
|
QModelIndex ind = roster.getIndexByPath(entry);
|
||||||
|
if (ind.isValid())
|
||||||
|
squawk->expand(ind);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
|
||||||
|
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkClosing() {
|
||||||
|
dialogueQueue.setParentWidnow(nullptr);
|
||||||
|
|
||||||
|
if (!nowQuitting) {
|
||||||
|
disconnect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyingSquawk = true;
|
||||||
|
squawk->deleteLater();
|
||||||
|
squawk = nullptr;
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
if (!nowQuitting && QSystemTrayIcon::isSystemTrayAvailable() && settings.value("tray", false).toBool()) {
|
||||||
|
if (settings.value("hideTray", false).toBool()) {
|
||||||
|
createTrayIcon();
|
||||||
|
}
|
||||||
|
actionToggle.setText(tr("Show Squawk"));
|
||||||
|
} else {
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkDestroyed() {
|
||||||
|
destroyingSquawk = false;
|
||||||
|
if (nowQuitting)
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onChangeTray(bool enabled, bool hide) {
|
||||||
|
if (enabled) {
|
||||||
|
if (trayIcon == nullptr) {
|
||||||
|
if (!hide || squawk == nullptr)
|
||||||
|
createTrayIcon();
|
||||||
|
} else {
|
||||||
|
if (hide && squawk != nullptr) {
|
||||||
|
trayIcon->deleteLater();
|
||||||
|
trayIcon = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (trayIcon != nullptr) {
|
||||||
|
trayIcon->deleteLater();
|
||||||
|
trayIcon = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::createTrayIcon() {
|
||||||
|
trayIcon = new QSystemTrayIcon();
|
||||||
|
|
||||||
|
QMenu* trayIconMenu = new QMenu();
|
||||||
|
trayIconMenu->addAction(&actionToggle);
|
||||||
|
trayIconMenu->addAction(&actionQuit);
|
||||||
|
|
||||||
|
trayIcon->setContextMenu(trayIconMenu);
|
||||||
|
trayIcon->setIcon(QApplication::windowIcon().pixmap(32, 32));
|
||||||
|
trayIcon->setToolTip(QApplication::applicationDisplayName());
|
||||||
|
|
||||||
|
connect(trayIcon, &QSystemTrayIcon::activated, this, &Application::trayClicked);
|
||||||
|
connect(trayIcon, &QSystemTrayIcon::destroyed, trayIconMenu, &QMenu::deleteLater);
|
||||||
|
|
||||||
|
trayIcon->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case QSystemTrayIcon::Trigger:
|
||||||
|
case QSystemTrayIcon::DoubleClick:
|
||||||
|
case QSystemTrayIcon::MiddleClick:
|
||||||
|
toggleSquawk();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::toggleSquawk() {
|
||||||
|
QSettings settings;
|
||||||
|
if (squawk == nullptr) {
|
||||||
|
createMainWindow();
|
||||||
|
if (settings.value("hideTray", false).toBool()) {
|
||||||
|
trayIcon->deleteLater();
|
||||||
|
trayIcon = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionToggle.setText(tr("Minimize to tray"));
|
||||||
|
} else {
|
||||||
|
squawk->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onItemCollapsed(const QModelIndex& index) {
|
||||||
|
std::list<QString> address = roster.getItemPath(index);
|
||||||
|
if (address.size() > 0)
|
||||||
|
expandedPaths.erase(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onItemExpanded(const QModelIndex& index) {
|
||||||
|
std::list<QString> address = roster.getItemPath(index);
|
||||||
|
if (address.size() > 0)
|
||||||
|
expandedPaths.insert(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onAddedElement(const std::list<QString>& path) {
|
||||||
|
if (squawk != nullptr && expandedPaths.count(path) > 0) {
|
||||||
|
QModelIndex index = roster.getIndexByPath(path);
|
||||||
|
if (index.isValid())
|
||||||
|
squawk->expand(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::notify(const QString& account, const Shared::Message& msg) {
|
||||||
|
QString jid = msg.getPenPalJid();
|
||||||
|
QString name = QString(roster.getContactName(account, jid));
|
||||||
|
QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource()));
|
||||||
|
QVariantList args;
|
||||||
|
args << QString();
|
||||||
|
|
||||||
|
uint32_t notificationId = qHash(msg.getId());
|
||||||
|
args << notificationId;
|
||||||
|
if (path.size() > 0)
|
||||||
|
args << path;
|
||||||
|
else
|
||||||
|
args << QString("mail-message"); //TODO should here better be unknown user icon?
|
||||||
|
|
||||||
|
if (msg.getType() == Shared::Message::groupChat)
|
||||||
|
args << msg.getFromResource() + tr(" from ") + name;
|
||||||
|
else
|
||||||
|
args << name;
|
||||||
|
|
||||||
|
QString body(msg.getBody());
|
||||||
|
QString oob(msg.getOutOfBandUrl());
|
||||||
|
if (body == oob) {
|
||||||
|
body = tr("Attached file");
|
||||||
|
}
|
||||||
|
|
||||||
|
args << body;
|
||||||
|
args << QStringList({
|
||||||
|
"markAsRead", tr("Mark as Read"),
|
||||||
|
"openConversation", tr("Open conversation")
|
||||||
|
});
|
||||||
|
args << QVariantMap({
|
||||||
|
{"desktop-entry", qApp->desktopFileName()},
|
||||||
|
{"category", QString("message")},
|
||||||
|
{"urgency", 1},
|
||||||
|
// {"sound-file", "/path/to/macaw/squawk"},
|
||||||
|
{"sound-name", QString("message-new-instant")}
|
||||||
|
});
|
||||||
|
args << -1;
|
||||||
|
notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
||||||
|
|
||||||
|
storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId())));
|
||||||
|
|
||||||
|
if (squawk != nullptr)
|
||||||
|
QApplication::alert(squawk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationClosed(quint32 id, quint32 reason) {
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
|
||||||
|
//TODO may ba also mark as read?
|
||||||
|
}
|
||||||
|
if (reason != 1) { //just expired, can be activated again from history, so no removing for now
|
||||||
|
storage.erase(id);
|
||||||
|
qDebug() << "Notification" << id << "was closed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationInvoked(quint32 id, const QString& action) {
|
||||||
|
qDebug() << "Notification" << id << action << "request";
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (action == "markAsRead")
|
||||||
|
roster.markMessageAsRead(itr->second.first, itr->second.second);
|
||||||
|
else if (action == "openConversation")
|
||||||
|
focusConversation(itr->second.first, "", itr->second.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::unreadMessagesCountChanged(int count) {
|
||||||
|
QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update");
|
||||||
|
signal << qApp->desktopFileName() + QLatin1String(".desktop");
|
||||||
|
signal << QVariantMap ({
|
||||||
|
{"count-visible", count != 0},
|
||||||
|
{"count", count}
|
||||||
|
});
|
||||||
|
QDBusConnection::sessionBus().send(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) {
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
if (squawk->currentConversationId() != id) {
|
||||||
|
QModelIndex index = roster.getContactIndex(id.account, id.name, resource);
|
||||||
|
squawk->select(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk->isMinimized())
|
||||||
|
squawk->showNormal();
|
||||||
|
else
|
||||||
|
squawk->show();
|
||||||
|
|
||||||
|
squawk->raise();
|
||||||
|
squawk->activateWindow();
|
||||||
|
} else {
|
||||||
|
openConversation(id, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
SHARED_UNUSED(messageId);
|
||||||
|
//TODO focus messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::setState(Shared::Availability p_availability) {
|
||||||
|
if (availability != p_availability) {
|
||||||
|
availability = p_availability;
|
||||||
|
emit changeState(availability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::stateChanged(Shared::Availability state) {
|
||||||
|
availability = state;
|
||||||
|
if (squawk != nullptr)
|
||||||
|
squawk->stateChanged(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::readSettings() {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
int avail;
|
||||||
|
if (settings.contains("availability"))
|
||||||
|
avail = settings.value("availability").toInt();
|
||||||
|
else
|
||||||
|
avail = static_cast<int>(Shared::Availability::online);
|
||||||
|
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
QStringList entries = settings.allKeys();
|
||||||
|
for (const QString& entry : entries) {
|
||||||
|
QStringList p = entry.split("/");
|
||||||
|
if (p.last() == "expanded" && settings.value(entry, false).toBool()) {
|
||||||
|
p.pop_back();
|
||||||
|
expandedPaths.emplace(p.begin(), p.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
|
createMainWindow();
|
||||||
|
|
||||||
|
if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool())
|
||||||
|
createTrayIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::writeSettings() {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.setValue("availability", static_cast<int>(availability));
|
||||||
|
|
||||||
|
settings.remove("roster");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
for (const std::list<QString>& address : expandedPaths) {
|
||||||
|
QString path = "";
|
||||||
|
for (const QString& hop : address)
|
||||||
|
path += hop + "/";
|
||||||
|
|
||||||
|
path += "expanded";
|
||||||
|
settings.setValue(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::requestPassword(const QString& account, bool authenticationError) {
|
||||||
|
if (authenticationError)
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askCredentials);
|
||||||
|
else
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationClosed() {
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
Models::Roster::ElId id(conv->getAccount(), conv->getJid());
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end())
|
||||||
|
conversations.erase(itr);
|
||||||
|
|
||||||
|
if (conv->isMuc) {
|
||||||
|
Room* room = static_cast<Room*>(conv);
|
||||||
|
if (!room->autoJoined())
|
||||||
|
emit setRoomJoined(id.account, id.name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) {
|
||||||
|
Models::Item::Type type = roster.getContactType(id);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Models::Item::contact:
|
||||||
|
if (subscribe)
|
||||||
|
emit subscribeContact(id.account, id.name, "");
|
||||||
|
else
|
||||||
|
emit unsubscribeContact(id.account, id.name, "");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Models::Item::room:
|
||||||
|
setRoomAutoJoin(id.account, id.name, subscribe);
|
||||||
|
if (!isConverstationOpened(id))
|
||||||
|
emit setRoomJoined(id.account, id.name, subscribe);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::subscribeConversation(Conversation* conv) {
|
||||||
|
connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage);
|
||||||
|
connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage);
|
||||||
|
connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend);
|
||||||
|
connect(conv, &Conversation::setEncryption, this, &Application::onConversationSetEncryption);
|
||||||
|
connect(conv, &Conversation::notifyableMessage, this, &Application::notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) {
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
Models::Account* acc = roster.getAccount(id.account);
|
||||||
|
Conversation* conv = nullptr;
|
||||||
|
bool created = false;
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conv = itr->second;
|
||||||
|
} else {
|
||||||
|
Models::Element* el = roster.getElement(id);
|
||||||
|
if (el != nullptr) {
|
||||||
|
if (el->type == Models::Item::room) {
|
||||||
|
created = true;
|
||||||
|
Models::Room* room = static_cast<Models::Room*>(el);
|
||||||
|
conv = new Room(acc, room);
|
||||||
|
if (!room->getJoined())
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
} else if (el->type == Models::Item::contact) {
|
||||||
|
created = true;
|
||||||
|
conv = new Chat(acc, static_cast<Models::Contact*>(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conv != nullptr) {
|
||||||
|
if (created) {
|
||||||
|
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
subscribeConversation(conv);
|
||||||
|
conversations.insert(std::make_pair(id, conv));
|
||||||
|
}
|
||||||
|
|
||||||
|
conv->show();
|
||||||
|
conv->raise();
|
||||||
|
conv->activateWindow();
|
||||||
|
|
||||||
|
if (resource.size() > 0)
|
||||||
|
conv->setPalResource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationMessage(const Shared::Message& msg) {
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.addMessage(acc, msg);
|
||||||
|
emit sendMessage(acc, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) {
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.changeMessage(acc, msg.getPenPalJid(), originalId, {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::pending)}
|
||||||
|
});
|
||||||
|
emit replaceMessage(acc, originalId, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationSetEncryption(Shared::EncryptionProtocol value) {
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
QString jid = conv->getJid();
|
||||||
|
|
||||||
|
emit setEncryption(acc, jid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationResend(const QString& id) {
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
QString jid = conv->getJid();
|
||||||
|
|
||||||
|
emit resendMessage(acc, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkOpenedConversation() {
|
||||||
|
subscribeConversation(squawk->currentConversation);
|
||||||
|
Models::Roster::ElId id = squawk->currentConversationId();
|
||||||
|
|
||||||
|
const Models::Element* el = roster.getElementConst(id);
|
||||||
|
if (el != nullptr && el->isRoom() && !static_cast<const Models::Room*>(el)->getJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::removeAccount(const QString& account) {
|
||||||
|
Conversations::const_iterator itr = conversations.begin();
|
||||||
|
while (itr != conversations.end()) {
|
||||||
|
if (itr->first.account == account) {
|
||||||
|
Conversations::const_iterator lItr = itr;
|
||||||
|
++itr;
|
||||||
|
Conversation* conv = lItr->second;
|
||||||
|
disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
conv->close();
|
||||||
|
conversations.erase(lItr);
|
||||||
|
} else {
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk != nullptr && squawk->currentConversationId().account == account)
|
||||||
|
squawk->closeCurrentConversation();
|
||||||
|
|
||||||
|
roster.removeAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data) {
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
|
QString attr = itr.key();
|
||||||
|
roster.updateAccount(account, attr, *itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addGroup(const QString& account, const QString& name) {
|
||||||
|
roster.addGroup(account, name);
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = roster.getAccountIndex(account);
|
||||||
|
squawk->expand(ind);
|
||||||
|
if (settings.value(name + "/expanded", false).toBool())
|
||||||
|
squawk->expand(roster.getGroupIndex(account, name));
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::isConverstationOpened(const Models::Roster::ElId& id) const {
|
||||||
|
return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);}
|
134
main/application.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// 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 APPLICATION_H
|
||||||
|
#define APPLICATION_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
#include "dialogqueue.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include "ui/squawk.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
#include "ui/widgets/conversation.h"
|
||||||
|
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/enums.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class Application : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Application(Core::Squawk* core);
|
||||||
|
~Application();
|
||||||
|
|
||||||
|
bool isConverstationOpened(const Models::Roster::ElId& id) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
||||||
|
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
||||||
|
|
||||||
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
void setEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value);
|
||||||
|
|
||||||
|
void quitting();
|
||||||
|
void readyToQuit();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void readSettings();
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
void unreadMessagesCountChanged(int count);
|
||||||
|
void setState(Shared::Availability availability);
|
||||||
|
|
||||||
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
|
void removeAccount(const QString& account);
|
||||||
|
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
||||||
|
|
||||||
|
void addGroup(const QString& account, const QString& name);
|
||||||
|
|
||||||
|
void requestPassword(const QString& account, bool authenticationError);
|
||||||
|
|
||||||
|
void writeSettings();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConversationClosed();
|
||||||
|
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
||||||
|
void onSquawkOpenedConversation();
|
||||||
|
void onConversationMessage(const Shared::Message& msg);
|
||||||
|
void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg);
|
||||||
|
void onConversationResend(const QString& id);
|
||||||
|
void stateChanged(Shared::Availability state);
|
||||||
|
void onSquawkClosing();
|
||||||
|
void onSquawkDestroyed();
|
||||||
|
void onNotificationClosed(quint32 id, quint32 reason);
|
||||||
|
void onNotificationInvoked(quint32 id, const QString& action);
|
||||||
|
void onChangeTray(bool enabled, bool hide);
|
||||||
|
void trayClicked(QSystemTrayIcon::ActivationReason reason);
|
||||||
|
void toggleSquawk();
|
||||||
|
void onItemExpanded(const QModelIndex& index);
|
||||||
|
void onItemCollapsed(const QModelIndex& index);
|
||||||
|
void onAddedElement(const std::list<QString>& path);
|
||||||
|
void onConversationSetEncryption(Shared::EncryptionProtocol value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createMainWindow();
|
||||||
|
void subscribeConversation(Conversation* conv);
|
||||||
|
void checkForTheLastWindow();
|
||||||
|
void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = "");
|
||||||
|
void createTrayIcon();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||||
|
typedef std::map<uint32_t, std::pair<Models::Roster::ElId, QString>> Notifications;
|
||||||
|
|
||||||
|
Shared::Availability availability;
|
||||||
|
Core::Squawk* core;
|
||||||
|
Squawk* squawk;
|
||||||
|
QDBusInterface notifications;
|
||||||
|
Models::Roster roster;
|
||||||
|
Conversations conversations;
|
||||||
|
DialogQueue dialogueQueue;
|
||||||
|
bool nowQuitting;
|
||||||
|
bool destroyingSquawk;
|
||||||
|
Notifications storage;
|
||||||
|
QSystemTrayIcon* trayIcon;
|
||||||
|
QAction actionQuit;
|
||||||
|
QAction actionToggle;
|
||||||
|
std::set<std::list<QString>> expandedPaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPLICATION_H
|
187
main/dialogqueue.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// 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 "dialogqueue.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
DialogQueue::DialogQueue(const Models::Roster& p_roster):
|
||||||
|
QObject(),
|
||||||
|
currentSource(),
|
||||||
|
currentAction(none),
|
||||||
|
queue(),
|
||||||
|
collection(queue.get<0>()),
|
||||||
|
sequence(queue.get<1>()),
|
||||||
|
prompt(nullptr),
|
||||||
|
parent(nullptr),
|
||||||
|
roster(p_roster)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::~DialogQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::quit()
|
||||||
|
{
|
||||||
|
queue.clear();
|
||||||
|
if (currentAction != none) {
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::setParentWidnow(QMainWindow* p_parent)
|
||||||
|
{
|
||||||
|
parent = p_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogQueue::addAction(const QString& source, DialogQueue::Action action)
|
||||||
|
{
|
||||||
|
if (action == none) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (currentAction == none) {
|
||||||
|
currentAction = action;
|
||||||
|
currentSource = source;
|
||||||
|
performNextAction();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (currentAction != action || currentSource != source) {
|
||||||
|
std::pair<Queue::iterator, bool> result = queue.emplace(source, action);
|
||||||
|
return result.second;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogQueue::cancelAction(const QString& source, DialogQueue::Action action)
|
||||||
|
{
|
||||||
|
if (source == currentSource && action == currentAction) {
|
||||||
|
actionDone();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Collection::iterator itr = collection.find(ActionId{source, action});
|
||||||
|
if (itr != collection.end()) {
|
||||||
|
collection.erase(itr);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::performNextAction()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
actionDone();
|
||||||
|
break;
|
||||||
|
case askPassword: {
|
||||||
|
QInputDialog* dialog = new QInputDialog(parent);
|
||||||
|
prompt = dialog;
|
||||||
|
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
||||||
|
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
||||||
|
dialog->setInputMode(QInputDialog::TextInput);
|
||||||
|
dialog->setTextEchoMode(QLineEdit::Password);
|
||||||
|
dialog->setLabelText(tr("Input the password for account %1").arg(currentSource));
|
||||||
|
dialog->setWindowTitle(tr("Password for account %1").arg(currentSource));
|
||||||
|
dialog->setTextValue("");
|
||||||
|
dialog->exec();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case askCredentials: {
|
||||||
|
CredentialsPrompt* dialog = new CredentialsPrompt(parent);
|
||||||
|
prompt = dialog;
|
||||||
|
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
||||||
|
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
||||||
|
const Models::Account* acc = roster.getAccountConst(currentSource);
|
||||||
|
dialog->setAccount(currentSource);
|
||||||
|
dialog->setLogin(acc->getLogin());
|
||||||
|
dialog->setPassword(acc->getPassword());
|
||||||
|
dialog->exec();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::onPropmtAccepted()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
break;
|
||||||
|
case askPassword: {
|
||||||
|
QInputDialog* dialog = static_cast<QInputDialog*>(prompt);
|
||||||
|
emit responsePassword(currentSource, dialog->textValue());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case askCredentials: {
|
||||||
|
CredentialsPrompt* dialog = static_cast<CredentialsPrompt*>(prompt);
|
||||||
|
emit modifyAccountRequest(currentSource, {
|
||||||
|
{"login", dialog->getLogin()},
|
||||||
|
{"password", dialog->getPassword()}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::onPropmtRejected()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
break;
|
||||||
|
case askPassword:
|
||||||
|
case askCredentials:
|
||||||
|
emit disconnectAccount(currentSource);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::actionDone()
|
||||||
|
{
|
||||||
|
prompt->deleteLater();
|
||||||
|
prompt = nullptr;
|
||||||
|
|
||||||
|
if (queue.empty()) {
|
||||||
|
currentAction = none;
|
||||||
|
currentSource = "";
|
||||||
|
} else {
|
||||||
|
Sequence::iterator itr = sequence.begin();
|
||||||
|
currentAction = itr->action;
|
||||||
|
currentSource = itr->source;
|
||||||
|
sequence.erase(itr);
|
||||||
|
performNextAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::ActionId::ActionId(const QString& p_source, DialogQueue::Action p_action):
|
||||||
|
source(p_source),
|
||||||
|
action(p_action) {}
|
||||||
|
|
||||||
|
bool DialogQueue::ActionId::operator < (const DialogQueue::ActionId& other) const
|
||||||
|
{
|
||||||
|
if (action == other.action) {
|
||||||
|
return source < other.source;
|
||||||
|
} else {
|
||||||
|
return action < other.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::ActionId::ActionId(const DialogQueue::ActionId& other):
|
||||||
|
source(other.source),
|
||||||
|
action(other.action) {}
|
101
main/dialogqueue.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// 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 DIALOGQUEUE_H
|
||||||
|
#define DIALOGQUEUE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
|
||||||
|
#include <ui/widgets/accounts/credentialsprompt.h>
|
||||||
|
#include <ui/models/roster.h>
|
||||||
|
|
||||||
|
class DialogQueue : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Action {
|
||||||
|
none,
|
||||||
|
askPassword,
|
||||||
|
askCredentials
|
||||||
|
};
|
||||||
|
|
||||||
|
DialogQueue(const Models::Roster& roster);
|
||||||
|
~DialogQueue();
|
||||||
|
|
||||||
|
bool addAction(const QString& source, Action action);
|
||||||
|
bool cancelAction(const QString& source, Action action);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
||||||
|
void responsePassword(const QString& account, const QString& password);
|
||||||
|
void disconnectAccount(const QString&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setParentWidnow(QMainWindow* parent);
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void performNextAction();
|
||||||
|
void actionDone();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onPropmtAccepted();
|
||||||
|
void onPropmtRejected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString currentSource;
|
||||||
|
Action currentAction;
|
||||||
|
|
||||||
|
struct ActionId {
|
||||||
|
public:
|
||||||
|
ActionId(const QString& p_source, Action p_action);
|
||||||
|
ActionId(const ActionId& other);
|
||||||
|
|
||||||
|
const QString source;
|
||||||
|
const Action action;
|
||||||
|
|
||||||
|
bool operator < (const ActionId& other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef boost::multi_index_container <
|
||||||
|
ActionId,
|
||||||
|
boost::multi_index::indexed_by <
|
||||||
|
boost::multi_index::ordered_unique <
|
||||||
|
boost::multi_index::identity <ActionId>
|
||||||
|
>,
|
||||||
|
boost::multi_index::sequenced<>
|
||||||
|
>
|
||||||
|
> Queue;
|
||||||
|
|
||||||
|
typedef Queue::nth_index<0>::type Collection;
|
||||||
|
typedef Queue::nth_index<1>::type Sequence;
|
||||||
|
|
||||||
|
Queue queue;
|
||||||
|
Collection& collection;
|
||||||
|
Sequence& sequence;
|
||||||
|
|
||||||
|
QDialog* prompt;
|
||||||
|
QMainWindow* parent;
|
||||||
|
const Models::Roster& roster;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIALOGQUEUE_H
|
58
main/main.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "root.h"
|
||||||
|
#include "shared/messageinfo.h"
|
||||||
|
#include "shared/identity.h"
|
||||||
|
#include "shared/info.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
#include <QXmppOmemoStorage.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
|
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
||||||
|
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||||
|
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||||
|
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
||||||
|
qRegisterMetaType<std::list<QString>>("std::list<QString>");
|
||||||
|
qRegisterMetaType<std::set<QString>>("std::set<QString>");
|
||||||
|
qRegisterMetaType<std::list<Shared::Identity>>("std::list<Shared::Identity>");
|
||||||
|
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||||
|
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
||||||
|
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
||||||
|
qRegisterMetaType<Shared::EncryptionProtocol>("Shared::EncryptionProtocol");
|
||||||
|
qRegisterMetaType<Shared::KeyInfo>("Shared::KeyInfo");
|
||||||
|
qRegisterMetaType<Shared::Info>("Shared::Info");
|
||||||
|
qRegisterMetaType<Shared::TrustLevel>("Shared::TrustLevel");
|
||||||
|
#ifdef WITH_OMEMO
|
||||||
|
qRegisterMetaType<QXmppOmemoStorage::OwnDevice>("QXmppOmemoStorage::OwnDevice");
|
||||||
|
qRegisterMetaTypeStreamOperators<QXmppOmemoStorage::OwnDevice>("QXmppOmemoStorage::OwnDevice");
|
||||||
|
qRegisterMetaType<QXmppOmemoStorage::Device>("QXmppOmemoStorage::Device");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Root app(argc, argv);
|
||||||
|
if (!app.initializeSettings())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return app.run();
|
||||||
|
}
|
||||||
|
|
189
main/root.cpp
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "root.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <shared/pathcheck.h>
|
||||||
|
|
||||||
|
const std::vector<unsigned int> Root::appIconSizes({
|
||||||
|
16, 24, 32, 48, 64, 96, 128, 256, 512
|
||||||
|
});
|
||||||
|
|
||||||
|
Root::Root(int& argc, char *argv[]) :
|
||||||
|
QApplication(argc, argv),
|
||||||
|
signalCatcher(this),
|
||||||
|
defaultTranslator(),
|
||||||
|
currentTranslator(),
|
||||||
|
appIcon(),
|
||||||
|
settings(),
|
||||||
|
componentsInitialized(false),
|
||||||
|
global(nullptr),
|
||||||
|
coreThread(nullptr),
|
||||||
|
core(nullptr),
|
||||||
|
gui(nullptr)
|
||||||
|
{
|
||||||
|
setApplicationName("squawk");
|
||||||
|
setOrganizationName("macaw.me");
|
||||||
|
setApplicationDisplayName("Squawk");
|
||||||
|
setApplicationVersion("0.2.3");
|
||||||
|
setDesktopFileName("squawk");
|
||||||
|
|
||||||
|
initializeTranslation();
|
||||||
|
initializeAppIcon();
|
||||||
|
|
||||||
|
global = new Shared::Global(); //important to instantiate after initialization of translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
Root::~Root() {
|
||||||
|
if (componentsInitialized) {
|
||||||
|
delete gui;
|
||||||
|
if (core != nullptr)
|
||||||
|
delete core;
|
||||||
|
|
||||||
|
delete coreThread;
|
||||||
|
}
|
||||||
|
delete global;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Root::initializeTranslation() {
|
||||||
|
defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
|
installTranslator(&defaultTranslator);
|
||||||
|
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
bool found = false;
|
||||||
|
for (QString share : shares) {
|
||||||
|
found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
|
|
||||||
|
installTranslator(¤tTranslator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Root::initializeAppIcon() {
|
||||||
|
for (std::vector<unsigned int>::size_type i = 0; i < appIconSizes.size(); ++i)
|
||||||
|
appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i]));
|
||||||
|
|
||||||
|
Root::setWindowIcon(appIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Root::initializeSettings() {
|
||||||
|
QVariant vs = settings.value("style");
|
||||||
|
if (vs.isValid()) {
|
||||||
|
QString style = vs.toString().toLower();
|
||||||
|
if (style != "system")
|
||||||
|
Shared::Global::setStyle(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Shared::Global::supported("colorSchemeTools")) {
|
||||||
|
QVariant vt = settings.value("theme");
|
||||||
|
if (vt.isValid()) {
|
||||||
|
QString theme = vt.toString();
|
||||||
|
if (theme.toLower() != "system")
|
||||||
|
Shared::Global::setTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = Shared::downloadsPathCheck();
|
||||||
|
if (path.size() > 0) {
|
||||||
|
settings.setValue("downloadsPath", path);
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't initialize directory for downloads, quitting";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Root::run() {
|
||||||
|
if (!componentsInitialized)
|
||||||
|
initializeComponents();
|
||||||
|
|
||||||
|
coreThread->start();
|
||||||
|
int result = exec();
|
||||||
|
qDebug("Event loop stopped");
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
processEvents(); //I dont like all of this mess
|
||||||
|
if (coreThread->isRunning()) { //but it's the best solution for now
|
||||||
|
if (core != nullptr) { //Ideally, following line should never appear in the log
|
||||||
|
qDebug() << "Core is still seems to be running, killing manually";
|
||||||
|
core->deleteLater();
|
||||||
|
coreThread->quit();
|
||||||
|
processEvents();
|
||||||
|
core = nullptr;
|
||||||
|
}
|
||||||
|
coreThread->wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Root::initializeComponents() {
|
||||||
|
core = new Core::Squawk();
|
||||||
|
coreThread = new QThread();
|
||||||
|
core->moveToThread(coreThread);
|
||||||
|
gui = new Application(core);
|
||||||
|
|
||||||
|
QObject::connect(&signalCatcher, &SignalCatcher::interrupt, gui, &Application::quit);
|
||||||
|
|
||||||
|
QObject::connect(coreThread, &QThread::started, core, &Core::Squawk::start);
|
||||||
|
QObject::connect(gui, &Application::quitting, core, &Core::Squawk::stop);
|
||||||
|
|
||||||
|
QObject::connect(core, &Core::Squawk::quit, core, &Core::Squawk::deleteLater);
|
||||||
|
QObject::connect(core, &Core::Squawk::destroyed, this, &Root::onCoreDestroyed);
|
||||||
|
QObject::connect(core, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreThread, &QThread::finished, this, &Root::quit, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
componentsInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Root::notify(QObject* receiver, QEvent* e) {
|
||||||
|
try {
|
||||||
|
return QApplication::notify(receiver, e);
|
||||||
|
} catch(const std::runtime_error& e) {
|
||||||
|
qDebug() << "std::runtime_error in thread:" << QThread::currentThreadId();
|
||||||
|
qDebug() << "error message:" << e.what();
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
qDebug() << "std::exception in thread:" << QThread::currentThreadId();
|
||||||
|
qDebug() << "error message:" << e.what();
|
||||||
|
} catch(const int& e) {
|
||||||
|
qDebug() << "integer exception in thread:" << QThread::currentThreadId();
|
||||||
|
qDebug() << "thrown integer:" << std::to_string(e).c_str();
|
||||||
|
} catch(...) {
|
||||||
|
qDebug() << "unhandled exception thread:" << QThread::currentThreadId();
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Squawk is crashing...";
|
||||||
|
exit(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Root::onCoreDestroyed() {
|
||||||
|
core = nullptr;
|
||||||
|
}
|
70
main/root.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef ROOT_H
|
||||||
|
#define ROOT_H
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <core/squawk.h>
|
||||||
|
#include <core/signalcatcher.h>
|
||||||
|
|
||||||
|
#include <shared/global.h>
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
class Root : public QApplication {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Root(int& argc, char* argv[]);
|
||||||
|
~Root();
|
||||||
|
bool notify(QObject* receiver, QEvent* e) override;
|
||||||
|
int run();
|
||||||
|
|
||||||
|
bool initializeSettings();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCoreDestroyed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initializeTranslation();
|
||||||
|
void initializeAppIcon();
|
||||||
|
void initializeComponents();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const std::vector<unsigned int> appIconSizes;
|
||||||
|
|
||||||
|
SignalCatcher signalCatcher;
|
||||||
|
QTranslator defaultTranslator;
|
||||||
|
QTranslator currentTranslator;
|
||||||
|
QIcon appIcon;
|
||||||
|
QSettings settings;
|
||||||
|
bool componentsInitialized;
|
||||||
|
|
||||||
|
Shared::Global* global;
|
||||||
|
QThread* coreThread;
|
||||||
|
Core::Squawk* core;
|
||||||
|
Application* gui;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ROOT_H
|
@ -1,23 +1,26 @@
|
|||||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||||
pkgname=squawk
|
pkgname=squawk
|
||||||
pkgver=0.1.5
|
pkgver=0.2.3
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
||||||
arch=('i686' 'x86_64')
|
arch=('i686' 'x86_64')
|
||||||
url="https://git.macaw.me/blue/squawk"
|
url="https://git.macaw.me/blue/squawk"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
|
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp-qt5')
|
||||||
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
|
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost')
|
||||||
optdepends=('kwallet: secure password storage (requires rebuild)')
|
optdepends=('kwallet5: secure password storage (requires rebuild)'
|
||||||
|
'kconfig5: system themes support (requires rebuild)'
|
||||||
|
'kconfigwidgets5: system themes support (requires rebuild)'
|
||||||
|
'kio5: better show in folder action (requires rebuild)')
|
||||||
|
|
||||||
source=("$pkgname-$pkgver.tar.gz")
|
source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz")
|
||||||
sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25')
|
sha256sums=('SKIP')
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||||
cmake --build . -j $nproc
|
cmake --build .
|
||||||
}
|
}
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
DESTDIR="$pkgdir/" cmake --build . --target install
|
DESTDIR="$pkgdir/" cmake --install .
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
configure_file(squawk.desktop squawk.desktop COPYONLY)
|
configure_file(squawk.desktop squawk.desktop COPYONLY)
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||||
|
|
||||||
|
configure_file(macaw.me.squawk.appdata.xml macaw.me.squawk.appdata.xml COPYONLY)
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/macaw.me.squawk.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
|
||||||
|
33
packaging/macaw.me.squawk.appdata.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:its="http://www.w3.org/2005/11/its" xmlns="https://specifications.freedesktop.org/metainfo/1.0" type="desktop-application">
|
||||||
|
<id>macaw.me.squawk</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0+</project_license>
|
||||||
|
<name>Squawk</name>
|
||||||
|
<summary>Desktop Qt based XMPP messenger</summary>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Squawk is a lightweight XMPP desktop messenger.
|
||||||
|
The primary objective of this project is to offer
|
||||||
|
you a fast and user-friendly messaging experience
|
||||||
|
that closely aligns with your system’s style, while
|
||||||
|
also minimizing resource consumption.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Squawk is still at a very early stage and might not suit
|
||||||
|
everyone but you are welcome to try it out.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
<launchable type="desktop-id">macaw.me.squawk.desktop</launchable>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>https://macaw.me/projects/squawk/0.2.2.png</image>
|
||||||
|
<caption>View XMPP contacts and conversations</caption>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<url type="homepage">https://macaw.me/projects/squawk/</url>
|
||||||
|
<provides>
|
||||||
|
<binary>squawk</binary>
|
||||||
|
</provides>
|
||||||
|
<update_contact>blue@macaw.me</update_contact>
|
||||||
|
</component>
|
@ -1,4 +1,14 @@
|
|||||||
if (WITH_KIO)
|
if (WITH_KIO)
|
||||||
add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp)
|
add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp)
|
||||||
target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets)
|
target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets)
|
||||||
|
|
||||||
|
install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (WITH_KCONFIG)
|
||||||
|
add_library(colorSchemeTools SHARED colorschemetools.cpp)
|
||||||
|
target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore)
|
||||||
|
target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets)
|
||||||
|
|
||||||
|
install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk)
|
||||||
|
endif()
|
||||||
|
70
plugins/colorschemetools.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QIcon>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
#include <KConfigCore/KSharedConfig>
|
||||||
|
#include <KConfigCore/KConfigGroup>
|
||||||
|
#include <KConfigWidgets/KColorScheme>
|
||||||
|
|
||||||
|
QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection);
|
||||||
|
|
||||||
|
extern "C" QIcon* createPreview(const QString& path) {
|
||||||
|
KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path);
|
||||||
|
QIcon* result = new QIcon();
|
||||||
|
KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig);
|
||||||
|
KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig);
|
||||||
|
KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig);
|
||||||
|
KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig);
|
||||||
|
|
||||||
|
result->addPixmap(createPixmap(16, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background()));
|
||||||
|
result->addPixmap(createPixmap(24, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background()));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void deletePreview(QIcon* icon) {
|
||||||
|
delete icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void colorSchemeName(const QString& path, QString& answer) {
|
||||||
|
KSharedConfigPtr config = KSharedConfig::openConfig(path);
|
||||||
|
KConfigGroup group(config, QStringLiteral("General"));
|
||||||
|
answer = group.readEntry("Name", QFileInfo(path).baseName());
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void createPalette(const QString& path, QPalette& answer) {
|
||||||
|
KSharedConfigPtr config = KSharedConfig::openConfig(path);
|
||||||
|
answer = KColorScheme::createApplicationPalette(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection) {
|
||||||
|
QPixmap pix(size, size);
|
||||||
|
pix.fill(Qt::black);
|
||||||
|
QPainter p;
|
||||||
|
p.begin(&pix);
|
||||||
|
const int itemSize = size / 2 - 1;
|
||||||
|
p.fillRect(1, 1, itemSize, itemSize, window);
|
||||||
|
p.fillRect(1 + itemSize, 1, itemSize, itemSize, button);
|
||||||
|
p.fillRect(1, 1 + itemSize, itemSize, itemSize, view);
|
||||||
|
p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, selection);
|
||||||
|
p.end();
|
||||||
|
return pix;
|
||||||
|
}
|
@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QUrl>
|
#include <QUrl>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <KIO/OpenFileManagerWindowJob>
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
@ -3,11 +3,8 @@ target_sources(squawk PRIVATE resources.qrc)
|
|||||||
configure_file(images/logo.svg squawk.svg COPYONLY)
|
configure_file(images/logo.svg squawk.svg COPYONLY)
|
||||||
configure_file(squawk.rc squawk.rc COPYONLY)
|
configure_file(squawk.rc squawk.rc COPYONLY)
|
||||||
|
|
||||||
if(WIN32)
|
set(CONVERT_BIN magick)
|
||||||
set(CONVERT_BIN magick convert)
|
|
||||||
else(WIN32)
|
|
||||||
set(CONVERT_BIN convert)
|
|
||||||
endif(WIN32)
|
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
@ -18,20 +15,20 @@ if (WIN32)
|
|||||||
set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc")
|
set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc")
|
||||||
set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE)
|
set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE)
|
||||||
target_sources(squawk PRIVATE ${SQUAWK_WIN_RC})
|
target_sources(squawk PRIVATE ${SQUAWK_WIN_RC})
|
||||||
endif(WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset")
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset")
|
||||||
execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND ${CONVERT_BIN} -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
set(MACOSX_BUNDLE_ICON_FILE squawk.icns)
|
set(MACOSX_BUNDLE_ICON_FILE squawk.icns)
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE)
|
set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE)
|
||||||
@ -47,8 +44,8 @@ if (APPLE)
|
|||||||
MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO
|
MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "Squawk"
|
MACOSX_BUNDLE_BUNDLE_NAME "Squawk"
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in)
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in)
|
||||||
endif(APPLE)
|
endif (APPLE)
|
||||||
endif()
|
endif ()
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||||
|
13
resources/images/fallback/dark/big/lock.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 11,3 C 8.784,3 7,4.784 7,7 l 0,4 -2,0 c 0,2.666667 0,5.333333 0,8 4,0 8,0 12,0 l 0,-8 c -0.666667,0 -1.333333,0 -2,0 L 15,7 C 15,4.784 13.216,3 11,3 m 0,1 c 1.662,0 3,1.561 3,3.5 L 14,11 8,11 8,7.5 C 8,5.561 9.338,4 11,4"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 558 B |
13
resources/images/fallback/dark/big/shield.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 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
|
||||||
|
class="ColorScheme-Text"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
14
resources/images/fallback/dark/big/unlock.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="m11 3c-2.216 0-4 1.784-4 4v1h1v-.5c0-1.939 1.338-3.5 3-3.5 1.662 0 3 1.561 3 3.5v3.5h-5-1-1-1-1v1 7h1 10 1v-8h-1-1v-4c0-2.216-1.784-4-4-4m-5 9h10v6h-10v-6"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 491 B |
13
resources/images/fallback/dark/small/lock.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 8,2 C 6.3431375,2 5,3.3431372 5,5 l 0,3 -2,0 0,6 10,0 0,-6 -2,0 0,-3 C 11,3.3431372 9.6568625,2 8,2 Z m 0,1 c 1.1045695,0 2,0.8954305 2,2 L 10,8 6,8 6,5 C 6,3.8954305 6.8954305,3 8,3 Z"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 522 B |
13
resources/images/fallback/dark/small/shield.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 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
|
||||||
|
class="ColorScheme-Text"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
13
resources/images/fallback/dark/small/unlock.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 8 2 C 6.3431375 2 5 3.3431371 5 5 L 5 6 L 6 6 L 6 5 C 6 3.8954305 6.8954305 3 8 3 C 9.1045695 3 10 3.8954305 10 5 L 10 8 L 7 8 L 6 8 L 5 8 L 3 8 L 3 14 L 13 14 L 13 8 L 11 8 L 11 5 C 11 3.3431371 9.6568625 2 8 2 z M 4 9 L 12 9 L 12 13 L 4 13 L 4 9 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 588 B |
13
resources/images/fallback/light/big/lock.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 11,3 C 8.784,3 7,4.784 7,7 l 0,4 -2,0 c 0,2.666667 0,5.333333 0,8 4,0 8,0 12,0 l 0,-8 c -0.666667,0 -1.333333,0 -2,0 L 15,7 C 15,4.784 13.216,3 11,3 m 0,1 c 1.662,0 3,1.561 3,3.5 L 14,11 8,11 8,7.5 C 8,5.561 9.338,4 11,4"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 558 B |
13
resources/images/fallback/light/big/shield.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 8,2 C 8,2 6.5,3.9931391 2,4.4931641 2,4.4931641 2,11.493575 8,14 14,11.493575 14,4.4931641 14,4.4931641 9.5,3.9931391 8,2 8,2 Z m 0,1.0327148 c 1.1902463,1.008525 2.90787,1.6813196 5.134277,2.0200196 C 13.013333,6.1366343 12.897371,6.9523225 12.617188,7.7407227 12.02837,9.3975477 11.341831,10.405496 10.726074,11.130371 9.7719035,12.253646 8.905394,12.708244 8,13.160644 7.094606,12.708244 6.2280961,12.253646 5.2739258,11.130371 4.658169,10.405496 3.97163,9.3975477 3.3828125,7.7407227 3.102629,6.9523225 2.9866669,6.1366343 2.8657227,5.0527344 5.0921299,4.7140344 6.8097538,4.0412398 8,3.0327148 Z M 8,3.9321289 C 6.6923817,4.8398539 5.2233869,5.2995548 3.7490234,5.6123046 4.4471579,9.5738045 5.9510862,11.267813 8,12.328613 10.048914,11.267813 11.552843,9.5738045 12.250977,5.6123046 10.776613,5.2995547 9.3076183,4.8398539 8,3.9321289 Z"
|
||||||
|
class="ColorScheme-Text"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
14
resources/images/fallback/light/big/unlock.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="m11 3c-2.216 0-4 1.784-4 4v1h1v-.5c0-1.939 1.338-3.5 3-3.5 1.662 0 3 1.561 3 3.5v3.5h-5-1-1-1-1v1 7h1 10 1v-8h-1-1v-4c0-2.216-1.784-4-4-4m-5 9h10v6h-10v-6"
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 491 B |