Compare commits
167 Commits
Author | SHA1 | Date | |
---|---|---|---|
1af20e27f2 | |||
ea7dcc5f18 | |||
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 | |||
50d710de04 | |||
332131796c | |||
3a70df21f8 | |||
a24e8382d1 | |||
d20fd84d39 | |||
5862f1552b | |||
ebeb4089eb | |||
a53126d8bc | |||
7db269acb5 | |||
67e5f9744e | |||
d0bdb374a0 | |||
8b3752ef47 | |||
8c6ac1a21d | |||
1c0802c8ca | |||
d84a33e144 | |||
4f6946a5fc | |||
f3153ef1db | |||
41d9fa7a1c | |||
261b34b712 | |||
d1f108e69d | |||
1e37aa762c | |||
a1f3c00a54 | |||
923afe2420 | |||
faa7d396a5 | |||
c55b7c6baf | |||
6764d8ea11 | |||
5fbb96618f | |||
d89c030e66 | |||
5787af8a4f | |||
1706b93b59 | |||
3f09b8f838 | |||
87c216b491 | |||
5f925217fc | |||
3f1fba4de2 | |||
ddfaa63a24 | |||
721f6daa36 | |||
0d584c5aba | |||
4307262f6e | |||
bd07c49755 | |||
8e99cc2969 | |||
a184ecafa3 | |||
7d2688151c | |||
0038aca1f6 | |||
6e06a1d5bc | |||
b7b70bc198 | |||
ce047db787 | |||
f45319de25 | |||
ebf0c64ffb | |||
d514db9c4a | |||
f34289399e | |||
05d6761baa | |||
216dcd29e9 | |||
0973cb2991 | |||
50190f3eac | |||
b44873d587 | |||
4c5efad9dc | |||
8310708c92 | |||
d936c0302d | |||
0e937199b0 | |||
48e498be25 | |||
3a7735b192 | |||
8f914c02a7 | |||
50bb3f5fd7 | |||
a0348b8fd2 | |||
85555da81f | |||
ebe5addfb5 | |||
b3c6860e25 | |||
00ffbac6b0 | |||
ff4124d1f0 | |||
15342f3c53 | |||
270a32db9e | |||
e0ef1ef797 | |||
e1eea2f3a2 | |||
e54cff0f0c | |||
4e6bd04b02 | |||
38159eafeb | |||
ef1a9846bf | |||
3a120c773a | |||
5f64321c2a | |||
a543eb1aef | |||
480c78cf61 | |||
0dcfc5eedc | |||
87426ee20f | |||
20bcae5ab2 | |||
6b65910ded | |||
9ca4aa29d4 | |||
a625ecb47b | |||
55ae5858b5 | |||
9c855553c5 | |||
83a2e6af85 | |||
494afcf2b5 | |||
a8698cc94f | |||
b50ce146b8 | |||
6657dc79ce | |||
bdca0aa6b1 | |||
cb44b12a7e | |||
21c7d65027 | |||
29c7d31c89 | |||
a77dfd191a | |||
b95028e33e | |||
543538fc56 | |||
7ce27d1c11 | |||
95f0d4008a | |||
3477226367 | |||
ddfb3419cc |
153
CHANGELOG.md
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Squawk 0.2.2 (May 05, 2022)
|
||||||
|
### Bug fixes
|
||||||
|
- now when you remove an account it actually gets removed
|
||||||
|
- segfault on unitialized Availability in some rare occesions
|
||||||
|
- 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 passord from KWallet before asking you to input it
|
||||||
|
- accounts now connect to the server asyncronously - 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 againg 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
|
||||||
|
- carbon copies switches on again after reconnection
|
||||||
|
- requesting the history of the current chat 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
|
||||||
|
- 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
|
||||||
|
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
||||||
|
- 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
|
||||||
|
- 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
|
||||||
|
- 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)
|
||||||
|
### Bug fixes
|
||||||
|
- error with sending attached files to the conference
|
||||||
|
- error with building on lower versions of QXmpp
|
||||||
|
- error on the first access to the conference database
|
||||||
|
- quit now actually quits the app
|
||||||
|
- history in MUC now works properly and requests messages from server
|
||||||
|
- error with handling non lower cased JIDs
|
||||||
|
- some workaround upon reconnection
|
||||||
|
|
||||||
|
|
||||||
|
## Squawk 0.1.4 (Apr 14, 2020)
|
||||||
|
### New features
|
||||||
|
- message line now is in the same window with roster (new window dialog is still able to opened on context menu)
|
||||||
|
- several new ways to manage your account password:
|
||||||
|
- store it in plain text with the config (like it always was)
|
||||||
|
- store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is)
|
||||||
|
- ask the account password on each program launch
|
||||||
|
- store it in KWallet which is dynamically loaded
|
||||||
|
- dragging into conversation now attach files
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- never updating MUC avatars now get updated
|
||||||
|
- going offline related segfault fix
|
||||||
|
- statuses now behave better: they wrap if they don't fit, you can select them, you can follow links from there
|
||||||
|
- messages and statuses don't loose content if you use < ore > symbols
|
||||||
|
- now avatars of those who are not in the MUC right now but was also display next to the message
|
||||||
|
- fix crash on attempt to attach the same file for the second time
|
||||||
|
|
||||||
|
|
||||||
|
## Squawk 0.1.3 (Mar 31, 2020)
|
||||||
|
### New features
|
||||||
|
- delivery states for the messages
|
||||||
|
- delivery receipts now work for real
|
||||||
|
- avatars in conferences
|
||||||
|
- edited messages now display correctly
|
||||||
|
- restyling to get better look with different desktop themes
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- clickable links now detects better
|
||||||
|
- fixed lost messages that came with no ID
|
||||||
|
- avatar related fixes
|
||||||
|
- message carbons now get turned on only if the server supports them
|
||||||
|
- progress spinner fix
|
||||||
|
- files in dialog now have better comment
|
||||||
|
|
||||||
|
|
||||||
|
## Squawk 0.1.2 (Dec 25, 2019)
|
||||||
|
### New features
|
||||||
|
- icons in roster for conferences
|
||||||
|
- pal avatar in dialog window
|
||||||
|
- avatars next to every message in dialog windows (not in conferences yet)
|
||||||
|
- roster window position and size now are stored in config
|
||||||
|
- expanded accounts and groups are stored in config
|
||||||
|
- availability (from top combobox) now is stored in config
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- segfault when sending more then one attached file
|
||||||
|
- wrong path and name of saving file
|
||||||
|
- wrong message syntax when attaching file and writing text in the save message
|
||||||
|
- problem with links highlighting in dialog
|
||||||
|
- project building fixes
|
||||||
|
|
||||||
|
|
||||||
|
## Squawk 0.1.1 (Nov 16, 2019)
|
||||||
|
A lot of bug fixes, memory leaks fixes
|
||||||
|
### New features
|
||||||
|
- exchange files via HTTP File Upload
|
||||||
|
- download VCards of your contacts
|
||||||
|
- upload your VCard with information about your contact phones email addresses, names, career information and avatar
|
||||||
|
- avatars of your contacts in roster and in notifications
|
||||||
|
|
||||||
|
|
||||||
|
## Squawk 0.0.5 (Oct 10, 2019)
|
||||||
|
### Features
|
||||||
|
- chat directly
|
||||||
|
- have multiple accounts
|
||||||
|
- add contacts
|
||||||
|
- remove contacts
|
||||||
|
- assign contact to different groups
|
||||||
|
- chat in MUCs
|
||||||
|
- join MUCs, leave them, keep them subscribed or unsubscribed
|
||||||
|
- download attachmets
|
||||||
|
- local history
|
||||||
|
- desktop notifications of new messages
|
210
CMakeLists.txt
@ -1,75 +1,173 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
project(squawk)
|
project(squawk VERSION 0.2.2 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include_directories(.)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
set(WIN32_FLAG "")
|
||||||
find_package(Qt5LinguistTools)
|
set(MACOSX_BUNDLE_FLAG "")
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if (WIN32)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(WIN32_FLAG WIN32)
|
||||||
|
endif(WIN32)
|
||||||
|
if (APPLE)
|
||||||
|
set(MACOSX_BUNDLE_FLAG MACOSX_BUNDLE)
|
||||||
|
endif(APPLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
|
add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG})
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR})
|
||||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
|
||||||
|
|
||||||
|
|
||||||
set(squawk_SRC
|
|
||||||
main.cpp
|
|
||||||
global.cpp
|
|
||||||
exception.cpp
|
|
||||||
signalcatcher.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
|
|
||||||
execute_process(COMMAND convert -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
execute_process(COMMAND convert -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
configure_file(packaging/squawk.desktop squawk.desktop COPYONLY)
|
|
||||||
|
|
||||||
set(TS_FILES
|
|
||||||
translations/squawk.ru.ts
|
|
||||||
)
|
|
||||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
|
||||||
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
|
||||||
|
|
||||||
qt5_add_resources(RCC resources/resources.qrc)
|
|
||||||
|
|
||||||
add_executable(squawk ${squawk_SRC} ${RCC})
|
|
||||||
target_link_libraries(squawk Qt5::Widgets)
|
|
||||||
|
|
||||||
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
option(SYSTEM_QXMPP "Use system qxmpp lib" ON)
|
||||||
|
option(WITH_KWALLET "Build KWallet support module" ON)
|
||||||
|
option(WITH_KIO "Build KIO support module" ON)
|
||||||
|
option(WITH_KCONFIG "Build KConfig support module" ON)
|
||||||
|
|
||||||
if(NOT SYSTEM_QXMPP)
|
# Dependencies
|
||||||
add_subdirectory(external/qxmpp)
|
## Qt
|
||||||
|
set(QT_VERSION_MAJOR 5)
|
||||||
|
find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED)
|
||||||
|
find_package(Boost COMPONENTS)
|
||||||
|
|
||||||
|
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
|
||||||
|
if (SYSTEM_QXMPP)
|
||||||
|
find_package(QXmpp CONFIG)
|
||||||
|
|
||||||
|
if (NOT QXmpp_FOUND)
|
||||||
|
set(SYSTEM_QXMPP OFF)
|
||||||
|
message("QXmpp package wasn't found, trying to build with bundled QXmpp")
|
||||||
|
else ()
|
||||||
|
message("Building with system QXmpp")
|
||||||
|
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
|
||||||
|
if (WITH_KIO)
|
||||||
|
find_package(KF5KIO CONFIG)
|
||||||
|
|
||||||
|
if (NOT KF5KIO_FOUND)
|
||||||
|
set(WITH_KIO OFF)
|
||||||
|
message("KIO package wasn't found, KIO support modules wouldn't be built")
|
||||||
|
else ()
|
||||||
|
target_compile_definitions(squawk PRIVATE WITH_KIO)
|
||||||
|
message("Building with support of KIO")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
## KWallet
|
||||||
|
if (WITH_KWALLET)
|
||||||
|
find_package(KF5Wallet CONFIG)
|
||||||
|
|
||||||
|
if (NOT KF5Wallet_FOUND)
|
||||||
|
set(WITH_KWALLET OFF)
|
||||||
|
message("KWallet package wasn't found, KWallet support module wouldn't be built")
|
||||||
|
else ()
|
||||||
|
target_compile_definitions(squawk PRIVATE WITH_KWALLET)
|
||||||
|
message("Building with support of KWallet")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
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()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(ui)
|
## Signal (TODO)
|
||||||
|
# find_package(Signal REQUIRED)
|
||||||
|
|
||||||
|
## LMDB
|
||||||
|
find_package(LMDB REQUIRED)
|
||||||
|
|
||||||
|
# Linking
|
||||||
|
target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml)
|
||||||
|
target_link_libraries(squawk PRIVATE lmdb)
|
||||||
|
target_link_libraries(squawk PRIVATE simpleCrypt)
|
||||||
|
# Link thread libraries on Linux
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(squawk PRIVATE Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Build type
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
set (COMPILE_OPTIONS -fno-sized-deallocation) # for eliminating _ZdlPvm
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
list(APPEND COMPILE_OPTIONS -O3)
|
||||||
|
endif()
|
||||||
|
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)
|
||||||
|
|
||||||
|
add_subdirectory(main)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(external/simpleCrypt)
|
||||||
target_link_libraries(squawk squawkUI)
|
add_subdirectory(packaging)
|
||||||
target_link_libraries(squawk squawkCORE)
|
add_subdirectory(plugins)
|
||||||
target_link_libraries(squawk uuid)
|
add_subdirectory(resources)
|
||||||
|
add_subdirectory(shared)
|
||||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
add_subdirectory(translations)
|
||||||
|
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 ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
|
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
install(FILES squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
install(FILES squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
|
||||||
install(FILES squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
install(FILES squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
if (APPLE)
|
||||||
install(FILES squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..."
|
||||||
install(FILES squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app"
|
||||||
|
)
|
||||||
|
endif(APPLE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
46
README.md
@ -4,15 +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.3.png)
|
![Squawk screenshot](https://macaw.me/images/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)*
|
||||||
- uuid _(usually included in some other package, for example it's ***libutil-linux*** in archlinux)_
|
|
||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.0 or higher
|
- CMake 3.4 or higher
|
||||||
- qxmpp 1.1.0 or higher
|
- qxmpp 1.1.0 or higher
|
||||||
|
- KDE Frameworks: kwallet (optional)
|
||||||
|
- KDE Frameworks: KIO (optional)
|
||||||
|
- 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
|
||||||
|
|
||||||
@ -32,6 +37,26 @@ You can also clone 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.
|
||||||
|
|
||||||
|
#### For Windows (Mingw-w64) build
|
||||||
|
|
||||||
|
You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt.
|
||||||
|
|
||||||
|
The best way to acquire library `lmdb` and `boost` is through Msys2.
|
||||||
|
|
||||||
|
First install Msys2, and then install `mingw-w64-x86_64-lmdb` and `mingw-w64-x86_64-boost` by pacman.
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
There are two ways to build, it depends whether you have qxmpp installed in your system
|
There are two ways to build, it depends whether you have qxmpp installed in your system
|
||||||
|
|
||||||
#### Building with system qxmpp
|
#### Building with system qxmpp
|
||||||
@ -43,7 +68,7 @@ $ git clone https://git.macaw.me/blue/squawk
|
|||||||
$ cd squawk
|
$ cd squawk
|
||||||
$ mkdir build
|
$ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake ..
|
$ cmake .. [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -56,10 +81,21 @@ $ 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
|
$ cmake .. -D SYSTEM_QXMPP=False [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...]
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can always refer to `appveyor.yml` to see how AppVeyor build squawk.
|
||||||
|
|
||||||
|
### List of keys
|
||||||
|
|
||||||
|
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`)
|
||||||
|
- `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`)
|
||||||
|
- `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`)
|
||||||
|
- `WITH_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`)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details
|
This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details
|
||||||
|
117
appveyor.yml
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
image:
|
||||||
|
- Visual Studio 2019
|
||||||
|
- "Previous Ubuntu1804"
|
||||||
|
- macOS-Mojave
|
||||||
|
|
||||||
|
branches:
|
||||||
|
except:
|
||||||
|
- gh-pages
|
||||||
|
|
||||||
|
for:
|
||||||
|
-
|
||||||
|
matrix:
|
||||||
|
only:
|
||||||
|
- image: Visual Studio 2019
|
||||||
|
|
||||||
|
environment:
|
||||||
|
QTDIR: C:\Qt\5.15.2\mingw81_64
|
||||||
|
QTTOOLDIR: C:\Qt\Tools\mingw810_64\bin
|
||||||
|
QTNINJADIR: C:\Qt\Tools\Ninja
|
||||||
|
|
||||||
|
install:
|
||||||
|
- set PATH=%QTTOOLDIR%;%QTNINJADIR%;%QTDIR%\bin;%PATH%
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- choco install --yes zstandard
|
||||||
|
- choco install --yes --version=7.1.0.2 imagemagick.app
|
||||||
|
- set PATH=C:\Program Files\ImageMagick-7.1.0-Q16-HDRI;%PATH%
|
||||||
|
|
||||||
|
- mkdir lmdb
|
||||||
|
- cd lmdb
|
||||||
|
- curl -OL https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar.zst
|
||||||
|
- zstd -d ./mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar.zst
|
||||||
|
- tar -xvf ./mingw-w64-x86_64-lmdb-0.9.27-1-any.pkg.tar
|
||||||
|
- cd ..
|
||||||
|
|
||||||
|
- mkdir boost
|
||||||
|
- cd boost
|
||||||
|
- curl -OL https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar.zst
|
||||||
|
- zstd -d ./mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar.zst
|
||||||
|
- tar -xvf ./mingw-w64-x86_64-boost-1.77.0-2-any.pkg.tar
|
||||||
|
- cd ..
|
||||||
|
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake -GNinja -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=%QTDIR% -DLMDB_ROOT_DIR:PATH=C:/projects/squawk/lmdb/mingw64 -DBOOST_ROOT:PATH=C:/projects/squawk/boost/mingw64 ..
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- cmake --build .
|
||||||
|
- mkdir deploy
|
||||||
|
- cd deploy
|
||||||
|
- copy ..\squawk.exe .\
|
||||||
|
- copy ..\external\qxmpp\src\libqxmpp.dll .\
|
||||||
|
- windeployqt .\squawk.exe
|
||||||
|
- windeployqt .\libqxmpp.dll
|
||||||
|
- cd ..\..
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: build/deploy/squawk.exe
|
||||||
|
name: Squawk executable (Qt 5.15.2)
|
||||||
|
|
||||||
|
- path: build/deploy
|
||||||
|
name: Squawk deployment with Qt Framework
|
||||||
|
|
||||||
|
-
|
||||||
|
matrix:
|
||||||
|
only:
|
||||||
|
- image: macOS-Mojave
|
||||||
|
|
||||||
|
install:
|
||||||
|
- brew install lmdb imagemagick boost
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=$HOME/Qt/5.15.2/clang_64 ..
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- cmake --build .
|
||||||
|
|
||||||
|
after_build:
|
||||||
|
- zip -r squawk.app.zip squawk.app
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: build/squawk.app/Contents/MacOS/squawk
|
||||||
|
name: Squawk executable (Qt 5.15.2)
|
||||||
|
- path: build/external/qxmpp/src/
|
||||||
|
name: QXMPP
|
||||||
|
- path: build/squawk.app.zip
|
||||||
|
name: Squawk Bundle with Qt Framework (Qt 5.15.2)
|
||||||
|
|
||||||
|
-
|
||||||
|
matrix:
|
||||||
|
only:
|
||||||
|
- image: "Previous Ubuntu1804"
|
||||||
|
|
||||||
|
install:
|
||||||
|
- ls $HOME/Qt
|
||||||
|
- sudo apt update
|
||||||
|
- sudo apt install -y liblmdb-dev liblmdb0 imagemagick mesa-common-dev libglu1-mesa-dev libboost-all-dev
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake -DCMAKE_BUILD_TYPE:String=Release -DCMAKE_PREFIX_PATH:STRING=$HOME/Qt/5.12.10/gcc_64 -DCMAKE_BUILD_RPATH="\$ORIGIN" ..
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- cmake --build .
|
||||||
|
|
||||||
|
after_build:
|
||||||
|
- zip -r squawk.zip squawk -j external/qxmpp/src/libqxmpp*
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: build/squawk.zip
|
||||||
|
name: Squawk executable and libraries (Qt 5.12)
|
52
cmake/FindLMDB.cmake
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license
|
||||||
|
#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code
|
||||||
|
|
||||||
|
# Try to find LMDB headers and library.
|
||||||
|
#
|
||||||
|
# Usage of this module as follows:
|
||||||
|
#
|
||||||
|
# find_package(LMDB)
|
||||||
|
#
|
||||||
|
# Variables used by this module, they can change the default behaviour and need
|
||||||
|
# to be set before calling find_package:
|
||||||
|
#
|
||||||
|
# LMDB_ROOT_DIR Set this variable to the root installation of
|
||||||
|
# LMDB if the module has problems finding the
|
||||||
|
# proper installation path.
|
||||||
|
#
|
||||||
|
# Variables defined by this module:
|
||||||
|
#
|
||||||
|
# LMDB_FOUND System has LMDB library/headers.
|
||||||
|
# LMDB_LIBRARIES The LMDB library.
|
||||||
|
# LMDB_INCLUDE_DIRS The location of LMDB headers.
|
||||||
|
|
||||||
|
find_path(LMDB_ROOT_DIR
|
||||||
|
NAMES include/lmdb.h
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(LMDB_LIBRARIES
|
||||||
|
NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible
|
||||||
|
HINTS ${LMDB_ROOT_DIR}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(lmdb UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(lmdb PROPERTIES
|
||||||
|
IMPORTED_LOCATION ${LMDB_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
find_path(LMDB_INCLUDE_DIRS
|
||||||
|
NAMES lmdb.h
|
||||||
|
HINTS ${LMDB_ROOT_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(LMDB DEFAULT_MSG
|
||||||
|
LMDB_LIBRARIES
|
||||||
|
LMDB_INCLUDE_DIRS
|
||||||
|
)
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
LMDB_ROOT_DIR
|
||||||
|
LMDB_LIBRARIES
|
||||||
|
LMDB_INCLUDE_DIRS
|
||||||
|
)
|
15
cmake/FindSignal.cmake
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
find_path(Signal_INCLUDE_DIR NAMES signal/signal_protocol.h)
|
||||||
|
find_library(Signal_LIBRARY signal-protocol-c)
|
||||||
|
mark_as_advanced(Signal_INCLUDE_DIR Signal_LIBRARY)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Signal REQUIRED_VARS Signal_LIBRARY Signal_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if (Signal_FOUND AND NOT TARGET Signal::Signal)
|
||||||
|
add_library(Signal::Signal UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(Signal::Signal PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||||
|
IMPORTED_LOCATION "${Signal_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Signal_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif ()
|
41
cmake/MacOSXBundleInfo.plist.in
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleGetInfoString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleLongVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||||
|
<key>CSResourcesFileMapped</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<string>True</string>
|
||||||
|
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -1,39 +1,29 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
set(SIGNALCATCHER_SOURCE signalcatcher.cpp)
|
||||||
project(squawkCORE)
|
if(WIN32)
|
||||||
|
set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp)
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
target_sources(squawk PRIVATE
|
||||||
|
account.cpp
|
||||||
|
account.h
|
||||||
|
adapterfunctions.cpp
|
||||||
|
adapterfunctions.h
|
||||||
|
conference.cpp
|
||||||
|
conference.h
|
||||||
|
contact.cpp
|
||||||
|
contact.h
|
||||||
|
networkaccess.cpp
|
||||||
|
networkaccess.h
|
||||||
|
rosteritem.cpp
|
||||||
|
rosteritem.h
|
||||||
|
${SIGNALCATCHER_SOURCE}
|
||||||
|
signalcatcher.h
|
||||||
|
squawk.cpp
|
||||||
|
squawk.h
|
||||||
|
)
|
||||||
|
|
||||||
find_package(Qt5Core CONFIG REQUIRED)
|
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
||||||
find_package(Qt5Gui CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Network CONFIG REQUIRED)
|
|
||||||
find_package(Qt5Xml CONFIG REQUIRED)
|
|
||||||
|
|
||||||
set(squawkCORE_SRC
|
add_subdirectory(handlers)
|
||||||
squawk.cpp
|
add_subdirectory(storage)
|
||||||
account.cpp
|
add_subdirectory(passwordStorageEngines)
|
||||||
archive.cpp
|
|
||||||
rosteritem.cpp
|
|
||||||
contact.cpp
|
|
||||||
conference.cpp
|
|
||||||
storage.cpp
|
|
||||||
networkaccess.cpp
|
|
||||||
adapterFuctions.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
|
||||||
add_library(squawkCORE ${squawkCORE_SRC})
|
|
||||||
|
|
||||||
if(SYSTEM_QXMPP)
|
|
||||||
find_package(QXmpp CONFIG REQUIRED)
|
|
||||||
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# Use the Widgets module from Qt 5.
|
|
||||||
target_link_libraries(squawkCORE Qt5::Core)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Network)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Gui)
|
|
||||||
target_link_libraries(squawkCORE Qt5::Xml)
|
|
||||||
target_link_libraries(squawkCORE qxmpp)
|
|
||||||
target_link_libraries(squawkCORE lmdb)
|
|
||||||
|
1763
core/account.cpp
151
core/account.h
@ -25,6 +25,7 @@
|
|||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -38,29 +39,43 @@
|
|||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
#include <QXmppHttpUploadIq.h>
|
|
||||||
#include <QXmppVCardIq.h>
|
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
|
||||||
#include "global.h"
|
#include "shared/shared.h"
|
||||||
#include "contact.h"
|
#include "contact.h"
|
||||||
#include "conference.h"
|
#include "conference.h"
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
|
||||||
|
#include "handlers/messagehandler.h"
|
||||||
|
#include "handlers/rosterhandler.h"
|
||||||
|
#include "handlers/vcardhandler.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
|
|
||||||
class Account : public QObject
|
class Account : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
friend class MessageHandler;
|
||||||
|
friend class RosterHandler;
|
||||||
|
friend class VCardHandler;
|
||||||
public:
|
public:
|
||||||
Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0);
|
enum class Error {
|
||||||
~Account();
|
authentication,
|
||||||
|
other,
|
||||||
|
none
|
||||||
|
};
|
||||||
|
|
||||||
void connect();
|
Account(
|
||||||
void disconnect();
|
const QString& p_login,
|
||||||
void reconnect();
|
const QString& p_server,
|
||||||
|
const QString& p_password,
|
||||||
|
const QString& p_name,
|
||||||
|
bool p_active,
|
||||||
|
NetworkAccess* p_net,
|
||||||
|
QObject* parent = 0);
|
||||||
|
~Account();
|
||||||
|
|
||||||
Shared::ConnectionState getState() const;
|
Shared::ConnectionState getState() const;
|
||||||
QString getName() const;
|
QString getName() const;
|
||||||
@ -69,7 +84,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;
|
||||||
|
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);
|
||||||
@ -77,11 +97,10 @@ public:
|
|||||||
void setPassword(const QString& p_password);
|
void setPassword(const QString& p_password);
|
||||||
void setResource(const QString& p_resource);
|
void setResource(const QString& p_resource);
|
||||||
void setAvailability(Shared::Availability avail);
|
void setAvailability(Shared::Availability avail);
|
||||||
QString getFullJid() const;
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
void sendMessage(Shared::Message data);
|
void sendMessage(const Shared::Message& data);
|
||||||
void sendMessage(const Shared::Message& data, const QString& path);
|
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 setReconnectTimes(unsigned int times);
|
|
||||||
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);
|
||||||
void removeContactRequest(const QString& jid);
|
void removeContactRequest(const QString& jid);
|
||||||
@ -89,20 +108,27 @@ public:
|
|||||||
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||||
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||||
void renameContactRequest(const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& jid, const QString& newName);
|
||||||
|
void requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
void setRoomJoined(const QString& jid, bool joined);
|
void setRoomJoined(const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||||
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 uploadVCard(const Shared::VCard& card);
|
||||||
|
void resendMessage(const QString& jid, const QString& id);
|
||||||
|
void replaceMessage(const QString& originalId, const Shared::Message& data);
|
||||||
|
void invalidatePassword();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void connect();
|
||||||
|
void disconnect();
|
||||||
|
void reconnect();
|
||||||
void requestVCard(const QString& jid);
|
void requestVCard(const QString& jid);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changed(const QMap<QString, QVariant>& data);
|
void changed(const QMap<QString, QVariant>& data);
|
||||||
void connectionStateChanged(int);
|
void connectionStateChanged(Shared::ConnectionState);
|
||||||
void availabilityChanged(int);
|
void availabilityChanged(Shared::Availability);
|
||||||
void addGroup(const QString& name);
|
void addGroup(const QString& name);
|
||||||
void removeGroup(const QString& name);
|
void removeGroup(const QString& name);
|
||||||
void addRoom(const QString& jid, const QMap<QString, QVariant>& data);
|
void addRoom(const QString& jid, const QMap<QString, QVariant>& data);
|
||||||
@ -116,23 +142,23 @@ signals:
|
|||||||
void removePresence(const QString& jid, const QString& name);
|
void removePresence(const QString& jid, const QString& name);
|
||||||
void message(const Shared::Message& data);
|
void message(const Shared::Message& data);
|
||||||
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void responseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
void responseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
void error(const QString& text);
|
void error(const QString& text);
|
||||||
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
||||||
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
||||||
void uploadFileError(const QString& messageId, const QString& error);
|
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
||||||
|
void needPassword();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
std::map<QString, QString> achiveQueries;
|
std::map<QString, QString> archiveQueries;
|
||||||
QXmppClient client;
|
QXmppClient client;
|
||||||
QXmppConfiguration config;
|
QXmppConfiguration config;
|
||||||
QXmppPresence presence;
|
QXmppPresence presence;
|
||||||
Shared::ConnectionState state;
|
Shared::ConnectionState state;
|
||||||
std::map<QString, std::set<QString>> groups;
|
|
||||||
QXmppCarbonManager* cm;
|
QXmppCarbonManager* cm;
|
||||||
QXmppMamManager* am;
|
QXmppMamManager* am;
|
||||||
QXmppMucManager* mm;
|
QXmppMucManager* mm;
|
||||||
@ -142,97 +168,40 @@ private:
|
|||||||
QXmppUploadRequestManager* um;
|
QXmppUploadRequestManager* um;
|
||||||
QXmppDiscoveryManager* dm;
|
QXmppDiscoveryManager* dm;
|
||||||
QXmppMessageReceiptManager* rcpm;
|
QXmppMessageReceiptManager* rcpm;
|
||||||
std::map<QString, Contact*> contacts;
|
bool reconnectScheduled;
|
||||||
std::map<QString, Conference*> conferences;
|
QTimer* reconnectTimer;
|
||||||
unsigned int maxReconnectTimes;
|
|
||||||
unsigned int reconnectTimes;
|
|
||||||
|
|
||||||
std::map<QString, QString> queuedContacts;
|
|
||||||
std::set<QString> outOfRosterContacts;
|
|
||||||
std::set<QString> pendingVCardRequests;
|
|
||||||
std::map<QString, Shared::Message> pendingMessages;
|
|
||||||
std::deque<std::pair<QString, Shared::Message>> uploadingSlotsQueue;
|
|
||||||
|
|
||||||
QString avatarHash;
|
|
||||||
QString avatarType;
|
|
||||||
bool ownVCardRequestInProgress;
|
|
||||||
NetworkAccess* network;
|
NetworkAccess* network;
|
||||||
std::map<QString, QString> pendingStateMessages;
|
Shared::AccountPassword passwordType;
|
||||||
|
Error lastError;
|
||||||
|
bool pepSupport;
|
||||||
|
bool active;
|
||||||
|
bool notReadyPassword;
|
||||||
|
|
||||||
|
MessageHandler* mh;
|
||||||
|
RosterHandler* rh;
|
||||||
|
VCardHandler* vh;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientConnected();
|
void onClientStateChange(QXmppClient::State state);
|
||||||
void onClientDisconnected();
|
|
||||||
void onClientError(QXmppClient::Error err);
|
void onClientError(QXmppClient::Error err);
|
||||||
|
|
||||||
void onRosterReceived();
|
|
||||||
void onRosterItemAdded(const QString& bareJid);
|
|
||||||
void onRosterItemChanged(const QString& bareJid);
|
|
||||||
void onRosterItemRemoved(const QString& bareJid);
|
|
||||||
void onRosterPresenceChanged(const QString& bareJid, const QString& resource);
|
|
||||||
|
|
||||||
void onPresenceReceived(const QXmppPresence& presence);
|
void onPresenceReceived(const QXmppPresence& presence);
|
||||||
void onMessageReceived(const QXmppMessage& message);
|
void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at);
|
||||||
|
|
||||||
void onCarbonMessageReceived(const QXmppMessage& message);
|
|
||||||
void onCarbonMessageSent(const QXmppMessage& message);
|
|
||||||
|
|
||||||
void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message);
|
void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message);
|
||||||
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
||||||
|
|
||||||
void onMucRoomAdded(QXmppMucRoom* room);
|
|
||||||
void onMucJoinedChanged(bool joined);
|
|
||||||
void onMucAutoJoinChanged(bool autoJoin);
|
|
||||||
void onMucNickNameChanged(const QString& nickName);
|
|
||||||
void onMucSubjectChanged(const QString& subject);
|
|
||||||
void onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data);
|
|
||||||
void onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data);
|
|
||||||
void onMucRemoveParticipant(const QString& nickName);
|
|
||||||
|
|
||||||
void bookmarksReceived(const QXmppBookmarkSet& bookmarks);
|
|
||||||
|
|
||||||
void onContactGroupAdded(const QString& group);
|
|
||||||
void onContactGroupRemoved(const QString& group);
|
|
||||||
void onContactNameChanged(const QString& name);
|
|
||||||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
|
||||||
void onContactHistoryResponse(const std::list<Shared::Message>& list);
|
|
||||||
void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at);
|
|
||||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
|
||||||
|
|
||||||
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 onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
|
||||||
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
|
||||||
void onFileUploaded(const QString& messageId, const QString& url);
|
|
||||||
void onFileUploadError(const QString& messageId, const QString& errMsg);
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
|
void onContactHistoryResponse(const std::list<Shared::Message>& list, bool last);
|
||||||
void onReceiptReceived(const QString& jid, const QString &id);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addedAccount(const QString &bareJid);
|
void handleDisconnection();
|
||||||
void handleNewContact(Contact* contact);
|
void onReconnectTimer();
|
||||||
void handleNewRosterItem(RosterItem* contact);
|
|
||||||
void handleNewConference(Conference* contact);
|
|
||||||
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
|
||||||
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
|
||||||
void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin);
|
|
||||||
void addToGroup(const QString& jid, const QString& group);
|
|
||||||
void removeFromGroup(const QString& jid, const QString& group);
|
|
||||||
void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const;
|
|
||||||
Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) const;
|
|
||||||
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
|
||||||
void storeConferences();
|
|
||||||
void clearConferences();
|
|
||||||
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url);
|
|
||||||
RosterItem* getRosterItem(const QString& jid);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
|
||||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
|
32
core/adapterfunctions.h
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/>.
|
||||||
|
*/
|
||||||
|
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
#define CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||||
|
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CORE_ADAPTER_FUNCTIONS_H
|
@ -25,7 +25,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||||||
nick(p_nick),
|
nick(p_nick),
|
||||||
room(p_room),
|
room(p_room),
|
||||||
joined(false),
|
joined(false),
|
||||||
autoJoin(p_autoJoin)
|
autoJoin(p_autoJoin),
|
||||||
|
exParticipants()
|
||||||
{
|
{
|
||||||
muc = true;
|
muc = true;
|
||||||
name = p_name;
|
name = p_name;
|
||||||
@ -44,6 +45,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||||||
if (autoJoin) {
|
if (autoJoin) {
|
||||||
room->join();
|
room->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
archive->readAllResourcesAvatars(exParticipants);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Conference::~Conference()
|
Core::Conference::~Conference()
|
||||||
@ -134,17 +137,20 @@ 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);
|
||||||
|
QXmppMucItem mi = pres.mucItem();
|
||||||
if (resource == jid) {
|
if (resource == jid) {
|
||||||
qDebug() << "Room" << jid << "is reporting of adding itself to the list participants. Not sure what to do with that yet, skipping";
|
resource = "";
|
||||||
} else {
|
}
|
||||||
QXmppPresence pres = room->participantPresence(p_name);
|
|
||||||
|
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
|
||||||
|
bool hasAvatar = itr != exParticipants.end();
|
||||||
|
|
||||||
|
if (resource.size() > 0) {
|
||||||
QDateTime lastInteraction = pres.lastUserInteraction();
|
QDateTime lastInteraction = pres.lastUserInteraction();
|
||||||
if (!lastInteraction.isValid()) {
|
if (!lastInteraction.isValid()) {
|
||||||
lastInteraction = QDateTime::currentDateTimeUtc();
|
lastInteraction = QDateTime::currentDateTimeUtc();
|
||||||
}
|
}
|
||||||
QXmppMucItem mi = pres.mucItem();
|
|
||||||
Archive::AvatarInfo info;
|
|
||||||
bool hasAvatar = readAvatarInfo(info, resource);
|
|
||||||
|
|
||||||
QMap<QString, QVariant> cData = {
|
QMap<QString, QVariant> cData = {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
@ -155,12 +161,12 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
if (info.autogenerated) {
|
if (itr->second.autogenerated) {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||||
}
|
}
|
||||||
cData.insert("avatarPath", avatarPath(resource) + "." + info.type);
|
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||||
cData.insert("avatarPath", "");
|
cData.insert("avatarPath", "");
|
||||||
@ -169,22 +175,43 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||||||
|
|
||||||
emit addParticipant(resource, cData);
|
emit addParticipant(resource, cData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (pres.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 (!hasAvatar || !itr->second.autogenerated) {
|
||||||
|
setAutoGeneratedAvatar(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) {
|
||||||
|
emit requestVCard(p_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit requestVCard(p_name);
|
||||||
|
}
|
||||||
|
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();
|
||||||
if (resource == jid) {
|
QXmppPresence pres = room->participantPresence(p_name);
|
||||||
qDebug() << "Room" << jid << "is reporting of changing his own presence. Not sure what to do with that yet, skipping";
|
QXmppMucItem mi = pres.mucItem();
|
||||||
} else {
|
handlePresence(pres);
|
||||||
QXmppPresence pres = room->participantPresence(p_name);
|
if (resource != jid) {
|
||||||
QDateTime lastInteraction = pres.lastUserInteraction();
|
QDateTime lastInteraction = pres.lastUserInteraction();
|
||||||
if (!lastInteraction.isValid()) {
|
if (!lastInteraction.isValid()) {
|
||||||
lastInteraction = QDateTime::currentDateTimeUtc();
|
lastInteraction = QDateTime::currentDateTimeUtc();
|
||||||
}
|
}
|
||||||
QXmppMucItem mi = pres.mucItem();
|
|
||||||
handlePresence(pres);
|
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
@ -261,30 +288,46 @@ void Core::Conference::handlePresence(const QXmppPresence& pres)
|
|||||||
|
|
||||||
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
|
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
|
||||||
{
|
{
|
||||||
bool result = RosterItem::setAutoGeneratedAvatar(resource);
|
Archive::AvatarInfo newInfo;
|
||||||
|
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);
|
||||||
|
if (itr == exParticipants.end()) {
|
||||||
|
exParticipants.insert(std::make_pair(resource, newInfo));
|
||||||
|
} else {
|
||||||
|
itr->second = newInfo;
|
||||||
|
}
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
{"availability", avatarPath(resource) + ".png"}
|
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Conference::setAvatar(const QByteArray& data, const QString& resource)
|
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
|
||||||
{
|
{
|
||||||
bool result = RosterItem::setAvatar(data, 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) {
|
||||||
QMimeDatabase db;
|
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||||
QMimeType type = db.mimeTypeForData(data);
|
if (itr == exParticipants.end()) {
|
||||||
QString ext = type.preferredSuffix();
|
exParticipants.insert(std::make_pair(resource, info));
|
||||||
|
} else {
|
||||||
|
itr->second = info;
|
||||||
|
}
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
{"avatarPath", avatarPath(resource) + "." + ext}
|
{"avatarPath", avatarPath(resource) + "." + info.type}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||||
|
if (itr != exParticipants.end()) {
|
||||||
|
exParticipants.erase(itr);
|
||||||
|
}
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
|
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
|
||||||
{"avatarPath", ""}
|
{"avatarPath", ""}
|
||||||
@ -309,3 +352,12 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
|
||||||
|
{
|
||||||
|
QMap<QString, QVariant> result;
|
||||||
|
for (const std::pair<const QString, Archive::AvatarInfo>& pair : exParticipants) {
|
||||||
|
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -19,9 +19,15 @@
|
|||||||
#ifndef CORE_CONFERENCE_H
|
#ifndef CORE_CONFERENCE_H
|
||||||
#define CORE_CONFERENCE_H
|
#define CORE_CONFERENCE_H
|
||||||
|
|
||||||
#include "rosteritem.h"
|
#include <QDir>
|
||||||
|
|
||||||
#include <QXmppMucManager.h>
|
#include <QXmppMucManager.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "rosteritem.h"
|
||||||
|
#include "shared/global.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -46,8 +52,8 @@ public:
|
|||||||
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;
|
||||||
bool setAvatar(const QByteArray &data, const QString &resource = "") override;
|
|
||||||
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
|
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
|
||||||
|
QMap<QString, QVariant> getAllAvatars() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nickChanged(const QString& nick);
|
void nickChanged(const QString& nick);
|
||||||
@ -58,11 +64,16 @@ signals:
|
|||||||
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeParticipant(const QString& name);
|
void removeParticipant(const QString& name);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString nick;
|
QString nick;
|
||||||
QXmppMucRoom* room;
|
QXmppMucRoom* room;
|
||||||
bool joined;
|
bool joined;
|
||||||
bool autoJoin;
|
bool autoJoin;
|
||||||
|
std::map<QString, Archive::AvatarInfo> exParticipants;
|
||||||
|
static const std::set<QString> supportedList;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRoomJoined();
|
void onRoomJoined();
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
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::unknown)
|
subscriptionState(Shared::SubscriptionState::unknown)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
core/handlers/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
messagehandler.cpp
|
||||||
|
messagehandler.h
|
||||||
|
rosterhandler.cpp
|
||||||
|
rosterhandler.h
|
||||||
|
vcardhandler.cpp
|
||||||
|
vcardhandler.h
|
||||||
|
)
|
595
core/handlers/messagehandler.cpp
Normal file
@ -0,0 +1,595 @@
|
|||||||
|
/*
|
||||||
|
* 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 "messagehandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
|
||||||
|
Core::MessageHandler::MessageHandler(Core::Account* account):
|
||||||
|
QObject(),
|
||||||
|
acc(account),
|
||||||
|
pendingStateMessages(),
|
||||||
|
uploadingSlotsQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg)
|
||||||
|
{
|
||||||
|
bool handled = false;
|
||||||
|
switch (msg.type()) {
|
||||||
|
case QXmppMessage::Normal:
|
||||||
|
qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping";
|
||||||
|
break;
|
||||||
|
case QXmppMessage::Chat:
|
||||||
|
handled = handleChatMessage(msg);
|
||||||
|
break;
|
||||||
|
case QXmppMessage::GroupChat:
|
||||||
|
handled = handleGroupMessage(msg);
|
||||||
|
break;
|
||||||
|
case QXmppMessage::Error: {
|
||||||
|
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
|
||||||
|
if (std::get<0>(ids)) {
|
||||||
|
QString id = std::get<1>(ids);
|
||||||
|
QString jid = std::get<2>(ids);
|
||||||
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
|
{"errorText", msg.error().text()}
|
||||||
|
};
|
||||||
|
if (cnt != 0) {
|
||||||
|
cnt->changeMessage(id, cData);
|
||||||
|
}
|
||||||
|
emit acc->changeMessage(jid, id, cData);
|
||||||
|
handled = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppMessage::Headline:
|
||||||
|
qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!handled) {
|
||||||
|
logMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing)
|
||||||
|
{
|
||||||
|
if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) {
|
||||||
|
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 (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)
|
||||||
|
{
|
||||||
|
const QString& body(msg.body());
|
||||||
|
if (body.size() != 0) {
|
||||||
|
|
||||||
|
Shared::Message sMsg(Shared::Message::groupChat);
|
||||||
|
initializeMessage(sMsg, msg, outgoing, forwarded, guessing);
|
||||||
|
QString jid = sMsg.getPenPalJid();
|
||||||
|
Conference* cnt = acc->rh->getConference(jid);
|
||||||
|
if (cnt == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(msg.id());
|
||||||
|
if (std::get<0>(ids)) {
|
||||||
|
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
||||||
|
cnt->changeMessage(std::get<1>(ids), cData);
|
||||||
|
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
|
||||||
|
} else {
|
||||||
|
QString oId = msg.replaceId();
|
||||||
|
if (oId.size() > 0) {
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"body", sMsg.getBody()},
|
||||||
|
{"stamp", sMsg.getTime()}
|
||||||
|
};
|
||||||
|
cnt->correctMessageInArchive(oId, sMsg);
|
||||||
|
emit acc->changeMessage(jid, oId, cData);
|
||||||
|
} else {
|
||||||
|
cnt->appendMessageToArchive(sMsg);
|
||||||
|
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
|
||||||
|
if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt
|
||||||
|
emit acc->message(sMsg);
|
||||||
|
} else {
|
||||||
|
//qDebug() << "Delayed delivery: ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const
|
||||||
|
{
|
||||||
|
const QDateTime& time(source.stamp());
|
||||||
|
QString id;
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
||||||
|
id = source.originId();
|
||||||
|
if (id.size() == 0) {
|
||||||
|
id = source.id();
|
||||||
|
}
|
||||||
|
target.setStanzaId(source.stanzaId());
|
||||||
|
qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId();
|
||||||
|
#else
|
||||||
|
id = source.id();
|
||||||
|
#endif
|
||||||
|
target.setId(id);
|
||||||
|
QString messageId = target.getId();
|
||||||
|
if (messageId.size() == 0) {
|
||||||
|
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
|
||||||
|
messageId = target.getId();
|
||||||
|
qDebug() << "Had do initialize a message with no id, assigning autogenerated" << messageId;
|
||||||
|
}
|
||||||
|
target.setFrom(source.from());
|
||||||
|
target.setTo(source.to());
|
||||||
|
target.setBody(source.body());
|
||||||
|
target.setForwarded(forwarded);
|
||||||
|
|
||||||
|
if (guessing) {
|
||||||
|
if (target.getFromJid() == acc->getBareJid()) {
|
||||||
|
outgoing = true;
|
||||||
|
} else {
|
||||||
|
outgoing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.setOutgoing(outgoing);
|
||||||
|
if (time.isValid()) {
|
||||||
|
target.setTime(time);
|
||||||
|
} else {
|
||||||
|
target.setCurrentTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString oob = source.outOfBandUrl();
|
||||||
|
if (oob.size() > 0) {
|
||||||
|
target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId));
|
||||||
|
}
|
||||||
|
target.setOutOfBandUrl(oob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason)
|
||||||
|
{
|
||||||
|
qDebug() << reason;
|
||||||
|
qDebug() << "- from: " << msg.from();
|
||||||
|
qDebug() << "- to: " << msg.to();
|
||||||
|
qDebug() << "- body: " << msg.body();
|
||||||
|
qDebug() << "- type: " << msg.type();
|
||||||
|
qDebug() << "- state: " << msg.state();
|
||||||
|
qDebug() << "- stamp: " << msg.stamp();
|
||||||
|
qDebug() << "- id: " << msg.id();
|
||||||
|
#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0)
|
||||||
|
qDebug() << "- stanzaId: " << msg.stanzaId();
|
||||||
|
#endif
|
||||||
|
qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl();
|
||||||
|
qDebug() << "==============================";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg)
|
||||||
|
{
|
||||||
|
handleChatMessage(msg, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg)
|
||||||
|
{
|
||||||
|
handleChatMessage(msg, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<bool, QString, QString> Core::MessageHandler::getOriginalPendingMessageId(const QString& id)
|
||||||
|
{
|
||||||
|
std::tuple<bool, QString, QString> result({false, "", ""});
|
||||||
|
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
|
||||||
|
if (itr != pendingStateMessages.end()) {
|
||||||
|
std::get<0>(result) = true;
|
||||||
|
std::get<2>(result) = itr->second;
|
||||||
|
|
||||||
|
std::map<QString, QString>::const_iterator itrC = pendingCorrectionMessages.find(id);
|
||||||
|
if (itrC != pendingCorrectionMessages.end()) {
|
||||||
|
if (itrC->second.size() > 0) {
|
||||||
|
std::get<1>(result) = itrC->second;
|
||||||
|
} else {
|
||||||
|
std::get<1>(result) = itr->first;
|
||||||
|
}
|
||||||
|
pendingCorrectionMessages.erase(itrC);
|
||||||
|
} else {
|
||||||
|
std::get<1>(result) = itr->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingStateMessages.erase(itr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
std::tuple<bool, QString, QString> ids = getOriginalPendingMessageId(id);
|
||||||
|
if (std::get<0>(ids)) {
|
||||||
|
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids));
|
||||||
|
|
||||||
|
if (ri != 0) {
|
||||||
|
ri->changeMessage(std::get<1>(ids), cData);
|
||||||
|
}
|
||||||
|
emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId)
|
||||||
|
{
|
||||||
|
if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) {
|
||||||
|
pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId));
|
||||||
|
prepareUpload(data, newMessage);
|
||||||
|
} else {
|
||||||
|
performSending(data, originalId, newMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage)
|
||||||
|
{
|
||||||
|
QString jid = data.getPenPalJid();
|
||||||
|
QString id = data.getId();
|
||||||
|
qDebug() << "Sending message with id:" << id;
|
||||||
|
if (originalId.size() > 0) {
|
||||||
|
qDebug() << "To replace one with id:" << originalId;
|
||||||
|
}
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
|
bool sent = false;
|
||||||
|
if (newMessage && originalId.size() > 0) {
|
||||||
|
newMessage = false;
|
||||||
|
}
|
||||||
|
QDateTime sendTime = QDateTime::currentDateTimeUtc();
|
||||||
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
|
QXmppMessage msg(createPacket(data, sendTime, originalId));
|
||||||
|
|
||||||
|
sent = acc->client.sendPacket(msg);
|
||||||
|
if (sent) {
|
||||||
|
data.setState(Shared::Message::State::sent);
|
||||||
|
} else {
|
||||||
|
data.setState(Shared::Message::State::error);
|
||||||
|
data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data.setState(Shared::Message::State::error);
|
||||||
|
data.setErrorText("You are is offline or reconnecting");
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> changes(getChanges(data, sendTime, newMessage, originalId));
|
||||||
|
|
||||||
|
QString realId;
|
||||||
|
if (originalId.size() > 0) {
|
||||||
|
realId = originalId;
|
||||||
|
} else {
|
||||||
|
realId = id;
|
||||||
|
}
|
||||||
|
if (ri != 0) {
|
||||||
|
if (newMessage) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
|
} else {
|
||||||
|
ri->changeMessage(realId, changes);
|
||||||
|
}
|
||||||
|
if (sent) {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
if (originalId.size() > 0) {
|
||||||
|
pendingCorrectionMessages.insert(std::make_pair(id, originalId));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pendingStateMessages.erase(id);
|
||||||
|
pendingCorrectionMessages.erase(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit acc->changeMessage(jid, realId, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(acc->getFullJid(), 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) {
|
||||||
|
QString jid = data.getPenPalJid();
|
||||||
|
QString id = data.getId();
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(jid);
|
||||||
|
if (!ri) {
|
||||||
|
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString path = data.getAttachPath();
|
||||||
|
QString url = acc->network->getFileRemoteUrl(path);
|
||||||
|
if (url.size() != 0) {
|
||||||
|
sendMessageWithLocalUploadedFile(data, url, newMessage);
|
||||||
|
} else {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
if (newMessage) {
|
||||||
|
ri->appendMessageToArchive(data);
|
||||||
|
} else {
|
||||||
|
QMap<QString, QVariant> changes({
|
||||||
|
{"state", (uint)Shared::Message::State::pending}
|
||||||
|
});
|
||||||
|
ri->changeMessage(id, changes);
|
||||||
|
emit acc->changeMessage(jid, id, changes);
|
||||||
|
}
|
||||||
|
//this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file
|
||||||
|
if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
|
||||||
|
if (acc->um->serviceFound()) {
|
||||||
|
QFileInfo file(path);
|
||||||
|
if (file.exists() && file.isReadable()) {
|
||||||
|
pendingStateMessages.insert(std::make_pair(id, jid));
|
||||||
|
uploadingSlotsQueue.emplace_back(path, id);
|
||||||
|
if (uploadingSlotsQueue.size() == 1) {
|
||||||
|
acc->um->requestUploadSlot(file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
|
||||||
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
|
||||||
|
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting");
|
||||||
|
qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
|
||||||
|
{
|
||||||
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
|
||||||
|
} else {
|
||||||
|
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
||||||
|
const QString& mId = pair.second;
|
||||||
|
QString palJid = pendingStateMessages.at(mId);
|
||||||
|
acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
|
||||||
|
|
||||||
|
uploadingSlotsQueue.pop_front();
|
||||||
|
if (uploadingSlotsQueue.size() > 0) {
|
||||||
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
|
||||||
|
{
|
||||||
|
QString err(request.error().text());
|
||||||
|
if (uploadingSlotsQueue.size() == 0) {
|
||||||
|
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
|
||||||
|
qDebug() << err;
|
||||||
|
} else {
|
||||||
|
const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
|
||||||
|
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err;
|
||||||
|
handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
|
||||||
|
|
||||||
|
uploadingSlotsQueue.pop_front();
|
||||||
|
if (uploadingSlotsQueue.size() > 0) {
|
||||||
|
acc->um->requestUploadSlot(uploadingSlotsQueue.front().first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onDownloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
|
||||||
|
{
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"attachPath", path}
|
||||||
|
};
|
||||||
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
|
if (info.account == acc->getName()) {
|
||||||
|
RosterItem* cnt = acc->rh->getRosterItem(info.jid);
|
||||||
|
if (cnt != 0) {
|
||||||
|
if (cnt->changeMessage(info.messageId, cData)) {
|
||||||
|
emit acc->changeMessage(info.jid, info.messageId, cData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up)
|
||||||
|
{
|
||||||
|
if (up) {
|
||||||
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
|
if (info.account == acc->getName()) {
|
||||||
|
handleUploadError(info.jid, info.messageId, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
|
||||||
|
{
|
||||||
|
emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
|
||||||
|
pendingStateMessages.erase(messageId);
|
||||||
|
pendingCorrectionMessages.erase(messageId);
|
||||||
|
requestChangeMessage(jid, messageId, {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::error)},
|
||||||
|
{"errorText", errorText}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
|
if (info.account == acc->getName()) {
|
||||||
|
RosterItem* ri = acc->rh->getRosterItem(info.jid);
|
||||||
|
if (ri != 0) {
|
||||||
|
Shared::Message msg = ri->getMessage(info.messageId);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
msg.setOutOfBandUrl(url);
|
||||||
|
if (msg.getBody().size() == 0) { //not sure why, but most messages do that
|
||||||
|
msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
|
||||||
|
}
|
||||||
|
performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage);
|
||||||
|
//TODO removal/progress update
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::set<QString> allowedToChangeKeys({
|
||||||
|
"attachPath",
|
||||||
|
"outOfBandUrl",
|
||||||
|
"state",
|
||||||
|
"errorText"
|
||||||
|
});
|
||||||
|
|
||||||
|
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
|
if (cnt != 0) {
|
||||||
|
bool allSupported = true;
|
||||||
|
QString unsupportedString;
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
|
||||||
|
if (allowedToChangeKeys.count(itr.key()) != 1) { //to not allow this method
|
||||||
|
allSupported = false; //to make a message to look like if it was edited
|
||||||
|
unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method
|
||||||
|
break; //because the underlying tech assumes that
|
||||||
|
} //the change is initiated by user, not by system
|
||||||
|
}
|
||||||
|
if (allSupported) {
|
||||||
|
cnt->changeMessage(messageId, data);
|
||||||
|
emit acc->changeMessage(jid, messageId, data);
|
||||||
|
} else {
|
||||||
|
qDebug() << "A request to change message" << messageId << "of conversation" << jid << "with following data" << data;
|
||||||
|
qDebug() << "only limited set of dataFields are supported yet here, and" << unsupportedString << "isn't one of them, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
RosterItem* cnt = acc->rh->getRosterItem(jid);
|
||||||
|
if (cnt != 0) {
|
||||||
|
try {
|
||||||
|
Shared::Message msg = cnt->getMessage(id);
|
||||||
|
if (msg.getState() == Shared::Message::State::error) {
|
||||||
|
if (msg.getEdited()){
|
||||||
|
QString originalId = msg.getId();
|
||||||
|
msg.generateRandomId();
|
||||||
|
sendMessage(msg, false, originalId);
|
||||||
|
} else {
|
||||||
|
sendMessage(msg, false);
|
||||||
|
}
|
||||||
|
} 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";
|
||||||
|
}
|
||||||
|
} catch (const Archive::NotFound& err) {
|
||||||
|
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this jid isn't present in account roster, skipping";
|
||||||
|
}
|
||||||
|
}
|
86
core/handlers/messagehandler.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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_MESSAGEHANDLER_H
|
||||||
|
#define CORE_MESSAGEHANDLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QXmppMessage.h>
|
||||||
|
#include <QXmppHttpUploadIq.h>
|
||||||
|
|
||||||
|
#include <shared/message.h>
|
||||||
|
#include <shared/messageinfo.h>
|
||||||
|
#include <shared/pathcheck.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class MessageHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MessageHandler(Account* account);
|
||||||
|
|
||||||
|
public:
|
||||||
|
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 resendMessage(const QString& jid, const QString& id);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onMessageReceived(const QXmppMessage& message);
|
||||||
|
void onCarbonMessageReceived(const QXmppMessage& message);
|
||||||
|
void onCarbonMessageSent(const QXmppMessage& message);
|
||||||
|
void onReceiptReceived(const QString& jid, const QString& id);
|
||||||
|
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
|
||||||
|
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
|
||||||
|
void onDownloadFileComplete(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 requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
||||||
|
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
|
||||||
|
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
|
||||||
|
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
|
||||||
|
void performSending(Shared::Message data, const QString& originalId, bool newMessage = true);
|
||||||
|
void prepareUpload(const Shared::Message& data, bool newMessage = true);
|
||||||
|
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::tuple<bool, QString, QString> getOriginalPendingMessageId(const QString& id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_MESSAGEHANDLER_H
|
600
core/handlers/rosterhandler.cpp
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
/*
|
||||||
|
* 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 "rosterhandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
|
||||||
|
Core::RosterHandler::RosterHandler(Core::Account* account):
|
||||||
|
QObject(),
|
||||||
|
acc(account),
|
||||||
|
contacts(),
|
||||||
|
conferences(),
|
||||||
|
groups(),
|
||||||
|
queuedContacts(),
|
||||||
|
outOfRosterContacts(),
|
||||||
|
pepSupport(false)
|
||||||
|
{
|
||||||
|
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
||||||
|
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
||||||
|
connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved);
|
||||||
|
connect(acc->rm, &QXmppRosterManager::itemChanged, this, &RosterHandler::onRosterItemChanged);
|
||||||
|
|
||||||
|
|
||||||
|
connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded);
|
||||||
|
connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::RosterHandler::~RosterHandler()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards
|
||||||
|
|
||||||
|
QStringList bj = acc->rm->getRosterBareJids();
|
||||||
|
for (int i = 0; i < bj.size(); ++i) {
|
||||||
|
const QString& jid = bj[i];
|
||||||
|
addedAccount(jid.toLower());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onRosterItemAdded(const QString& bareJid)
|
||||||
|
{
|
||||||
|
QString lcJid = bareJid.toLower();
|
||||||
|
addedAccount(lcJid);
|
||||||
|
std::map<QString, QString>::const_iterator itr = queuedContacts.find(lcJid);
|
||||||
|
if (itr != queuedContacts.end()) {
|
||||||
|
acc->rm->subscribe(lcJid, itr->second);
|
||||||
|
queuedContacts.erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addedAccount(const QString& jid)
|
||||||
|
{
|
||||||
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||||
|
QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid);
|
||||||
|
Contact* contact;
|
||||||
|
bool newContact = false;
|
||||||
|
if (itr == contacts.end()) {
|
||||||
|
newContact = true;
|
||||||
|
contact = new Contact(jid, acc->name);
|
||||||
|
contacts.insert(std::make_pair(jid, contact));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
contact = itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> gr = re.groups();
|
||||||
|
Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType());
|
||||||
|
contact->setGroups(gr);
|
||||||
|
contact->setSubscriptionState(state);
|
||||||
|
contact->setName(re.name());
|
||||||
|
|
||||||
|
if (newContact) {
|
||||||
|
QMap<QString, QVariant> cData({
|
||||||
|
{"name", re.name()},
|
||||||
|
{"state", QVariant::fromValue(state)}
|
||||||
|
});
|
||||||
|
|
||||||
|
careAboutAvatar(contact, cData);
|
||||||
|
int grCount = 0;
|
||||||
|
for (QSet<QString>::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) {
|
||||||
|
const QString& groupName = *itr;
|
||||||
|
addToGroup(jid, groupName);
|
||||||
|
emit acc->addContact(jid, groupName, cData);
|
||||||
|
grCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grCount == 0) {
|
||||||
|
emit acc->addContact(jid, "", cData);
|
||||||
|
}
|
||||||
|
handleNewContact(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin)
|
||||||
|
{
|
||||||
|
QXmppMucRoom* room = acc->mm->addRoom(jid);
|
||||||
|
QString lNick = nick;
|
||||||
|
if (lNick.size() == 0) {
|
||||||
|
lNick = acc->getName();
|
||||||
|
}
|
||||||
|
Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room);
|
||||||
|
conferences.insert(std::make_pair(jid, conf));
|
||||||
|
|
||||||
|
handleNewConference(conf);
|
||||||
|
|
||||||
|
QMap<QString, QVariant> cData = {
|
||||||
|
{"autoJoin", conf->getAutoJoin()},
|
||||||
|
{"joined", conf->getJoined()},
|
||||||
|
{"nick", conf->getNick()},
|
||||||
|
{"name", conf->getName()},
|
||||||
|
{"avatars", conf->getAllAvatars()}
|
||||||
|
};
|
||||||
|
careAboutAvatar(conf, cData);
|
||||||
|
emit acc->addRoom(jid, cData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
bool hasAvatar = item->readAvatarInfo(info);
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated) {
|
||||||
|
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated));
|
||||||
|
} else {
|
||||||
|
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid));
|
||||||
|
}
|
||||||
|
data.insert("avatarPath", item->avatarPath() + "." + info.type);
|
||||||
|
} else {
|
||||||
|
data.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty));
|
||||||
|
data.insert("avatarPath", "");
|
||||||
|
acc->requestVCard(item->jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups)
|
||||||
|
{
|
||||||
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
|
std::map<QString, QString>::const_iterator itr = queuedContacts.find(jid);
|
||||||
|
if (itr != queuedContacts.end()) {
|
||||||
|
qDebug() << "An attempt to add contact " << jid << " to account " << acc->name << " but the account is already queued for adding, skipping";
|
||||||
|
} else {
|
||||||
|
queuedContacts.insert(std::make_pair(jid, "")); //TODO need to add reason here;
|
||||||
|
acc->rm->addItem(jid, name, groups);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to add contact " << jid << " to account " << acc->name << " but the account is not in the connected state, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::removeContactRequest(const QString& jid)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
if (acc->state == Shared::ConnectionState::connected) {
|
||||||
|
std::set<QString>::const_iterator itr = outOfRosterContacts.find(lcJid);
|
||||||
|
if (itr != outOfRosterContacts.end()) {
|
||||||
|
outOfRosterContacts.erase(itr);
|
||||||
|
onRosterItemRemoved(lcJid);
|
||||||
|
} else {
|
||||||
|
acc->rm->removeItem(lcJid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to remove contact " << lcJid << " from account " << acc->name << " but the account is not in the connected state, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact)
|
||||||
|
{
|
||||||
|
connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory);
|
||||||
|
connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse);
|
||||||
|
connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged);
|
||||||
|
connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged);
|
||||||
|
connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::handleNewContact(Core::Contact* contact)
|
||||||
|
{
|
||||||
|
handleNewRosterItem(contact);
|
||||||
|
connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded);
|
||||||
|
connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved);
|
||||||
|
connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::handleNewConference(Core::Conference* contact)
|
||||||
|
{
|
||||||
|
handleNewRosterItem(contact);
|
||||||
|
connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged);
|
||||||
|
connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged);
|
||||||
|
connect(contact, &Conference::joinedChanged, this, &RosterHandler::onMucJoinedChanged);
|
||||||
|
connect(contact, &Conference::autoJoinChanged, this, &RosterHandler::onMucAutoJoinChanged);
|
||||||
|
connect(contact, &Conference::addParticipant, this, &RosterHandler::onMucAddParticipant);
|
||||||
|
connect(contact, &Conference::changeParticipant, this, &RosterHandler::onMucChangeParticipant);
|
||||||
|
connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->addRoomParticipant(room->jid, nickName, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->changeRoomParticipant(room->jid, nickName, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName)
|
||||||
|
{
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->removeRoomParticipant(room->jid, nickName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucSubjectChanged(const QString& subject)
|
||||||
|
{
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->changeRoom(room->jid, {
|
||||||
|
{"subject", subject}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onContactGroupAdded(const QString& group)
|
||||||
|
{
|
||||||
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
|
if (contact->groupsCount() == 1) {
|
||||||
|
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QVariant> cData({
|
||||||
|
{"name", contact->getName()},
|
||||||
|
{"state", QVariant::fromValue(contact->getSubscriptionState())}
|
||||||
|
});
|
||||||
|
addToGroup(contact->jid, group);
|
||||||
|
emit acc->addContact(contact->jid, group, cData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onContactGroupRemoved(const QString& group)
|
||||||
|
{
|
||||||
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
|
if (contact->groupsCount() == 0) {
|
||||||
|
// not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
emit acc->removeContact(contact->jid, group);
|
||||||
|
removeFromGroup(contact->jid, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onContactNameChanged(const QString& cname)
|
||||||
|
{
|
||||||
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
|
QMap<QString, QVariant> cData({
|
||||||
|
{"name", cname},
|
||||||
|
});
|
||||||
|
emit acc->changeContact(contact->jid, cData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate)
|
||||||
|
{
|
||||||
|
Contact* contact = static_cast<Contact*>(sender());
|
||||||
|
QMap<QString, QVariant> cData({
|
||||||
|
{"state", QVariant::fromValue(cstate)},
|
||||||
|
});
|
||||||
|
emit acc->changeContact(contact->jid, cData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addToGroup(const QString& jid, const QString& group)
|
||||||
|
{
|
||||||
|
std::map<QString, std::set<QString>>::iterator gItr = groups.find(group);
|
||||||
|
if (gItr == groups.end()) {
|
||||||
|
gItr = groups.insert(std::make_pair(group, std::set<QString>())).first;
|
||||||
|
emit acc->addGroup(group);
|
||||||
|
}
|
||||||
|
gItr->second.insert(jid.toLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group)
|
||||||
|
{
|
||||||
|
QSet<QString> toRemove;
|
||||||
|
std::map<QString, std::set<QString>>::iterator itr = groups.find(group);
|
||||||
|
if (itr == groups.end()) {
|
||||||
|
qDebug() << "An attempt to remove contact" << jid << "of account" << acc->name << "from non existing group" << group << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::set<QString> contacts = itr->second;
|
||||||
|
std::set<QString>::const_iterator cItr = contacts.find(jid.toLower());
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
contacts.erase(cItr);
|
||||||
|
if (contacts.size() == 0) {
|
||||||
|
emit acc->removeGroup(group);
|
||||||
|
groups.erase(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid)
|
||||||
|
{
|
||||||
|
RosterItem* item = 0;
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
std::map<QString, Contact*>::const_iterator citr = contacts.find(lcJid);
|
||||||
|
if (citr != contacts.end()) {
|
||||||
|
item = citr->second;
|
||||||
|
} else {
|
||||||
|
std::map<QString, Conference*>::const_iterator coitr = conferences.find(lcJid);
|
||||||
|
if (coitr != conferences.end()) {
|
||||||
|
item = coitr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Conference * Core::RosterHandler::getConference(const QString& jid)
|
||||||
|
{
|
||||||
|
Conference* item = 0;
|
||||||
|
std::map<QString, Conference*>::const_iterator coitr = conferences.find(jid.toLower());
|
||||||
|
if (coitr != conferences.end()) {
|
||||||
|
item = coitr->second;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Contact * Core::RosterHandler::getContact(const QString& jid)
|
||||||
|
{
|
||||||
|
Contact* item = 0;
|
||||||
|
std::map<QString, Contact*>::const_iterator citr = contacts.find(jid.toLower());
|
||||||
|
if (citr != contacts.end()) {
|
||||||
|
item = citr->second;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
Contact* cnt = new Contact(lcJid, acc->name);
|
||||||
|
contacts.insert(std::make_pair(lcJid, cnt));
|
||||||
|
outOfRosterContacts.insert(lcJid);
|
||||||
|
cnt->setSubscriptionState(Shared::SubscriptionState::unknown);
|
||||||
|
emit acc->addContact(lcJid, "", QMap<QString, QVariant>({
|
||||||
|
{"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}
|
||||||
|
}));
|
||||||
|
handleNewContact(cnt);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onRosterItemChanged(const QString& bareJid)
|
||||||
|
{
|
||||||
|
QString lcJid = bareJid.toLower();
|
||||||
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
|
if (itr == contacts.end()) {
|
||||||
|
qDebug() << "An attempt to change non existing contact" << lcJid << "from account" << acc->name << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Contact* contact = itr->second;
|
||||||
|
QXmppRosterIq::Item re = acc->rm->getRosterEntry(lcJid);
|
||||||
|
|
||||||
|
Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType());
|
||||||
|
|
||||||
|
contact->setGroups(re.groups());
|
||||||
|
contact->setSubscriptionState(state);
|
||||||
|
contact->setName(re.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid)
|
||||||
|
{
|
||||||
|
QString lcJid = bareJid.toLower();
|
||||||
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
|
if (itr == contacts.end()) {
|
||||||
|
qDebug() << "An attempt to remove non existing contact" << lcJid << "from account" << acc->name << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Contact* contact = itr->second;
|
||||||
|
contacts.erase(itr);
|
||||||
|
QSet<QString> cGroups = contact->getGroups();
|
||||||
|
for (QSet<QString>::const_iterator itr = cGroups.begin(), end = cGroups.end(); itr != end; ++itr) {
|
||||||
|
removeFromGroup(lcJid, *itr);
|
||||||
|
}
|
||||||
|
emit acc->removeContact(lcJid);
|
||||||
|
|
||||||
|
contact->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room)
|
||||||
|
{
|
||||||
|
qDebug() << "room" << room->jid() << "added with name" << room->name()
|
||||||
|
<< ", account" << acc->getName() << "joined:" << room->isJoined();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks)
|
||||||
|
{
|
||||||
|
QList<QXmppBookmarkConference> confs = bookmarks.conferences();
|
||||||
|
for (QList<QXmppBookmarkConference>::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) {
|
||||||
|
const QXmppBookmarkConference& c = *itr;
|
||||||
|
|
||||||
|
QString jid = c.jid().toLower();
|
||||||
|
std::map<QString, Conference*>::const_iterator cItr = conferences.find(jid);
|
||||||
|
if (cItr == conferences.end()) {
|
||||||
|
addNewRoom(jid, c.nickName(), c.name(), c.autoJoin());
|
||||||
|
} else {
|
||||||
|
if (c.autoJoin()) {
|
||||||
|
cItr->second->setJoined(true);
|
||||||
|
} else {
|
||||||
|
cItr->second->setAutoJoin(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucJoinedChanged(bool joined)
|
||||||
|
{
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->changeRoom(room->jid, {
|
||||||
|
{"joined", joined}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin)
|
||||||
|
{
|
||||||
|
storeConferences();
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->changeRoom(room->jid, {
|
||||||
|
{"autoJoin", autoJoin}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::onMucNickNameChanged(const QString& nickName)
|
||||||
|
{
|
||||||
|
storeConferences();
|
||||||
|
Conference* room = static_cast<Conference*>(sender());
|
||||||
|
emit acc->changeRoom(room->jid, {
|
||||||
|
{"nick", nickName}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs)
|
||||||
|
{
|
||||||
|
Shared::SubscriptionState state;
|
||||||
|
if (qs == QXmppRosterIq::Item::NotSet) {
|
||||||
|
state = Shared::SubscriptionState::unknown;
|
||||||
|
} else {
|
||||||
|
state = static_cast<Shared::SubscriptionState>(qs);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::storeConferences()
|
||||||
|
{
|
||||||
|
QXmppBookmarkSet bms = acc->bm->bookmarks();
|
||||||
|
QList<QXmppBookmarkConference> confs;
|
||||||
|
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) {
|
||||||
|
Conference* conference = itr->second;
|
||||||
|
QXmppBookmarkConference conf;
|
||||||
|
conf.setJid(conference->jid);
|
||||||
|
conf.setName(conference->getName());
|
||||||
|
conf.setNickName(conference->getNick());
|
||||||
|
conf.setAutoJoin(conference->getAutoJoin());
|
||||||
|
confs.push_back(conf);
|
||||||
|
}
|
||||||
|
bms.setConferences(confs);
|
||||||
|
acc->bm->setBookmarks(bms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::clearConferences()
|
||||||
|
{
|
||||||
|
for (std::map<QString, Conference*>::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) {
|
||||||
|
itr->second->deleteLater();
|
||||||
|
emit acc->removeRoom(itr->first);
|
||||||
|
}
|
||||||
|
conferences.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::removeRoomRequest(const QString& jid)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
std::map<QString, Conference*>::const_iterator itr = conferences.find(lcJid);
|
||||||
|
if (itr == conferences.end()) {
|
||||||
|
qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping";
|
||||||
|
}
|
||||||
|
itr->second->deleteLater();
|
||||||
|
conferences.erase(itr);
|
||||||
|
emit acc->removeRoom(lcJid);
|
||||||
|
storeConferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
std::map<QString, Conference*>::const_iterator cItr = conferences.find(lcJid);
|
||||||
|
if (cItr == conferences.end()) {
|
||||||
|
addNewRoom(lcJid, nick, "", autoJoin);
|
||||||
|
storeConferences();
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to add a MUC " << lcJid << " which is already present in the rester, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
|
if (itr == contacts.end()) {
|
||||||
|
qDebug() << "An attempt to add non existing contact" << lcJid << "of account"
|
||||||
|
<< acc->name << "to the group" << groupName << ", skipping";
|
||||||
|
} else {
|
||||||
|
QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
|
||||||
|
QSet<QString> groups = item.groups();
|
||||||
|
if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib
|
||||||
|
groups.insert(groupName);
|
||||||
|
item.setGroups(groups);
|
||||||
|
|
||||||
|
QXmppRosterIq iq;
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
iq.addItem(item);
|
||||||
|
acc->client.sendPacket(iq);
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to add contact" << jid << "of account"
|
||||||
|
<< acc->name << "to the group" << groupName << "but it's already in that group, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
|
||||||
|
{
|
||||||
|
QString lcJid = jid.toLower();
|
||||||
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(lcJid);
|
||||||
|
if (itr == contacts.end()) {
|
||||||
|
qDebug() << "An attempt to remove non existing contact" << lcJid << "of account"
|
||||||
|
<< acc->name << "from the group" << groupName << ", skipping";
|
||||||
|
} else {
|
||||||
|
QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid);
|
||||||
|
QSet<QString> groups = item.groups();
|
||||||
|
QSet<QString>::const_iterator gItr = groups.find(groupName);
|
||||||
|
if (gItr != groups.end()) {
|
||||||
|
groups.erase(gItr);
|
||||||
|
item.setGroups(groups);
|
||||||
|
|
||||||
|
QXmppRosterIq iq;
|
||||||
|
iq.setType(QXmppIq::Set);
|
||||||
|
iq.addItem(item);
|
||||||
|
acc->client.sendPacket(iq);
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
RosterItem* item = static_cast<RosterItem*>(sender());
|
||||||
|
QMap<QString, QVariant> cData({
|
||||||
|
{"avatarState", static_cast<uint>(type)},
|
||||||
|
{"avatarPath", path}
|
||||||
|
});
|
||||||
|
|
||||||
|
emit acc->changeContact(item->jid, cData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterHandler::handleOffline()
|
||||||
|
{
|
||||||
|
for (const std::pair<const QString, Conference*>& pair : conferences) {
|
||||||
|
pair.second->clearArchiveRequests();
|
||||||
|
pair.second->downgradeDatabaseState();
|
||||||
|
}
|
||||||
|
for (const std::pair<const QString, Contact*>& pair : contacts) {
|
||||||
|
pair.second->clearArchiveRequests();
|
||||||
|
pair.second->downgradeDatabaseState();
|
||||||
|
}
|
||||||
|
setPepSupport(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Core::RosterHandler::setPepSupport(bool support)
|
||||||
|
{
|
||||||
|
if (pepSupport != support) {
|
||||||
|
pepSupport = support;
|
||||||
|
}
|
||||||
|
}
|
116
core/handlers/rosterhandler.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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_ROSTERHANDLER_H
|
||||||
|
#define CORE_ROSTERHANDLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QXmppBookmarkSet.h>
|
||||||
|
#include <QXmppMucManager.h>
|
||||||
|
#include <QXmppRosterIq.h>
|
||||||
|
|
||||||
|
#include <shared/message.h>
|
||||||
|
#include <core/contact.h>
|
||||||
|
#include <core/conference.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class RosterHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
RosterHandler(Account* account);
|
||||||
|
~RosterHandler();
|
||||||
|
|
||||||
|
void addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||||
|
void removeContactRequest(const QString& jid);
|
||||||
|
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||||
|
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||||
|
|
||||||
|
void removeRoomRequest(const QString& jid);
|
||||||
|
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
|
void handleOffline();
|
||||||
|
|
||||||
|
Core::Contact* getContact(const QString& jid);
|
||||||
|
Core::Conference* getConference(const QString& jid);
|
||||||
|
Core::RosterItem* getRosterItem(const QString& jid);
|
||||||
|
Core::Contact* addOutOfRosterContact(const QString& jid);
|
||||||
|
|
||||||
|
void storeConferences();
|
||||||
|
void clearConferences();
|
||||||
|
void setPepSupport(bool support);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onRosterReceived();
|
||||||
|
void onRosterItemAdded(const QString& bareJid);
|
||||||
|
void onRosterItemChanged(const QString& bareJid);
|
||||||
|
void onRosterItemRemoved(const QString& bareJid);
|
||||||
|
|
||||||
|
void onMucRoomAdded(QXmppMucRoom* room);
|
||||||
|
void onMucJoinedChanged(bool joined);
|
||||||
|
void onMucAutoJoinChanged(bool autoJoin);
|
||||||
|
void onMucNickNameChanged(const QString& nickName);
|
||||||
|
void onMucSubjectChanged(const QString& subject);
|
||||||
|
void onMucAddParticipant(const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
|
void onMucChangeParticipant(const QString& nickName, const QMap<QString, QVariant>& data);
|
||||||
|
void onMucRemoveParticipant(const QString& nickName);
|
||||||
|
|
||||||
|
void bookmarksReceived(const QXmppBookmarkSet& bookmarks);
|
||||||
|
|
||||||
|
void onContactGroupAdded(const QString& group);
|
||||||
|
void onContactGroupRemoved(const QString& group);
|
||||||
|
void onContactNameChanged(const QString& name);
|
||||||
|
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
|
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin);
|
||||||
|
void addToGroup(const QString& jid, const QString& group);
|
||||||
|
void removeFromGroup(const QString& jid, const QString& group);
|
||||||
|
void addedAccount(const QString &bareJid);
|
||||||
|
void handleNewRosterItem(Core::RosterItem* contact);
|
||||||
|
void handleNewContact(Core::Contact* contact);
|
||||||
|
void handleNewConference(Core::Conference* contact);
|
||||||
|
void careAboutAvatar(Core::RosterItem* item, QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
std::map<QString, Contact*> contacts;
|
||||||
|
std::map<QString, Conference*> conferences;
|
||||||
|
std::map<QString, std::set<QString>> groups;
|
||||||
|
std::map<QString, QString> queuedContacts;
|
||||||
|
std::set<QString> outOfRosterContacts;
|
||||||
|
bool pepSupport;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_ROSTERHANDLER_H
|
312
core/handlers/vcardhandler.cpp
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
// 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),
|
||||||
|
ownVCardRequestInProgress(false),
|
||||||
|
pendingVCardRequests(),
|
||||||
|
avatarHash(),
|
||||||
|
avatarType()
|
||||||
|
{
|
||||||
|
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
|
||||||
|
//for some reason it doesn't work, launching from common handler
|
||||||
|
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
|
||||||
|
|
||||||
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avatarType.size() != 0) {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
} else {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::VCardHandler::~VCardHandler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
pendingVCardRequests.erase(id);
|
||||||
|
RosterItem* item = acc->rh->getRosterItem(jid);
|
||||||
|
|
||||||
|
if (item == 0) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
emit acc->receivedVCard(jid, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit acc->receivedVCard(acc->getBareJid(), vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::handleOffline()
|
||||||
|
{
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
Shared::VCard vCard; //just to show, that there is now more pending request
|
||||||
|
for (const QString& jid : pendingVCardRequests) {
|
||||||
|
emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
||||||
|
}
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::requestVCard(const QString& jid)
|
||||||
|
{
|
||||||
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||||
|
qDebug() << "requesting vCard" << jid;
|
||||||
|
if (jid == acc->getBareJid()) {
|
||||||
|
if (!ownVCardRequestInProgress) {
|
||||||
|
acc->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc->vm->requestVCard(jid);
|
||||||
|
pendingVCardRequests.insert(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence)
|
||||||
|
{
|
||||||
|
if (!ownVCardRequestInProgress) {
|
||||||
|
switch (p_presence.vCardUpdateType()) {
|
||||||
|
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->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||||
|
if (avatarHash != p_presence.photoHash()) {
|
||||||
|
acc->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
65
core/handlers/vcardhandler.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// 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 handleOffline();
|
||||||
|
void requestVCard(const QString& jid);
|
||||||
|
void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence);
|
||||||
|
void uploadVCard(const Shared::VCard& card);
|
||||||
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onVCardReceived(const QXmppVCardIq& card);
|
||||||
|
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
|
||||||
|
bool ownVCardRequestInProgress;
|
||||||
|
std::set<QString> pendingVCardRequests;
|
||||||
|
QString avatarHash;
|
||||||
|
QString avatarType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_VCARDHANDLER_H
|
@ -16,16 +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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
|
||||||
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
Core::NetworkAccess::NetworkAccess(QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
running(false),
|
running(false),
|
||||||
manager(0),
|
manager(0),
|
||||||
files("files"),
|
storage("fileURLStorage"),
|
||||||
downloads(),
|
downloads(),
|
||||||
uploads()
|
uploads(),
|
||||||
|
currentPath()
|
||||||
{
|
{
|
||||||
|
QSettings settings;
|
||||||
|
currentPath = settings.value("downloadsPath").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::NetworkAccess::~NetworkAccess()
|
Core::NetworkAccess::~NetworkAccess()
|
||||||
@ -33,60 +40,31 @@ Core::NetworkAccess::~NetworkAccess()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url)
|
void Core::NetworkAccess::downladFile(const QString& url)
|
||||||
{
|
{
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
if (itr != downloads.end()) {
|
if (itr != downloads.end()) {
|
||||||
Transfer* dwn = itr->second;
|
qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping";
|
||||||
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
|
||||||
if (mItr == dwn->messages.end()) {
|
|
||||||
dwn->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit downloadFileProgress(messageId, dwn->progress);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
QString path = files.getRecord(url);
|
std::pair<QString, std::list<Shared::MessageInfo>> p = storage.getPath(url);
|
||||||
QFileInfo info(path);
|
if (p.first.size() > 0) {
|
||||||
if (info.exists() && info.isFile()) {
|
QFileInfo info(p.first);
|
||||||
emit fileLocalPathResponse(messageId, path);
|
if (info.exists() && info.isFile()) {
|
||||||
|
emit downloadFileComplete(p.second, p.first);
|
||||||
|
} else {
|
||||||
|
startDownload(p.second, url);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
files.removeRecord(url);
|
startDownload(p.second, url);
|
||||||
emit fileLocalPathResponse(messageId, "");
|
|
||||||
}
|
}
|
||||||
} catch (const Archive::NotFound& e) {
|
} catch (const Archive::NotFound& e) {
|
||||||
emit fileLocalPathResponse(messageId, "");
|
qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway";
|
||||||
|
storage.addFile(url);
|
||||||
|
startDownload(std::list<Shared::MessageInfo>(), url);
|
||||||
} catch (const Archive::Unknown& e) {
|
} catch (const Archive::Unknown& e) {
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
qDebug() << "Error requesting file path:" << e.what();
|
||||||
emit fileLocalPathResponse(messageId, "");
|
emit loadFileError(std::list<Shared::MessageInfo>(), QString("Database error: ") + e.what(), false);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url)
|
|
||||||
{
|
|
||||||
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
|
||||||
if (itr != downloads.end()) {
|
|
||||||
Transfer* dwn = itr->second;
|
|
||||||
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
|
|
||||||
if (mItr == dwn->messages.end()) {
|
|
||||||
dwn->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit downloadFileProgress(messageId, dwn->progress);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
QString path = files.getRecord(url);
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startDownload(messageId, url);
|
|
||||||
}
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
startDownload(messageId, url);
|
|
||||||
} catch (const Archive::Unknown& e) {
|
|
||||||
qDebug() << "Error requesting file path:" << e.what();
|
|
||||||
emit downloadFileError(messageId, QString("Database error: ") + e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +73,10 @@ void Core::NetworkAccess::start()
|
|||||||
{
|
{
|
||||||
if (!running) {
|
if (!running) {
|
||||||
manager = new QNetworkAccessManager();
|
manager = new QNetworkAccessManager();
|
||||||
files.open();
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
manager->setTransferTimeout();
|
||||||
|
#endif
|
||||||
|
storage.open();
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +84,7 @@ void Core::NetworkAccess::start()
|
|||||||
void Core::NetworkAccess::stop()
|
void Core::NetworkAccess::stop()
|
||||||
{
|
{
|
||||||
if (running) {
|
if (running) {
|
||||||
files.close();
|
storage.close();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
manager = 0;
|
manager = 0;
|
||||||
running = false;
|
running = false;
|
||||||
@ -124,35 +105,56 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
|||||||
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
qreal received = bytesReceived;
|
if (dwn->success) {
|
||||||
qreal total = bytesTotal;
|
qreal received = bytesReceived;
|
||||||
qreal progress = received/total;
|
qreal total = bytesTotal;
|
||||||
dwn->progress = progress;
|
qreal progress = received/total;
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
dwn->progress = progress;
|
||||||
emit downloadFileProgress(*mItr, progress);
|
emit loadFileProgress(dwn->messages, progress, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
|
||||||
{
|
{
|
||||||
|
qDebug() << "DEBUG: DOWNLOAD ERROR";
|
||||||
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
|
qDebug() << rpl->errorString();
|
||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
QString errorText = getErrorText(code);
|
QString errorText = getErrorText(code);
|
||||||
if (errorText.size() > 0) {
|
//if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
emit loadFileError(dwn->messages, errorText, false);
|
||||||
emit downloadFileError(*mItr, errorText);
|
//}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
|
||||||
|
{
|
||||||
|
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
|
||||||
|
for (const QSslError& err : errors) {
|
||||||
|
qDebug() << err.errorString();
|
||||||
|
}
|
||||||
|
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
|
||||||
|
QString url = rpl->url().toString();
|
||||||
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
|
if (itr == downloads.end()) {
|
||||||
|
qDebug() << "an SSL error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
|
||||||
|
} else {
|
||||||
|
//if (errorText.size() > 0) {
|
||||||
|
itr->second->success = false;
|
||||||
|
Transfer* dwn = itr->second;
|
||||||
|
emit loadFileError(dwn->messages, "SSL errors occured", false);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
||||||
{
|
{
|
||||||
QString errorText("");
|
QString errorText("");
|
||||||
@ -175,7 +177,11 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
errorText = "Connection was closed because it timed out";
|
errorText = "Connection was closed because it timed out";
|
||||||
break;
|
break;
|
||||||
case QNetworkReply::OperationCanceledError:
|
case QNetworkReply::OperationCanceledError:
|
||||||
//this means I closed it myself by abort() or close(), don't think I need to notify here
|
//this means I closed it myself by abort() or close()
|
||||||
|
//I don't call them directory, but this is the error code
|
||||||
|
//Qt returns when it can not resume donwload after the network failure
|
||||||
|
//or when the download is canceled by the timout;
|
||||||
|
errorText = "Connection lost";
|
||||||
break;
|
break;
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
case QNetworkReply::SslHandshakeFailedError:
|
||||||
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
|
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
|
||||||
@ -276,50 +282,48 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
|
|||||||
|
|
||||||
void Core::NetworkAccess::onDownloadFinished()
|
void Core::NetworkAccess::onDownloadFinished()
|
||||||
{
|
{
|
||||||
QString path("");
|
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();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request is done but there is no record of it being downloaded, ignoring";
|
||||||
} else {
|
} else {
|
||||||
Transfer* dwn = itr->second;
|
Transfer* dwn = itr->second;
|
||||||
if (dwn->success) {
|
if (dwn->success) {
|
||||||
qDebug() << "download success for" << url;
|
qDebug() << "download success for" << url;
|
||||||
|
QString err;
|
||||||
QStringList hops = url.split("/");
|
QStringList hops = url.split("/");
|
||||||
QString fileName = hops.back();
|
QString fileName = hops.back();
|
||||||
QStringList parts = fileName.split(".");
|
QString jid;
|
||||||
path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/";
|
if (dwn->messages.size() > 0) {
|
||||||
QString suffix("");
|
jid = dwn->messages.front().jid;
|
||||||
QStringList::const_iterator sItr = parts.begin();
|
|
||||||
QString realName = *sItr;
|
|
||||||
++sItr;
|
|
||||||
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
|
||||||
suffix += "." + (*sItr);
|
|
||||||
}
|
|
||||||
QString postfix("");
|
|
||||||
QFileInfo proposedName(path + realName + postfix + suffix);
|
|
||||||
int counter = 0;
|
|
||||||
while (proposedName.exists()) {
|
|
||||||
postfix = QString("(") + std::to_string(++counter).c_str() + ")";
|
|
||||||
proposedName = QFileInfo(path + realName + postfix + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = proposedName.absoluteFilePath();
|
|
||||||
QFile file(path);
|
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
|
||||||
file.write(dwn->reply->readAll());
|
|
||||||
file.close();
|
|
||||||
files.addRecord(url, path);
|
|
||||||
qDebug() << "file" << path << "was successfully downloaded";
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't save file" << path;
|
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
|
||||||
path = "";
|
|
||||||
}
|
}
|
||||||
}
|
QString path = prepareDirectory(jid);
|
||||||
|
if (path.size() > 0) {
|
||||||
|
path = checkFileName(fileName, path);
|
||||||
|
|
||||||
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
|
QFile file(Shared::resolvePath(path));
|
||||||
emit fileLocalPathResponse(*mItr, path);
|
if (file.open(QIODevice::WriteOnly)) {
|
||||||
|
file.write(dwn->reply->readAll());
|
||||||
|
file.close();
|
||||||
|
storage.setPath(url, path);
|
||||||
|
qDebug() << "file" << path << "was successfully downloaded";
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't save file" << path;
|
||||||
|
err = "Error opening file to write:" + file.errorString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = "Couldn't prepare a directory for file";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.size() > 0) {
|
||||||
|
emit downloadFileComplete(dwn->messages, path);
|
||||||
|
} else {
|
||||||
|
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dwn->reply->deleteLater();
|
dwn->reply->deleteLater();
|
||||||
@ -328,16 +332,21 @@ void Core::NetworkAccess::onDownloadFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url)
|
void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url)
|
||||||
{
|
{
|
||||||
Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", url, 0});
|
Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0});
|
||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
dwn->reply = manager->get(req);
|
dwn->reply = manager->get(req);
|
||||||
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
||||||
|
connect(dwn->reply, &QNetworkReply::sslErrors, this, &NetworkAccess::onDownloadSSLError);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onDownloadError);
|
||||||
|
#else
|
||||||
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onDownloadError);
|
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onDownloadError);
|
||||||
|
#endif
|
||||||
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
||||||
downloads.insert(std::make_pair(url, dwn));
|
downloads.insert(std::make_pair(url, dwn));
|
||||||
emit downloadFileProgress(messageId, 0);
|
emit loadFileProgress(dwn->messages, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
||||||
@ -346,16 +355,16 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
|
|||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
if (itr == uploads.end()) {
|
if (itr == uploads.end()) {
|
||||||
qDebug() << "an error uploading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping";
|
qDebug() << "an error uploading" << url << ": the request is reporting an error but there is no record of it being uploading, ignoring";
|
||||||
} else {
|
} else {
|
||||||
QString errorText = getErrorText(code);
|
QString errorText = getErrorText(code);
|
||||||
if (errorText.size() > 0) {
|
//if (errorText.size() > 0) {
|
||||||
itr->second->success = false;
|
itr->second->success = false;
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
emit loadFileError(upl->messages, errorText, true);
|
||||||
emit uploadFileError(*mItr, errorText);
|
//}
|
||||||
}
|
|
||||||
}
|
//TODO deletion?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,17 +374,39 @@ void Core::NetworkAccess::onUploadFinished()
|
|||||||
QString url = rpl->url().toString();
|
QString url = rpl->url().toString();
|
||||||
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
|
||||||
if (itr == downloads.end()) {
|
if (itr == downloads.end()) {
|
||||||
qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping";
|
qDebug() << "an error uploading" << url << ": the request is done there is no record of it being uploading, ignoring";
|
||||||
} else {
|
} else {
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
if (upl->success) {
|
if (upl->success) {
|
||||||
qDebug() << "upload success for" << url;
|
qDebug() << "upload success for" << url;
|
||||||
files.addRecord(upl->url, upl->path);
|
|
||||||
|
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
// Copy file to Download folder if it is a temp file. See Conversation::onImagePasted.
|
||||||
emit fileLocalPathResponse(*mItr, upl->path);
|
if (upl->path.startsWith(QDir::tempPath() + QDir::separator() + QStringLiteral("squawk_img_attach_")) && upl->path.endsWith(".png")) {
|
||||||
emit uploadFileComplete(*mItr, upl->url);
|
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) {
|
||||||
|
// Change storage
|
||||||
|
upl->path = newPath;
|
||||||
|
} 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);
|
||||||
|
emit uploadFileComplete(upl->messages, upl->url, upl->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
upl->reply->deleteLater();
|
upl->reply->deleteLater();
|
||||||
@ -395,93 +426,35 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
|
|||||||
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
|
||||||
} else {
|
} else {
|
||||||
Transfer* upl = itr->second;
|
Transfer* upl = itr->second;
|
||||||
qreal received = bytesReceived;
|
if (upl->success) {
|
||||||
qreal total = bytesTotal;
|
qreal received = bytesReceived;
|
||||||
qreal progress = received/total;
|
qreal total = bytesTotal;
|
||||||
upl->progress = progress;
|
qreal progress = received/total;
|
||||||
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
|
upl->progress = progress;
|
||||||
emit uploadFileProgress(*mItr, progress);
|
emit loadFileProgress(upl->messages, progress, true);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0});
|
|
||||||
QNetworkRequest req(url);
|
|
||||||
QFile* file = new QFile(path);
|
|
||||||
if (file->open(QIODevice::ReadOnly)) {
|
|
||||||
upl->reply = manager->put(req, file);
|
|
||||||
|
|
||||||
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
|
|
||||||
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
|
|
||||||
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
|
||||||
uploads.insert(std::make_pair(url, upl));
|
|
||||||
emit downloadFileProgress(messageId, 0);
|
|
||||||
} else {
|
|
||||||
qDebug() << "couldn't upload file" << path;
|
|
||||||
emit uploadFileError(messageId, "Error opening file");
|
|
||||||
delete file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QString& url, const QString& path)
|
|
||||||
{
|
|
||||||
std::map<QString, Transfer*>::iterator itr = uploads.find(url);
|
|
||||||
if (itr != uploads.end()) {
|
|
||||||
Transfer* upl = itr->second;
|
|
||||||
std::set<QString>::const_iterator mItr = upl->messages.find(messageId);
|
|
||||||
if (mItr == upl->messages.end()) {
|
|
||||||
upl->messages.insert(messageId);
|
|
||||||
}
|
|
||||||
emit uploadFileProgress(messageId, upl->progress);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
QString ePath = files.getRecord(url);
|
|
||||||
if (ePath == path) {
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (info.exists() && info.isFile()) {
|
|
||||||
files.changeRecord(url, path);
|
|
||||||
emit fileLocalPathResponse(messageId, path);
|
|
||||||
} else {
|
|
||||||
files.removeRecord(url);
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const Archive::NotFound& e) {
|
|
||||||
startUpload(messageId, url, path);
|
|
||||||
} catch (const Archive::Unknown& e) {
|
|
||||||
qDebug() << "Error requesting file path on upload:" << e.what();
|
|
||||||
emit uploadFileError(messageId, QString("Database error: ") + e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
|
||||||
{
|
{
|
||||||
return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url
|
QString p = Shared::squawkifyPath(path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
p = storage.getUrl(p);
|
||||||
|
} catch (const Archive::NotFound& err) {
|
||||||
|
p = "";
|
||||||
|
} catch (...) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId)
|
void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
||||||
{
|
|
||||||
return false; //TODO this is a way to avoid parallel uploading of the same files by different chats
|
|
||||||
// message is is supposed to be added to the uploading messageids list
|
|
||||||
// the result should be true if there was an uploading file with this path
|
|
||||||
// message id can be empty, then it's just to check and not to add
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
|
|
||||||
{
|
{
|
||||||
QFile* file = new QFile(path);
|
QFile* file = new QFile(path);
|
||||||
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), file});
|
Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file});
|
||||||
QNetworkRequest req(put);
|
QNetworkRequest req(put);
|
||||||
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
|
||||||
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
|
||||||
@ -490,13 +463,127 @@ void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& pa
|
|||||||
upl->reply = manager->put(req, file);
|
upl->reply = manager->put(req, file);
|
||||||
|
|
||||||
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
|
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onUploadError);
|
||||||
|
#else
|
||||||
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
|
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
|
||||||
|
#endif
|
||||||
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
|
||||||
uploads.insert(std::make_pair(put.toString(), upl));
|
uploads.insert(std::make_pair(put.toString(), upl));
|
||||||
emit downloadFileProgress(messageId, 0);
|
emit loadFileProgress(upl->messages, 0, true);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "couldn't upload file" << path;
|
qDebug() << "couldn't upload file" << path;
|
||||||
emit uploadFileError(messageId, "Error opening file");
|
emit loadFileError(upl->messages, "Error opening file", true);
|
||||||
delete file;
|
delete file;
|
||||||
|
delete upl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
storage.addFile(url, account, jid, id);
|
||||||
|
std::map<QString, Transfer*>::iterator itr = downloads.find(url);
|
||||||
|
if (itr != downloads.end()) {
|
||||||
|
itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
storage.addFile(url, path, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path)
|
||||||
|
{
|
||||||
|
for (const std::pair<const QString, Transfer*>& pair : uploads) {
|
||||||
|
Transfer* info = pair.second;
|
||||||
|
if (pair.second->path == path) {
|
||||||
|
std::list<Shared::MessageInfo>& messages = info->messages;
|
||||||
|
bool dup = false;
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
if (info.account == acc && info.jid == jid && info.messageId == id) {
|
||||||
|
dup = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dup) {
|
||||||
|
info->messages.emplace_back(acc, jid, id); //TODO notification is going to happen the next tick, is that okay?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::prepareDirectory(const QString& jid)
|
||||||
|
{
|
||||||
|
QString path = currentPath;
|
||||||
|
QString addition;
|
||||||
|
if (jid.size() > 0) {
|
||||||
|
addition = jid;
|
||||||
|
path += QDir::separator() + jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir location(path);
|
||||||
|
|
||||||
|
if (!location.exists()) {
|
||||||
|
bool res = location.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return "squawk://" + addition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "squawk://" + addition;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path)
|
||||||
|
{
|
||||||
|
QStringList parts = name.split(".");
|
||||||
|
QString suffix("");
|
||||||
|
QStringList::const_iterator sItr = parts.begin();
|
||||||
|
QString realName = *sItr;
|
||||||
|
++sItr;
|
||||||
|
for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) {
|
||||||
|
suffix += "." + (*sItr);
|
||||||
|
}
|
||||||
|
QString postfix("");
|
||||||
|
QString resolvedPath = Shared::resolvePath(path);
|
||||||
|
QString count("");
|
||||||
|
QFileInfo proposedName(resolvedPath + QDir::separator() + realName + count + suffix);
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
while (proposedName.exists()) {
|
||||||
|
count = QString("(") + std::to_string(++counter).c_str() + ")";
|
||||||
|
proposedName = QFileInfo(resolvedPath + QDir::separator() + realName + count + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path + QDir::separator() + realName + count + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
return storage.addMessageAndCheckForPath(url, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::NetworkAccess::reportPathInvalid(const QString& 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;
|
||||||
|
}
|
||||||
|
@ -26,16 +26,20 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "storage.h"
|
#include "storage/urlstorage.h"
|
||||||
|
#include "shared/pathcheck.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//TODO Need to describe how to get rid of records when file is no longer reachable;
|
||||||
class NetworkAccess : public QObject
|
class NetworkAccess : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -48,30 +52,33 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
QString getFileRemoteUrl(const QString& path);
|
QString getFileRemoteUrl(const QString& path);
|
||||||
bool isUploading(const QString& path, const QString& messageId = "");
|
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
void uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
|
||||||
|
bool checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path);
|
||||||
|
std::list<Shared::MessageInfo> reportPathInvalid(const QString& path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
void loadFileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
void downloadFileProgress(const QString& messageId, qreal value);
|
void loadFileError(const std::list<Shared::MessageInfo>& msgs, const QString& text, bool up);
|
||||||
void downloadFileError(const QString& messageId, const QString& path);
|
void uploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path);
|
||||||
void uploadFileProgress(const QString& messageId, qreal value);
|
void downloadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path);
|
||||||
void uploadFileError(const QString& messageId, const QString& path);
|
|
||||||
void uploadFileComplete(const QString& messageId, const QString& url);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
void downladFile(const QString& url);
|
||||||
void downladFileRequest(const QString& messageId, const QString& url);
|
void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
void uploadFileRequest(const QString& messageId, const QString& url, const QString& path);
|
void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void moveFilesDirectory(const QString& newPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startDownload(const QString& messageId, const QString& url);
|
void startDownload(const std::list<Shared::MessageInfo>& msgs, const QString& url);
|
||||||
void startUpload(const QString& messageId, const QString& url, const QString& path);
|
|
||||||
QString getErrorText(QNetworkReply::NetworkError code);
|
QString getErrorText(QNetworkReply::NetworkError code);
|
||||||
|
QString prepareDirectory(const QString& jid);
|
||||||
|
QString checkFileName(const QString& name, const QString& path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void onDownloadError(QNetworkReply::NetworkError code);
|
void onDownloadError(QNetworkReply::NetworkError code);
|
||||||
|
void onDownloadSSLError(const QList<QSslError> &errors);
|
||||||
void onDownloadFinished();
|
void onDownloadFinished();
|
||||||
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void onUploadError(QNetworkReply::NetworkError code);
|
void onUploadError(QNetworkReply::NetworkError code);
|
||||||
@ -80,12 +87,13 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
bool running;
|
bool running;
|
||||||
QNetworkAccessManager* manager;
|
QNetworkAccessManager* manager;
|
||||||
Storage files;
|
UrlStorage storage;
|
||||||
std::map<QString, Transfer*> downloads;
|
std::map<QString, Transfer*> downloads;
|
||||||
std::map<QString, Transfer*> uploads;
|
std::map<QString, Transfer*> uploads;
|
||||||
|
QString currentPath;
|
||||||
|
|
||||||
struct Transfer {
|
struct Transfer {
|
||||||
std::set<QString> messages;
|
std::list<Shared::MessageInfo> messages;
|
||||||
qreal progress;
|
qreal progress;
|
||||||
QNetworkReply* reply;
|
QNetworkReply* reply;
|
||||||
bool success;
|
bool success;
|
||||||
|
9
core/passwordStorageEngines/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
if (WITH_KWALLET)
|
||||||
|
target_sources(squawk PRIVATE
|
||||||
|
kwallet.cpp
|
||||||
|
kwallet.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(wrappers)
|
||||||
|
target_include_directories(squawk PRIVATE $<TARGET_PROPERTY:KF5::Wallet,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||||
|
endif ()
|
230
core/passwordStorageEngines/kwallet.cpp
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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 "kwallet.h"
|
||||||
|
|
||||||
|
Core::PSE::KWallet::OpenWallet Core::PSE::KWallet::openWallet = 0;
|
||||||
|
Core::PSE::KWallet::NetworkWallet Core::PSE::KWallet::networkWallet = 0;
|
||||||
|
Core::PSE::KWallet::DeleteWallet Core::PSE::KWallet::deleteWallet = 0;
|
||||||
|
Core::PSE::KWallet::ReadPassword Core::PSE::KWallet::readPassword = 0;
|
||||||
|
Core::PSE::KWallet::WritePassword Core::PSE::KWallet::writePassword = 0;
|
||||||
|
Core::PSE::KWallet::HasFolder Core::PSE::KWallet::hasFolder = 0;
|
||||||
|
Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0;
|
||||||
|
Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0;
|
||||||
|
|
||||||
|
Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial;
|
||||||
|
QLibrary Core::PSE::KWallet::lib("kwalletWrapper");
|
||||||
|
|
||||||
|
Core::PSE::KWallet::KWallet():
|
||||||
|
QObject(),
|
||||||
|
cState(disconnected),
|
||||||
|
everError(false),
|
||||||
|
wallet(0),
|
||||||
|
readRequest(),
|
||||||
|
writeRequest()
|
||||||
|
{
|
||||||
|
if (sState == initial) {
|
||||||
|
lib.load();
|
||||||
|
|
||||||
|
if (lib.isLoaded()) {
|
||||||
|
openWallet = (OpenWallet) lib.resolve("openWallet");
|
||||||
|
networkWallet = (NetworkWallet) lib.resolve("networkWallet");
|
||||||
|
deleteWallet = (DeleteWallet) lib.resolve("deleteWallet");
|
||||||
|
readPassword = (ReadPassword) lib.resolve("readPassword");
|
||||||
|
writePassword = (WritePassword) lib.resolve("writePassword");
|
||||||
|
hasFolder = (HasFolder) lib.resolve("hasFolder");
|
||||||
|
createFolder = (CreateFolder) lib.resolve("createFolder");
|
||||||
|
setFolder = (SetFolder) lib.resolve("setFolder");
|
||||||
|
|
||||||
|
if (openWallet
|
||||||
|
&& networkWallet
|
||||||
|
&& deleteWallet
|
||||||
|
&& readPassword
|
||||||
|
&& writePassword
|
||||||
|
) {
|
||||||
|
sState = success;
|
||||||
|
} else {
|
||||||
|
sState = failure;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sState = failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::PSE::KWallet::~KWallet()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::open()
|
||||||
|
{
|
||||||
|
if (sState == success) {
|
||||||
|
if (cState == disconnected) {
|
||||||
|
QString name;
|
||||||
|
networkWallet(name);
|
||||||
|
wallet = openWallet(name, 0, ::KWallet::Wallet::Asynchronous);
|
||||||
|
if (wallet) {
|
||||||
|
cState = connecting;
|
||||||
|
connect(wallet, SIGNAL(walletOpened(bool)), this, SLOT(onWalletOpened(bool)));
|
||||||
|
connect(wallet, SIGNAL(walletClosed()), this, SLOT(onWalletClosed()));
|
||||||
|
} else {
|
||||||
|
everError = true;
|
||||||
|
emit opened(false);
|
||||||
|
rejectPending();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::PSE::KWallet::ConnectionState Core::PSE::KWallet::connectionState()
|
||||||
|
{
|
||||||
|
return cState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::close()
|
||||||
|
{
|
||||||
|
if (sState == success) {
|
||||||
|
if (cState != disconnected) {
|
||||||
|
deleteWallet(wallet);
|
||||||
|
wallet = 0;
|
||||||
|
}
|
||||||
|
rejectPending();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::onWalletClosed()
|
||||||
|
{
|
||||||
|
cState = disconnected;
|
||||||
|
deleteWallet(wallet);
|
||||||
|
wallet = 0;
|
||||||
|
emit closed();
|
||||||
|
rejectPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::onWalletOpened(bool success)
|
||||||
|
{
|
||||||
|
emit opened(success);
|
||||||
|
if (success) {
|
||||||
|
QString appName = QCoreApplication::applicationName();
|
||||||
|
if (!hasFolder(wallet, appName)) {
|
||||||
|
createFolder(wallet, appName);
|
||||||
|
}
|
||||||
|
setFolder(wallet, appName);
|
||||||
|
cState = connected;
|
||||||
|
readPending();
|
||||||
|
} else {
|
||||||
|
everError = true;
|
||||||
|
cState = disconnected;
|
||||||
|
deleteWallet(wallet);
|
||||||
|
wallet = 0;
|
||||||
|
rejectPending();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::PSE::KWallet::SupportState Core::PSE::KWallet::supportState()
|
||||||
|
{
|
||||||
|
return sState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::PSE::KWallet::everHadError() const
|
||||||
|
{
|
||||||
|
return everError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::resetEverHadError()
|
||||||
|
{
|
||||||
|
everError = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::requestReadPassword(const QString& login, bool askAgain)
|
||||||
|
{
|
||||||
|
if (sState == success) {
|
||||||
|
readRequest.insert(login);
|
||||||
|
readSwitch(askAgain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::requestWritePassword(const QString& login, const QString& password, bool askAgain)
|
||||||
|
{
|
||||||
|
if (sState == success) {
|
||||||
|
std::map<QString, QString>::iterator itr = writeRequest.find(login);
|
||||||
|
if (itr == writeRequest.end()) {
|
||||||
|
writeRequest.insert(std::make_pair(login, password));
|
||||||
|
} else {
|
||||||
|
itr->second = password;
|
||||||
|
}
|
||||||
|
readSwitch(askAgain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::readSwitch(bool askAgain)
|
||||||
|
{
|
||||||
|
switch (cState) {
|
||||||
|
case connected:
|
||||||
|
readPending();
|
||||||
|
break;
|
||||||
|
case connecting:
|
||||||
|
break;
|
||||||
|
case disconnected:
|
||||||
|
if (!everError || askAgain) {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::rejectPending()
|
||||||
|
{
|
||||||
|
writeRequest.clear();
|
||||||
|
std::set<QString>::const_iterator i = readRequest.begin();
|
||||||
|
while (i != readRequest.end()) {
|
||||||
|
emit rejectPassword(*i);
|
||||||
|
readRequest.erase(i);
|
||||||
|
i = readRequest.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PSE::KWallet::readPending()
|
||||||
|
{
|
||||||
|
std::map<QString, QString>::const_iterator itr = writeRequest.begin();
|
||||||
|
while (itr != writeRequest.end()) {
|
||||||
|
int result = writePassword(wallet, itr->first, itr->second);
|
||||||
|
if (result == 0) {
|
||||||
|
qDebug() << "Successfully saved password for user" << itr->first;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Error writing password for user" << itr->first << ":" << result;
|
||||||
|
}
|
||||||
|
writeRequest.erase(itr);
|
||||||
|
itr = writeRequest.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<QString>::const_iterator i = readRequest.begin();
|
||||||
|
while (i != readRequest.end()) {
|
||||||
|
QString password;
|
||||||
|
int result = readPassword(wallet, *i, password);
|
||||||
|
if (result == 0 && password.size() > 0) { //even though it's written that the error is supposed to be returned in case there were no password
|
||||||
|
emit responsePassword(*i, password); //it doesn't do so. I assume empty password as a lack of password in KWallet
|
||||||
|
} else {
|
||||||
|
emit rejectPassword(*i);
|
||||||
|
}
|
||||||
|
readRequest.erase(i);
|
||||||
|
i = readRequest.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
112
core/passwordStorageEngines/kwallet.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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_PSE_KWALLET_H
|
||||||
|
#define CORE_PSE_KWALLET_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLibrary>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <KF5/KWallet/KWallet>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
namespace PSE {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class KWallet : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum SupportState {
|
||||||
|
initial,
|
||||||
|
success,
|
||||||
|
failure
|
||||||
|
};
|
||||||
|
enum ConnectionState {
|
||||||
|
disconnected,
|
||||||
|
connecting,
|
||||||
|
connected
|
||||||
|
};
|
||||||
|
|
||||||
|
KWallet();
|
||||||
|
~KWallet();
|
||||||
|
|
||||||
|
static SupportState supportState();
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
ConnectionState connectionState();
|
||||||
|
bool everHadError() const;
|
||||||
|
void resetEverHadError();
|
||||||
|
void requestReadPassword(const QString& login, bool askAgain = false);
|
||||||
|
void requestWritePassword(const QString& login, const QString& password, bool askAgain = false);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void opened(bool success);
|
||||||
|
void closed();
|
||||||
|
void responsePassword(const QString& login, const QString& password);
|
||||||
|
void rejectPassword(const QString& login);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onWalletOpened(bool success);
|
||||||
|
void onWalletClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readSwitch(bool askAgain);
|
||||||
|
void readPending();
|
||||||
|
void rejectPending();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef ::KWallet::Wallet* (*OpenWallet)(const QString &, WId, ::KWallet::Wallet::OpenType);
|
||||||
|
typedef void (*NetworkWallet)(QString&);
|
||||||
|
typedef void (*DeleteWallet)(::KWallet::Wallet*);
|
||||||
|
typedef int (*ReadPassword)(::KWallet::Wallet*, const QString&, QString&);
|
||||||
|
typedef int (*WritePassword)(::KWallet::Wallet*, const QString&, const QString&);
|
||||||
|
typedef bool (*HasFolder)(::KWallet::Wallet* w, const QString &f);
|
||||||
|
typedef bool (*CreateFolder)(::KWallet::Wallet* w, const QString &f);
|
||||||
|
typedef bool (*SetFolder)(::KWallet::Wallet* w, const QString &f);
|
||||||
|
|
||||||
|
static OpenWallet openWallet;
|
||||||
|
static NetworkWallet networkWallet;
|
||||||
|
static DeleteWallet deleteWallet;
|
||||||
|
static ReadPassword readPassword;
|
||||||
|
static WritePassword writePassword;
|
||||||
|
static HasFolder hasFolder;
|
||||||
|
static CreateFolder createFolder;
|
||||||
|
static SetFolder setFolder;
|
||||||
|
|
||||||
|
static SupportState sState;
|
||||||
|
static QLibrary lib;
|
||||||
|
|
||||||
|
ConnectionState cState;
|
||||||
|
bool everError;
|
||||||
|
::KWallet::Wallet* wallet;
|
||||||
|
|
||||||
|
std::set<QString> readRequest;
|
||||||
|
std::map<QString, QString> writeRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif // CORE_PSE_KWALLET_H
|
4
core/passwordStorageEngines/wrappers/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
add_library(kwalletWrapper SHARED kwallet.cpp)
|
||||||
|
target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet)
|
||||||
|
|
||||||
|
install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
51
core/passwordStorageEngines/wrappers/kwallet.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) {
|
||||||
|
return KWallet::Wallet::openWallet(name, w, ot);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void deleteWallet(KWallet::Wallet* w) {
|
||||||
|
w->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void networkWallet(QString& str) {
|
||||||
|
str = KWallet::Wallet::NetworkWallet();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int readPassword(KWallet::Wallet* w, const QString &key, QString &value) {
|
||||||
|
return w->readPassword(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int writePassword(KWallet::Wallet* w, const QString &key, const QString &value) {
|
||||||
|
return w->writePassword(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool hasFolder(KWallet::Wallet* w, const QString &f) {
|
||||||
|
return w->hasFolder(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool createFolder(KWallet::Wallet* w, const QString &f) {
|
||||||
|
return w->createFolder(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" bool setFolder(KWallet::Wallet* w, const QString &f) {
|
||||||
|
return w->setFolder(f);
|
||||||
|
}
|
@ -122,7 +122,26 @@ void Core::RosterItem::nextRequest()
|
|||||||
{
|
{
|
||||||
if (syncronizing) {
|
if (syncronizing) {
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
emit historyResponse(responseCache);
|
bool last = false;
|
||||||
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
|
try {
|
||||||
|
QString firstId = archive->oldestId();
|
||||||
|
if (responseCache.size() == 0) {
|
||||||
|
if (requestedBefore == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (responseCache.front().getId() == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const Archive::Empty& e) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
emit historyResponse(responseCache, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requestCache.size() > 0) {
|
if (requestCache.size() > 0) {
|
||||||
@ -156,8 +175,12 @@ 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("", msg.getId(), msg.getTime());
|
Shared::Message msg = archive->newest();
|
||||||
|
emit needHistory("", getId(msg), msg.getTime());
|
||||||
|
} catch (const Archive::Empty& 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:
|
||||||
@ -176,27 +199,37 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
} catch (const Archive::NotFound& e) {
|
} catch (const Archive::NotFound& e) {
|
||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
emit needHistory(archive->oldestId(), "");
|
emit needHistory(getId(archive->oldest()), "");
|
||||||
} catch (const Archive::Empty& e) {
|
} catch (const Archive::Empty& e) {
|
||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
emit needHistory(archive->oldestId(), "");
|
emit needHistory(getId(archive->oldest()), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
int rSize = responseCache.size();
|
int rSize = responseCache.size();
|
||||||
if (rSize < count) {
|
if (rSize < count) {
|
||||||
if (rSize != 0) {
|
if (rSize != 0) {
|
||||||
emit needHistory(responseCache.front().getId(), "");
|
emit needHistory(getId(responseCache.front()), "");
|
||||||
} else {
|
} else {
|
||||||
emit needHistory(before, "");
|
QString bf;
|
||||||
|
if (muc) {
|
||||||
|
bf = archive->stanzaIdById(before);
|
||||||
|
if (bf.size() < 0) {
|
||||||
|
qDebug() << "Didn't find stanzaId for id requesting history for" << jid << ", falling back to requesting by id";
|
||||||
|
bf = before;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf = before;
|
||||||
|
}
|
||||||
|
emit needHistory(bf, "");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nextRequest();
|
nextRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emit needHistory(archive->oldestId(), "");
|
emit needHistory(getId(archive->oldest()), "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case complete:
|
case complete:
|
||||||
@ -213,10 +246,20 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Core::RosterItem::getId(const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
QString id;
|
||||||
|
if (muc) {
|
||||||
|
id = msg.getStanzaId();
|
||||||
|
} else {
|
||||||
|
id = msg.getId();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
||||||
{
|
{
|
||||||
const QString& id = msg.getId();
|
if (msg.getId().size() > 0) {
|
||||||
if (id.size() > 0) {
|
|
||||||
if (msg.storable()) {
|
if (msg.storable()) {
|
||||||
switch (archiveState) {
|
switch (archiveState) {
|
||||||
case empty:
|
case empty:
|
||||||
@ -224,22 +267,26 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
|||||||
archiveState = end;
|
archiveState = end;
|
||||||
}
|
}
|
||||||
if (!syncronizing) {
|
if (!syncronizing) {
|
||||||
requestHistory(-1, id);
|
requestHistory(-1, getId(msg));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case beginning:
|
case beginning:
|
||||||
appendCache.push_back(msg);
|
if (!archive->hasElement(msg.getId())) {
|
||||||
if (!syncronizing) {
|
appendCache.push_back(msg);
|
||||||
requestHistory(-1, id);
|
if (!syncronizing) {
|
||||||
|
requestHistory(-1, getId(msg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case end:
|
case end:
|
||||||
archive->addElement(msg);
|
archive->addElement(msg);
|
||||||
break;
|
break;
|
||||||
case chunk:
|
case chunk:
|
||||||
appendCache.push_back(msg);
|
if (!archive->hasElement(msg.getId())) {
|
||||||
if (!syncronizing) {
|
appendCache.push_back(msg);
|
||||||
requestHistory(-1, id);
|
if (!syncronizing) {
|
||||||
|
requestHistory(-1, getId(msg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case complete:
|
case complete:
|
||||||
@ -247,7 +294,7 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (!syncronizing && archiveState == empty) {
|
} else if (!syncronizing && archiveState == empty) {
|
||||||
requestHistory(-1, id);
|
requestHistory(-1, getId(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,6 +383,11 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
archiveState = complete;
|
archiveState = complete;
|
||||||
archive->setFromTheBeginning(true);
|
archive->setFromTheBeginning(true);
|
||||||
}
|
}
|
||||||
|
if (added == 0 && wasEmpty) {
|
||||||
|
archiveState = empty;
|
||||||
|
nextRequest();
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
QString before;
|
QString before;
|
||||||
if (responseCache.size() > 0) {
|
if (responseCache.size() > 0) {
|
||||||
@ -354,7 +406,7 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
} catch (const Archive::Empty& e) {
|
} catch (const Archive::Empty& e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!found || requestedCount > responseCache.size()) {
|
if (!found || requestedCount > int(responseCache.size())) {
|
||||||
if (archiveState == complete) {
|
if (archiveState == complete) {
|
||||||
nextRequest();
|
nextRequest();
|
||||||
} else {
|
} else {
|
||||||
@ -377,26 +429,6 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::requestFromEmpty(int count, const QString& before)
|
|
||||||
{
|
|
||||||
if (syncronizing) {
|
|
||||||
qDebug("perform from empty didn't work, another request queued");
|
|
||||||
} else {
|
|
||||||
if (archiveState != empty) {
|
|
||||||
qDebug("perform from empty didn't work, the state is not empty");
|
|
||||||
requestHistory(count, before);
|
|
||||||
} else {
|
|
||||||
syncronizing = true;
|
|
||||||
requestedCount = count;
|
|
||||||
requestedBefore = "";
|
|
||||||
hisoryCache.clear();
|
|
||||||
responseCache.clear();
|
|
||||||
|
|
||||||
emit needHistory(before, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::RosterItem::getServer() const
|
QString Core::RosterItem::getServer() const
|
||||||
{
|
{
|
||||||
QStringList lst = jid.split("@");
|
QStringList lst = jid.split("@");
|
||||||
@ -410,28 +442,37 @@ bool Core::RosterItem::isMuc() const
|
|||||||
|
|
||||||
QString Core::RosterItem::avatarPath(const QString& resource) const
|
QString Core::RosterItem::avatarPath(const QString& resource) const
|
||||||
{
|
{
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
|
||||||
path += "/" + account + "/" + jid + "/" + (resource.size() == 0 ? jid : resource);
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::setAvatar(const QByteArray& data, const QString& resource)
|
QString Core::RosterItem::folderPath() const
|
||||||
{
|
{
|
||||||
bool result = archive->setAvatar(data, false, resource);
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
path += "/" + account + "/" + jid;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& 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 {
|
||||||
QMimeDatabase db;
|
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
|
||||||
QMimeType type = db.mimeTypeForData(data);
|
|
||||||
QString ext = type.preferredSuffix();
|
|
||||||
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + ext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
||||||
|
{
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
return setAutoGeneratedAvatar(info, 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);
|
||||||
@ -453,7 +494,7 @@ bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
|||||||
stream.open(QBuffer::WriteOnly);
|
stream.open(QBuffer::WriteOnly);
|
||||||
image.save(&stream, "PNG");
|
image.save(&stream, "PNG");
|
||||||
stream.close();
|
stream.close();
|
||||||
bool result = archive->setAvatar(arr, 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");
|
||||||
}
|
}
|
||||||
@ -468,6 +509,7 @@ bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString
|
|||||||
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
|
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
|
||||||
{
|
{
|
||||||
Archive::AvatarInfo info;
|
Archive::AvatarInfo info;
|
||||||
|
Archive::AvatarInfo newInfo;
|
||||||
bool hasAvatar = readAvatarInfo(info, resource);
|
bool hasAvatar = readAvatarInfo(info, resource);
|
||||||
|
|
||||||
QByteArray ava = card.photo();
|
QByteArray ava = card.photo();
|
||||||
@ -477,13 +519,10 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
QString path = "";
|
QString path = "";
|
||||||
|
|
||||||
if (ava.size() > 0) {
|
if (ava.size() > 0) {
|
||||||
bool changed = setAvatar(ava, resource);
|
bool changed = setAvatar(ava, newInfo, resource);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
type = Shared::Avatar::valid;
|
type = Shared::Avatar::valid;
|
||||||
QMimeDatabase db;
|
path = avatarPath(resource) + "." + newInfo.type;
|
||||||
QMimeType type = db.mimeTypeForData(ava);
|
|
||||||
QString ext = type.preferredSuffix();
|
|
||||||
path = avatarPath(resource) + "." + ext;
|
|
||||||
} else if (hasAvatar) {
|
} else if (hasAvatar) {
|
||||||
if (info.autogenerated) {
|
if (info.autogenerated) {
|
||||||
type = Shared::Avatar::autocreated;
|
type = Shared::Avatar::autocreated;
|
||||||
@ -501,7 +540,6 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
path = avatarPath(resource) + ".png";
|
path = avatarPath(resource) + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vCard.setAvatarType(type);
|
vCard.setAvatarType(type);
|
||||||
vCard.setAvatarPath(path);
|
vCard.setAvatarPath(path);
|
||||||
|
|
||||||
@ -512,3 +550,47 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||||||
return vCard;
|
return vCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::RosterItem::clearArchiveRequests()
|
||||||
|
{
|
||||||
|
syncronizing = false;
|
||||||
|
requestedCount = 0;
|
||||||
|
requestedBefore = "";
|
||||||
|
for (const std::pair<int, QString>& pair : requestCache) {
|
||||||
|
if (pair.first != -1) {
|
||||||
|
emit historyResponse(responseCache, false); //just to notify those who still waits with whatever happened to be left in caches yet
|
||||||
|
}
|
||||||
|
responseCache.clear();
|
||||||
|
}
|
||||||
|
hisoryCache.clear();
|
||||||
|
responseCache.clear(); //in case the cycle never runned
|
||||||
|
appendCache.clear();
|
||||||
|
requestCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::RosterItem::downgradeDatabaseState()
|
||||||
|
{
|
||||||
|
if (archiveState == ArchiveState::complete) {
|
||||||
|
archiveState = ArchiveState::beginning;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (archiveState == ArchiveState::end) {
|
||||||
|
archiveState = ArchiveState::chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message Core::RosterItem::getMessage(const QString& id)
|
||||||
|
{
|
||||||
|
for (const Shared::Message& msg : appendCache) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Shared::Message& msg : hisoryCache) {
|
||||||
|
if (msg.getId() == id) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return archive->getElement(id);
|
||||||
|
}
|
||||||
|
@ -31,8 +31,11 @@
|
|||||||
#include <QXmppVCardIq.h>
|
#include <QXmppVCardIq.h>
|
||||||
#include <QXmppPresence.h>
|
#include <QXmppPresence.h>
|
||||||
|
|
||||||
#include "../global.h"
|
#include "shared/enums.h"
|
||||||
#include "archive.h"
|
#include "shared/message.h"
|
||||||
|
#include "shared/vcard.h"
|
||||||
|
#include "storage/archive.h"
|
||||||
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -65,20 +68,23 @@ public:
|
|||||||
void appendMessageToArchive(const Shared::Message& msg);
|
void appendMessageToArchive(const Shared::Message& msg);
|
||||||
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
||||||
void requestHistory(int count, const QString& before);
|
void requestHistory(int count, const QString& before);
|
||||||
void requestFromEmpty(int count, const QString& before);
|
|
||||||
QString avatarPath(const QString& resource = "") const;
|
QString avatarPath(const QString& resource = "") const;
|
||||||
|
QString folderPath() const;
|
||||||
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
||||||
virtual bool setAvatar(const QByteArray& data, const QString& resource = "");
|
|
||||||
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
||||||
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
|
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
|
||||||
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
||||||
|
|
||||||
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
bool changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
void clearArchiveRequests();
|
||||||
|
void downgradeDatabaseState();
|
||||||
|
|
||||||
|
Shared::Message getMessage(const QString& id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nameChanged(const QString& name);
|
void nameChanged(const QString& name);
|
||||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
void subscriptionStateChanged(Shared::SubscriptionState state);
|
||||||
void historyResponse(const std::list<Shared::Message>& messages);
|
void historyResponse(const std::list<Shared::Message>& messages, bool last);
|
||||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||||
void avatarChanged(Shared::Avatar, const QString& path);
|
void avatarChanged(Shared::Avatar, const QString& path);
|
||||||
void requestVCard(const QString& jid);
|
void requestVCard(const QString& jid);
|
||||||
@ -87,6 +93,10 @@ public:
|
|||||||
const QString jid;
|
const QString jid;
|
||||||
const QString account;
|
const QString account;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
|
||||||
|
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString name;
|
QString name;
|
||||||
ArchiveState archiveState;
|
ArchiveState archiveState;
|
||||||
@ -105,6 +115,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void nextRequest();
|
void nextRequest();
|
||||||
void performRequest(int count, const QString& before);
|
void performRequest(int count, const QString& before);
|
||||||
|
QString getId(const Shared::Message& msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ void SignalCatcher::handleSigInt()
|
|||||||
{
|
{
|
||||||
snInt->setEnabled(false);
|
snInt->setEnabled(false);
|
||||||
char tmp;
|
char tmp;
|
||||||
::read(sigintFd[1], &tmp, sizeof(tmp));
|
ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp));
|
||||||
|
|
||||||
app->quit();
|
emit interrupt();
|
||||||
|
|
||||||
snInt->setEnabled(true);
|
snInt->setEnabled(true);
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ void SignalCatcher::handleSigInt()
|
|||||||
void SignalCatcher::intSignalHandler(int unused)
|
void SignalCatcher::intSignalHandler(int unused)
|
||||||
{
|
{
|
||||||
char a = 1;
|
char a = 1;
|
||||||
::write(sigintFd[0], &a, sizeof(a));
|
ssize_t s = ::write(sigintFd[0], &a, sizeof(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
int SignalCatcher::setup_unix_signal_handlers()
|
int SignalCatcher::setup_unix_signal_handlers()
|
@ -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();
|
||||||
|
|
42
core/signalcatcher_win32.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2021 Shunf4 <shun1048576@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "signalcatcher.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
|
||||||
|
QObject(parent),
|
||||||
|
app(p_app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalCatcher::~SignalCatcher()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void SignalCatcher::handleSigInt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalCatcher::intSignalHandler(int unused)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int SignalCatcher::setup_unix_signal_handlers()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
420
core/squawk.cpp
@ -26,13 +26,27 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
QObject(parent),
|
QObject(parent),
|
||||||
accounts(),
|
accounts(),
|
||||||
amap(),
|
amap(),
|
||||||
network()
|
state(Shared::Availability::offline),
|
||||||
|
network(),
|
||||||
|
isInitialized(false)
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
,kwallet()
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
|
connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress);
|
||||||
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
|
connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError);
|
||||||
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
|
connect(&network, &NetworkAccess::downloadFileComplete, this, &Squawk::fileDownloadComplete);
|
||||||
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
|
connect(&network, &NetworkAccess::uploadFileComplete, this, &Squawk::fileUploadComplete);
|
||||||
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
|
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
|
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
||||||
|
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
||||||
|
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword);
|
||||||
|
|
||||||
|
Shared::Global::setSupported("KWallet", true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::Squawk::~Squawk()
|
Core::Squawk::~Squawk()
|
||||||
@ -44,26 +58,52 @@ Core::Squawk::~Squawk()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onWalletOpened(bool 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;
|
|
||||||
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];
|
|
||||||
settings.setValue("name", acc->getName());
|
|
||||||
settings.setValue("server", acc->getServer());
|
|
||||||
settings.setValue("login", acc->getLogin());
|
|
||||||
settings.setValue("password", acc->getPassword());
|
|
||||||
settings.setValue("resource", acc->getResource());
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
|
|
||||||
settings.sync();
|
if (isInitialized) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("core");
|
||||||
|
settings.beginWriteArray("accounts");
|
||||||
|
SimpleCrypt crypto(passwordHash);
|
||||||
|
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
Account* acc = accounts[i];
|
||||||
|
|
||||||
|
Shared::AccountPassword ap = acc->getPasswordType();
|
||||||
|
QString password;
|
||||||
|
|
||||||
|
switch (ap) {
|
||||||
|
case Shared::AccountPassword::plain:
|
||||||
|
password = acc->getPassword();
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::jammed:
|
||||||
|
password = crypto.encryptToString(acc->getPassword());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setValue("name", acc->getName());
|
||||||
|
settings.setValue("server", acc->getServer());
|
||||||
|
settings.setValue("login", acc->getLogin());
|
||||||
|
settings.setValue("password", password);
|
||||||
|
settings.setValue("resource", acc->getResource());
|
||||||
|
settings.setValue("passwordType", static_cast<int>(ap));
|
||||||
|
settings.setValue("active", acc->getActive());
|
||||||
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
settings.sync();
|
||||||
|
}
|
||||||
|
|
||||||
emit quit();
|
emit quit();
|
||||||
}
|
}
|
||||||
@ -72,21 +112,8 @@ void Core::Squawk::start()
|
|||||||
{
|
{
|
||||||
qDebug("Starting squawk core..");
|
qDebug("Starting squawk core..");
|
||||||
|
|
||||||
QSettings settings;
|
readSettings();
|
||||||
settings.beginGroup("core");
|
isInitialized = true;
|
||||||
int size = settings.beginReadArray("accounts");
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
addAccount(
|
|
||||||
settings.value("login").toString(),
|
|
||||||
settings.value("server").toString(),
|
|
||||||
settings.value("password").toString(),
|
|
||||||
settings.value("name").toString(),
|
|
||||||
settings.value("resource").toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
network.start();
|
network.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,30 +124,44 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
|||||||
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);
|
addAccount(login, server, password, name, resource, active, Shared::Global::fromInt<Shared::AccountPassword>(passwordType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource)
|
void Core::Squawk::addAccount(
|
||||||
|
const QString& login,
|
||||||
|
const QString& server,
|
||||||
|
const QString& password,
|
||||||
|
const QString& name,
|
||||||
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
|
Shared::AccountPassword passwordType)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
if (amap.count(name) > 0) {
|
||||||
unsigned int reconnects = settings.value("reconnects", 2).toUInt();
|
qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring";
|
||||||
|
return;
|
||||||
Account* acc = new Account(login, server, password, name, &network);
|
}
|
||||||
|
Account* acc = new Account(login, server, password, name, active, &network);
|
||||||
acc->setResource(resource);
|
acc->setResource(resource);
|
||||||
acc->setReconnectTimes(reconnects);
|
acc->setPasswordType(passwordType);
|
||||||
accounts.push_back(acc);
|
accounts.push_back(acc);
|
||||||
amap.insert(std::make_pair(name, acc));
|
amap.insert(std::make_pair(name, acc));
|
||||||
|
|
||||||
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);
|
||||||
connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup);
|
connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup);
|
||||||
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact), this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
|
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact),
|
||||||
connect(acc, qOverload<const QString&>(&Account::removeContact), this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
|
this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
|
||||||
|
connect(acc, qOverload<const QString&>(&Account::removeContact),
|
||||||
|
this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
|
||||||
connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact);
|
connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact);
|
||||||
connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence);
|
connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence);
|
||||||
connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence);
|
connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence);
|
||||||
@ -138,7 +179,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
|
|||||||
|
|
||||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
||||||
|
|
||||||
connect(acc, &Account::uploadFileError, this, &Squawk::uploadFileError);
|
connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError);
|
||||||
|
|
||||||
QMap<QString, QVariant> map = {
|
QMap<QString, QVariant> map = {
|
||||||
{"login", login},
|
{"login", login},
|
||||||
@ -146,26 +187,48 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
|
|||||||
{"name", name},
|
{"name", name},
|
||||||
{"password", password},
|
{"password", password},
|
||||||
{"resource", resource},
|
{"resource", resource},
|
||||||
{"state", Shared::disconnected},
|
{"state", QVariant::fromValue(Shared::ConnectionState::disconnected)},
|
||||||
{"offline", Shared::offline},
|
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
||||||
{"error", ""},
|
{"error", ""},
|
||||||
{"avatarPath", acc->getAvatarPath()}
|
{"avatarPath", acc->getAvatarPath()},
|
||||||
|
{"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(int p_state)
|
void Core::Squawk::changeState(Shared::Availability p_state)
|
||||||
{
|
{
|
||||||
Shared::Availability avail;
|
if (state != p_state) {
|
||||||
if (p_state < Shared::availabilityLowest && p_state > Shared::availabilityHighest) {
|
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
||||||
qDebug("An attempt to set invalid availability to Squawk core, skipping");
|
Account* acc = *itr;
|
||||||
}
|
acc->setAvailability(p_state);
|
||||||
avail = static_cast<Shared::Availability>(p_state);
|
if (state == Shared::Availability::offline && acc->getActive()) {
|
||||||
state = avail;
|
acc->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = p_state;
|
||||||
|
|
||||||
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
emit stateChanged(p_state);
|
||||||
(*itr)->setAvailability(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +239,10 @@ void Core::Squawk::connectAccount(const QString& account)
|
|||||||
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)
|
||||||
@ -187,26 +253,25 @@ void Core::Squawk::disconnectAccount(const QString& account)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr->second->setActive(false);
|
||||||
itr->second->disconnect();
|
itr->second->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountConnectionStateChanged(int 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", state}});
|
emit changeAccount(acc->getName(), {
|
||||||
|
{"state", QVariant::fromValue(p_state)},
|
||||||
|
{"error", ""}
|
||||||
|
});
|
||||||
|
|
||||||
if (state == Shared::disconnected) {
|
#ifdef WITH_KWALLET
|
||||||
bool equals = true;
|
if (p_state == Shared::ConnectionState::connected) {
|
||||||
for (Accounts::const_iterator itr = accounts.begin(), end = accounts.end(); itr != end; itr++) {
|
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
|
||||||
if ((*itr)->getState() != Shared::disconnected) {
|
kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true);
|
||||||
equals = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (equals) {
|
|
||||||
state = Shared::offline;
|
|
||||||
emit stateChanged(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
@ -257,10 +322,10 @@ void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& na
|
|||||||
emit removePresence(acc->getName(), jid, name);
|
emit removePresence(acc->getName(), jid, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAvailabilityChanged(int 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", 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)
|
||||||
@ -279,22 +344,33 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
|
|||||||
{
|
{
|
||||||
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, skipping");
|
qDebug() << "An attempt to send a message with non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second->sendMessage(data);
|
itr->second->sendMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path)
|
void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data)
|
||||||
{
|
{
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
if (itr == amap.end()) {
|
if (itr == amap.end()) {
|
||||||
qDebug("An attempt to send a message with non existing account, skipping");
|
qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
itr->second->sendMessage(data, path);
|
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);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
@ -307,10 +383,10 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in
|
|||||||
itr->second->requestArchive(jid, count, before);
|
itr->second->requestArchive(jid, count, before);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list)
|
void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last)
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit responseArchive(acc->getName(), jid, list);
|
emit responseArchive(acc->getName(), jid, list, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
|
void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map)
|
||||||
@ -323,12 +399,46 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
|
|
||||||
Core::Account* acc = itr->second;
|
Core::Account* acc = itr->second;
|
||||||
Shared::ConnectionState st = acc->getState();
|
Shared::ConnectionState st = acc->getState();
|
||||||
|
QMap<QString, QVariant>::const_iterator mItr;
|
||||||
|
bool needToReconnect = false;
|
||||||
|
bool wentReconnecting = false;
|
||||||
|
|
||||||
if (st != Shared::disconnected) {
|
mItr = map.find("login");
|
||||||
acc->reconnect();
|
if (mItr != map.end()) {
|
||||||
|
needToReconnect = acc->getLogin() != mItr->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needToReconnect) {
|
||||||
|
mItr = map.find("password");
|
||||||
|
if (mItr != map.end()) {
|
||||||
|
needToReconnect = acc->getPassword() != mItr->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!needToReconnect) {
|
||||||
|
mItr = map.find("server");
|
||||||
|
if (mItr != map.end()) {
|
||||||
|
needToReconnect = acc->getServer() != mItr->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!needToReconnect) {
|
||||||
|
mItr = map.find("resource");
|
||||||
|
if (mItr != map.end()) {
|
||||||
|
needToReconnect = acc->getResource() != mItr->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool activeChanged = false;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QVariant>::const_iterator mItr;
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end()) {
|
||||||
acc->setLogin(mItr->toString());
|
acc->setLogin(mItr->toString());
|
||||||
@ -349,6 +459,28 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
acc->setServer(mItr->toString());
|
acc->setServer(mItr->toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mItr = map.find("passwordType");
|
||||||
|
if (mItr != map.end()) {
|
||||||
|
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
if (acc->getPasswordType() == Shared::AccountPassword::kwallet
|
||||||
|
&& kwallet.supportState() == PSE::KWallet::success
|
||||||
|
&& !needToReconnect
|
||||||
|
) {
|
||||||
|
kwallet.requestWritePassword(acc->getName(), acc->getPassword(), true);
|
||||||
|
}
|
||||||
|
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,6 +488,10 @@ 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)
|
||||||
@ -367,7 +503,7 @@ void Core::Squawk::removeAccountRequest(const QString& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Account* acc = itr->second;
|
Account* acc = itr->second;
|
||||||
if (acc->getState() != Shared::disconnected) {
|
if (acc->getState() != Shared::ConnectionState::disconnected) {
|
||||||
acc->disconnect();
|
acc->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,8 +625,6 @@ void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString
|
|||||||
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());
|
||||||
@ -517,14 +651,9 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co
|
|||||||
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
itr->second->addRoomRequest(jid, nick, password, autoJoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::fileLocalPathRequest(const QString& messageId, const QString& url)
|
void Core::Squawk::fileDownloadRequest(const QString& url)
|
||||||
{
|
{
|
||||||
network.fileLocalPathRequest(messageId, url);
|
network.downladFile(url);
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::downloadFileRequest(const QString& messageId, const QString& url)
|
|
||||||
{
|
|
||||||
network.downladFileRequest(messageId, url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||||
@ -577,3 +706,110 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card
|
|||||||
itr->second->uploadVCard(card);
|
itr->second->uploadVCard(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 password = settings.value("password", "").toString();
|
||||||
|
if (passwordType == Shared::AccountPassword::jammed) {
|
||||||
|
SimpleCrypt crypto(passwordHash);
|
||||||
|
password = crypto.decryptToString(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccount(
|
||||||
|
settings.value("login").toString(),
|
||||||
|
settings.value("server").toString(),
|
||||||
|
password,
|
||||||
|
settings.value("name").toString(),
|
||||||
|
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);
|
||||||
|
if (itr == amap.end()) {
|
||||||
|
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Account* acc = itr->second;
|
||||||
|
acc->setPassword(password);
|
||||||
|
emit changeAccount(account, {{"password", password}});
|
||||||
|
if (state != Shared::Availability::offline && acc->getActive()) {
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
||||||
|
{
|
||||||
|
Account* acc = static_cast<Account*>(sender());
|
||||||
|
emit fileError({{acc->getName(), jid, id}}, errorText, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onLocalPathInvalid(const QString& path)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list = network.reportPathInvalid(path);
|
||||||
|
|
||||||
|
QMap<QString, QVariant> data({
|
||||||
|
{"attachPath", ""}
|
||||||
|
});
|
||||||
|
for (const Shared::MessageInfo& info : list) {
|
||||||
|
AccountsMap::const_iterator itr = amap.find(info.account);
|
||||||
|
if (itr != amap.end()) {
|
||||||
|
itr->second->requestChangeMessage(info.jid, info.messageId, data);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Reacting on failure to reach file" << path << "there was an attempt to change message in account" << info.account << "which doesn't exist, skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::changeDownloadsPath(const QString& path)
|
||||||
|
{
|
||||||
|
network.moveFilesDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,15 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "../global.h"
|
#include "shared/enums.h"
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/global.h"
|
||||||
#include "networkaccess.h"
|
#include "networkaccess.h"
|
||||||
|
#include "external/simpleCrypt/simplecrypt.h"
|
||||||
|
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
#include "passwordStorageEngines/kwallet.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
@ -43,46 +50,61 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void quit();
|
void quit();
|
||||||
|
void ready();
|
||||||
|
|
||||||
void newAccount(const QMap<QString, QVariant>&);
|
void newAccount(const QMap<QString, QVariant>&);
|
||||||
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
|
|
||||||
void addGroup(const QString& account, const QString& name);
|
void addGroup(const QString& account, const QString& name);
|
||||||
void removeGroup(const QString& account, const QString& name);
|
void removeGroup(const QString& account, const QString& name);
|
||||||
|
|
||||||
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
void removeContact(const QString& account, const QString& jid);
|
void removeContact(const QString& account, const QString& jid);
|
||||||
void removeContact(const QString& account, const QString& jid, const QString& group);
|
void removeContact(const QString& account, const QString& jid, const QString& group);
|
||||||
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removePresence(const QString& account, const QString& jid, const QString& name);
|
void removePresence(const QString& account, const QString& jid, const QString& name);
|
||||||
void stateChanged(int state);
|
|
||||||
|
void stateChanged(Shared::Availability state);
|
||||||
|
|
||||||
void accountMessage(const QString& account, const Shared::Message& data);
|
void accountMessage(const QString& account, const Shared::Message& data);
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list);
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void removeRoom(const QString& account, const QString jid);
|
void removeRoom(const QString& account, const QString jid);
|
||||||
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
||||||
void fileLocalPathResponse(const QString& messageId, const QString& path);
|
|
||||||
void downloadFileError(const QString& messageId, const QString& error);
|
void fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
||||||
void downloadFileProgress(const QString& messageId, qreal value);
|
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
||||||
void uploadFileError(const QString& messageId, const QString& error);
|
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
||||||
void uploadFileProgress(const QString& messageId, qreal value);
|
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
void requestPassword(const QString& account, bool authernticationError);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void newAccountRequest(const QMap<QString, QVariant>& map);
|
void newAccountRequest(const QMap<QString, QVariant>& map);
|
||||||
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
void modifyAccountRequest(const QString& name, const QMap<QString, QVariant>& map);
|
||||||
void removeAccountRequest(const QString& name);
|
void removeAccountRequest(const QString& name);
|
||||||
void connectAccount(const QString& account);
|
void connectAccount(const QString& account);
|
||||||
void disconnectAccount(const QString& account);
|
void disconnectAccount(const QString& account);
|
||||||
void changeState(int state);
|
|
||||||
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
void sendMessage(const QString& account, const Shared::Message& data);
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
void sendMessage(const QString& account, const Shared::Message& data, const QString& path);
|
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
||||||
|
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);
|
||||||
|
|
||||||
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
@ -90,14 +112,19 @@ public slots:
|
|||||||
void removeContactRequest(const QString& account, const QString& jid);
|
void removeContactRequest(const QString& account, const QString& jid);
|
||||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||||
|
|
||||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void removeRoomRequest(const QString& account, const QString& jid);
|
void removeRoomRequest(const QString& account, const QString& jid);
|
||||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
|
||||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
|
|
||||||
void requestVCard(const QString& account, const QString& jid);
|
void requestVCard(const QString& account, const QString& jid);
|
||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||||
|
void responsePassword(const QString& account, const QString& password);
|
||||||
|
void onLocalPathInvalid(const QString& path);
|
||||||
|
void changeDownloadsPath(const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::deque<Account*> Accounts;
|
typedef std::deque<Account*> Accounts;
|
||||||
@ -107,13 +134,25 @@ private:
|
|||||||
AccountsMap amap;
|
AccountsMap amap;
|
||||||
Shared::Availability state;
|
Shared::Availability state;
|
||||||
NetworkAccess network;
|
NetworkAccess network;
|
||||||
|
bool isInitialized;
|
||||||
|
|
||||||
private:
|
#ifdef WITH_KWALLET
|
||||||
void addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource);
|
PSE::KWallet kwallet;
|
||||||
|
#endif
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccountConnectionStateChanged(int state);
|
void addAccount(
|
||||||
void onAccountAvailabilityChanged(int state);
|
const QString& login,
|
||||||
|
const QString& server,
|
||||||
|
const QString& password,
|
||||||
|
const QString& name,
|
||||||
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
|
Shared::AccountPassword passwordType
|
||||||
|
);
|
||||||
|
|
||||||
|
void onAccountConnectionStateChanged(Shared::ConnectionState state);
|
||||||
|
void onAccountAvailabilityChanged(Shared::Availability state);
|
||||||
void onAccountChanged(const QMap<QString, QVariant>& data);
|
void onAccountChanged(const QMap<QString, QVariant>& data);
|
||||||
void onAccountAddGroup(const QString& name);
|
void onAccountAddGroup(const QString& name);
|
||||||
void onAccountError(const QString& text);
|
void onAccountError(const QString& text);
|
||||||
@ -125,7 +164,7 @@ private slots:
|
|||||||
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void onAccountAddPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemovePresence(const QString& jid, const QString& name);
|
void onAccountRemovePresence(const QString& jid, const QString& name);
|
||||||
void onAccountMessage(const Shared::Message& data);
|
void onAccountMessage(const Shared::Message& data);
|
||||||
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list);
|
void onAccountResponseArchive(const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountAddRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoom(const QString jid, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemoveRoom(const QString jid);
|
void onAccountRemoveRoom(const QString jid);
|
||||||
@ -133,6 +172,26 @@ 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 onWalletOpened(bool success);
|
||||||
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readSettings();
|
||||||
|
void parseAccount(
|
||||||
|
const QString& login,
|
||||||
|
const QString& server,
|
||||||
|
const QString& password,
|
||||||
|
const QString& name,
|
||||||
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
|
Shared::AccountPassword passwordType
|
||||||
|
);
|
||||||
|
|
||||||
|
static const quint64 passwordHash = 0x08d054225ac4871d;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
8
core/storage/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
archive.cpp
|
||||||
|
archive.h
|
||||||
|
storage.cpp
|
||||||
|
storage.h
|
||||||
|
urlstorage.cpp
|
||||||
|
urlstorage.h
|
||||||
|
)
|
@ -58,15 +58,24 @@ void Core::Archive::open(const QString& account)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 5);
|
mdb_env_set_maxdbs(environment, 5);
|
||||||
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
mdb_env_set_mapsize(environment,
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// On Windows, the file is immediately allocated.
|
||||||
|
// So we have to limit the size.
|
||||||
|
80UL * 1024UL * 1024UL
|
||||||
|
#else
|
||||||
|
512UL * 1024UL * 1024UL
|
||||||
|
#endif
|
||||||
|
);
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
||||||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY | MDB_INTEGERDUP | MDB_DUPSORT, &order);
|
||||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
||||||
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
||||||
|
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
|
|
||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
@ -99,6 +108,7 @@ void Core::Archive::open(const QString& account)
|
|||||||
void Core::Archive::close()
|
void Core::Archive::close()
|
||||||
{
|
{
|
||||||
if (opened) {
|
if (opened) {
|
||||||
|
mdb_dbi_close(environment, sid);
|
||||||
mdb_dbi_close(environment, avatars);
|
mdb_dbi_close(environment, avatars);
|
||||||
mdb_dbi_close(environment, stats);
|
mdb_dbi_close(environment, stats);
|
||||||
mdb_dbi_close(environment, order);
|
mdb_dbi_close(environment, order);
|
||||||
@ -113,6 +123,7 @@ bool Core::Archive::addElement(const Shared::Message& message)
|
|||||||
if (!opened) {
|
if (!opened) {
|
||||||
throw Closed("addElement", jid.toStdString());
|
throw Closed("addElement", jid.toStdString());
|
||||||
}
|
}
|
||||||
|
qDebug() << "Adding message with id " << message.getId();
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||||
message.serialize(ds);
|
message.serialize(ds);
|
||||||
@ -139,12 +150,36 @@ bool Core::Archive::addElement(const Shared::Message& message)
|
|||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
rc = mdb_txn_commit(txn);
|
if (message.getStanzaId().size() > 0) {
|
||||||
if (rc) {
|
const std::string& szid = message.getStanzaId().toStdString();
|
||||||
qDebug() << "A transaction error: " << mdb_strerror(rc);
|
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc);
|
qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc);
|
||||||
@ -164,10 +199,12 @@ void Core::Archive::clear()
|
|||||||
mdb_drop(txn, main, 0);
|
mdb_drop(txn, main, 0);
|
||||||
mdb_drop(txn, order, 0);
|
mdb_drop(txn, order, 0);
|
||||||
mdb_drop(txn, stats, 0);
|
mdb_drop(txn, stats, 0);
|
||||||
|
mdb_drop(txn, avatars, 0);
|
||||||
|
mdb_drop(txn, sid, 0);
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message Core::Archive::getElement(const QString& id)
|
Shared::Message Core::Archive::getElement(const QString& id) const
|
||||||
{
|
{
|
||||||
if (!opened) {
|
if (!opened) {
|
||||||
throw Closed("getElement", jid.toStdString());
|
throw Closed("getElement", jid.toStdString());
|
||||||
@ -186,7 +223,27 @@ Shared::Message Core::Archive::getElement(const QString& id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn)
|
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;
|
MDB_val lmdbKey, lmdbData;
|
||||||
lmdbKey.mv_size = id.size();
|
lmdbKey.mv_size = id.size();
|
||||||
@ -220,8 +277,11 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
std::string strId(id.toStdString());
|
std::string strId(id.toStdString());
|
||||||
try {
|
try {
|
||||||
Shared::Message msg = getMessage(strId, txn);
|
Shared::Message msg = getMessage(strId, txn);
|
||||||
|
bool hadStanzaId = msg.getStanzaId().size() > 0;
|
||||||
QDateTime oTime = msg.getTime();
|
QDateTime oTime = msg.getTime();
|
||||||
bool idChange = msg.change(data);
|
bool idChange = msg.change(data);
|
||||||
|
QDateTime nTime = msg.getTime();
|
||||||
|
bool orderChange = oTime != nTime;
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
MDB_val lmdbKey, lmdbData;
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
@ -231,15 +291,21 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
int rc;
|
int rc;
|
||||||
if (idChange) {
|
if (idChange || orderChange) {
|
||||||
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
|
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) {
|
if (rc == 0) {
|
||||||
strId = msg.getId().toStdString();
|
strId = msg.getId().toStdString();
|
||||||
lmdbKey.mv_size = strId.size();
|
lmdbKey.mv_size = strId.size();
|
||||||
lmdbKey.mv_data = (char*)strId.c_str();
|
lmdbKey.mv_data = (char*)strId.c_str();
|
||||||
|
|
||||||
|
quint64 stamp = nTime.toMSecsSinceEpoch();
|
||||||
quint64 stamp = oTime.toMSecsSinceEpoch();
|
|
||||||
lmdbData.mv_data = (quint8*)&stamp;
|
lmdbData.mv_data = (quint8*)&stamp;
|
||||||
lmdbData.mv_size = 8;
|
lmdbData.mv_size = 8;
|
||||||
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
|
||||||
@ -250,6 +316,20 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
|
|||||||
throw Unknown(jid.toStdString(), mdb_strerror(rc));
|
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_size = ba.size();
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||||
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0);
|
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0);
|
||||||
@ -395,7 +475,20 @@ unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messag
|
|||||||
if (rc) {
|
if (rc) {
|
||||||
qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc);
|
qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc);
|
||||||
} else {
|
} else {
|
||||||
//qDebug() << "element added with id" << message.getId() << "stamp" << message.getTime();
|
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++;
|
success++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -427,8 +520,9 @@ long unsigned int Core::Archive::size() const
|
|||||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
mdb_stat(txn, order, &stat);
|
mdb_stat(txn, order, &stat);
|
||||||
|
size_t amount = stat.ms_entries;
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return stat.ms_entries;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id)
|
||||||
@ -528,14 +622,54 @@ void Core::Archive::setFromTheBeginning(bool is)
|
|||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
bool success = setStatValue("beginning", is, txn);
|
bool success = setStatValue("beginning", is, txn);
|
||||||
if (success != 0) {
|
if (success) {
|
||||||
mdb_txn_abort(txn);
|
|
||||||
} else {
|
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
|
} else {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
void Core::Archive::printOrder()
|
||||||
{
|
{
|
||||||
qDebug() << "Printing order";
|
qDebug() << "Printing order";
|
||||||
@ -675,7 +809,7 @@ bool Core::Archive::dropAvatar(const std::string& resource)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QString& resource)
|
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource)
|
||||||
{
|
{
|
||||||
if (!opened) {
|
if (!opened) {
|
||||||
throw Closed("setAvatar", jid.toStdString());
|
throw Closed("setAvatar", jid.toStdString());
|
||||||
@ -726,7 +860,9 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QStr
|
|||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
MDB_val lmdbKey, lmdbData;
|
||||||
QByteArray value;
|
QByteArray value;
|
||||||
AvatarInfo newInfo(ext, newHash, generated);
|
newInfo.type = ext;
|
||||||
|
newInfo.hash = newHash;
|
||||||
|
newInfo.autogenerated = generated;
|
||||||
newInfo.serialize(&value);
|
newInfo.serialize(&value);
|
||||||
lmdbKey.mv_size = res.size();
|
lmdbKey.mv_size = res.size();
|
||||||
lmdbKey.mv_data = (char*)res.c_str();
|
lmdbKey.mv_data = (char*)res.c_str();
|
||||||
@ -802,6 +938,34 @@ bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const
|
||||||
{
|
{
|
||||||
if (!opened) {
|
if (!opened) {
|
@ -24,8 +24,8 @@
|
|||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QMimeType>
|
#include <QMimeType>
|
||||||
|
|
||||||
#include "global.h"
|
#include "shared/message.h"
|
||||||
#include "exception.h"
|
#include "shared/exception.h"
|
||||||
#include <lmdb.h>
|
#include <lmdb.h>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
@ -45,7 +45,8 @@ public:
|
|||||||
|
|
||||||
bool addElement(const Shared::Message& message);
|
bool addElement(const Shared::Message& message);
|
||||||
unsigned int addElements(const std::list<Shared::Message>& messages);
|
unsigned int addElements(const std::list<Shared::Message>& messages);
|
||||||
Shared::Message getElement(const QString& id);
|
Shared::Message getElement(const QString& id) const;
|
||||||
|
bool hasElement(const QString& id) const;
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
Shared::Message oldest();
|
Shared::Message oldest();
|
||||||
QString oldestId();
|
QString oldestId();
|
||||||
@ -56,9 +57,12 @@ public:
|
|||||||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||||
bool isFromTheBeginning();
|
bool isFromTheBeginning();
|
||||||
void setFromTheBeginning(bool is);
|
void setFromTheBeginning(bool is);
|
||||||
bool setAvatar(const QByteArray& data, bool generated = false, const QString& resource = "");
|
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
|
||||||
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
||||||
bool readAvatarInfo(AvatarInfo& target, 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:
|
public:
|
||||||
const QString jid;
|
const QString jid;
|
||||||
@ -168,10 +172,11 @@ private:
|
|||||||
bool opened;
|
bool opened;
|
||||||
bool fromTheBeginning;
|
bool fromTheBeginning;
|
||||||
MDB_env* environment;
|
MDB_env* environment;
|
||||||
MDB_dbi main;
|
MDB_dbi main; //id to message
|
||||||
MDB_dbi order;
|
MDB_dbi order; //time to id
|
||||||
MDB_dbi stats;
|
MDB_dbi stats;
|
||||||
MDB_dbi avatars;
|
MDB_dbi avatars;
|
||||||
|
MDB_dbi sid; //stanzaId to id
|
||||||
|
|
||||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
||||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
||||||
@ -182,7 +187,7 @@ private:
|
|||||||
void printOrder();
|
void printOrder();
|
||||||
void printKeys();
|
void printKeys();
|
||||||
bool dropAvatar(const std::string& resource);
|
bool dropAvatar(const std::string& resource);
|
||||||
Shared::Message getMessage(const std::string& id, MDB_txn* txn);
|
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 getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc);
|
||||||
Shared::Message edge(bool end);
|
Shared::Message edge(bool end);
|
||||||
};
|
};
|
491
core/storage/urlstorage.cpp
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "urlstorage.h"
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlStorage(const QString& p_name):
|
||||||
|
name(p_name),
|
||||||
|
opened(false),
|
||||||
|
environment(),
|
||||||
|
base(),
|
||||||
|
map()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::UrlStorage::~UrlStorage()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::open()
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
mdb_env_create(&environment);
|
||||||
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
path += "/" + name;
|
||||||
|
QDir cache(path);
|
||||||
|
|
||||||
|
if (!cache.exists()) {
|
||||||
|
bool res = cache.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
throw Archive::Directory(path.toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mdb_env_set_maxdbs(environment, 2);
|
||||||
|
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
|
||||||
|
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
|
||||||
|
mdb_dbi_open(txn, "map", MDB_CREATE, &map);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
opened = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::close()
|
||||||
|
{
|
||||||
|
if (opened) {
|
||||||
|
mdb_dbi_close(environment, map);
|
||||||
|
mdb_dbi_close(environment, base);
|
||||||
|
mdb_env_close(environment);
|
||||||
|
opened = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite)
|
||||||
|
{
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
writeInfo(key, info, txn, overwrite);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite)
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||||
|
info.serialize(ds);
|
||||||
|
|
||||||
|
const std::string& id = key.toStdString();
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey.mv_size = id.size();
|
||||||
|
lmdbKey.mv_data = (char*)id.c_str();
|
||||||
|
lmdbData.mv_size = ba.size();
|
||||||
|
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
if (rc == MDB_KEYEXIST) {
|
||||||
|
if (!overwrite) {
|
||||||
|
throw Archive::Exist(name.toStdString(), id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.hasPath()) {
|
||||||
|
std::string sp = info.getPath().toStdString();
|
||||||
|
lmdbData.mv_size = sp.size();
|
||||||
|
lmdbData.mv_data = (char*)sp.c_str();
|
||||||
|
rc = mdb_put(txn, map, &lmdbData, &lmdbKey, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info, MDB_txn* txn)
|
||||||
|
{
|
||||||
|
const std::string& id = key.toStdString();
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey.mv_size = id.size();
|
||||||
|
lmdbKey.mv_data = (char*)id.c_str();
|
||||||
|
int rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||||
|
QDataStream ds(&ba, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
info.deserialize(ds);
|
||||||
|
} else if (rc == MDB_NOTFOUND) {
|
||||||
|
throw Archive::NotFound(id, name.toStdString());
|
||||||
|
} else {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info)
|
||||||
|
{
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
readInfo(key, info, txn);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addFile(no message, no path)", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
addToInfo(url, "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addFile(no message, with path)", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
addToInfo(url, "", "", "", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addFile(with message, no path)", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
addToInfo(url, account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addFile(with message, with path)", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
addToInfo(url, account, jid, id, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addFile(with list)", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlInfo info (path, msgs);
|
||||||
|
writeInfo(url, info, true);;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Archive::Closed("addMessageAndCheckForPath", name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return addToInfo(url, account, jid, id).getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path)
|
||||||
|
{
|
||||||
|
UrlInfo info;
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
readInfo(url, info, txn);
|
||||||
|
} catch (const Archive::NotFound& e) {
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pathChange = false;
|
||||||
|
bool listChange = false;
|
||||||
|
if (path != "-s") {
|
||||||
|
if (info.getPath() != path) {
|
||||||
|
info.setPath(path);
|
||||||
|
pathChange = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.size() > 0 && jid.size() > 0 && id.size() > 0) {
|
||||||
|
listChange = info.addMessage(account, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathChange || listChange) {
|
||||||
|
try {
|
||||||
|
writeInfo(url, info, txn, true);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
UrlInfo info;
|
||||||
|
|
||||||
|
try {
|
||||||
|
readInfo(url, info, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
} catch (const Archive::NotFound& e) {
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.setPath(path);
|
||||||
|
try {
|
||||||
|
writeInfo(url, info, txn, true);
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
UrlInfo info;
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string id = url.toStdString();
|
||||||
|
readInfo(url, info, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
|
||||||
|
MDB_val lmdbKey;
|
||||||
|
lmdbKey.mv_size = id.size();
|
||||||
|
lmdbKey.mv_data = (char*)id.c_str();
|
||||||
|
int rc = mdb_del(txn, base, &lmdbKey, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.hasPath()) {
|
||||||
|
std::string path = info.getPath().toStdString();
|
||||||
|
lmdbKey.mv_size = path.size();
|
||||||
|
lmdbKey.mv_data = (char*)path.c_str();
|
||||||
|
|
||||||
|
int rc = mdb_del(txn, map, &lmdbKey, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string spath = path.toStdString();
|
||||||
|
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey.mv_size = spath.size();
|
||||||
|
lmdbKey.mv_data = (char*)spath.c_str();
|
||||||
|
|
||||||
|
QString url;
|
||||||
|
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||||
|
url = QString(surl.c_str());
|
||||||
|
} else if (rc == MDB_NOTFOUND) {
|
||||||
|
qDebug() << "Have been asked to remove file" << path << ", which isn't in the database, skipping";
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return list;
|
||||||
|
} else {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlInfo info;
|
||||||
|
std::string id = url.toStdString();
|
||||||
|
readInfo(url, info, txn);
|
||||||
|
info.getMessages(list);
|
||||||
|
info.setPath(QString());
|
||||||
|
writeInfo(url, info, txn, true);
|
||||||
|
|
||||||
|
rc = mdb_del(txn, map, &lmdbKey, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
} catch (...) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString Core::UrlStorage::getUrl(const QString& path)
|
||||||
|
{
|
||||||
|
std::list<Shared::MessageInfo> list;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
|
|
||||||
|
std::string spath = path.toStdString();
|
||||||
|
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey.mv_size = spath.size();
|
||||||
|
lmdbKey.mv_data = (char*)spath.c_str();
|
||||||
|
|
||||||
|
QString url;
|
||||||
|
int rc = mdb_get(txn, map, &lmdbKey, &lmdbData);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||||
|
url = QString(surl.c_str());
|
||||||
|
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return url;
|
||||||
|
} else if (rc == MDB_NOTFOUND) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw Archive::NotFound(spath, name.toStdString());
|
||||||
|
} else {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<QString, std::list<Shared::MessageInfo>> Core::UrlStorage::getPath(const QString& url)
|
||||||
|
{
|
||||||
|
UrlInfo info;
|
||||||
|
readInfo(url, info);
|
||||||
|
std::list<Shared::MessageInfo> container;
|
||||||
|
info.getMessages(container);
|
||||||
|
return std::make_pair(info.getPath(), container);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo():
|
||||||
|
localPath(),
|
||||||
|
messages() {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path):
|
||||||
|
localPath(path),
|
||||||
|
messages() {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs):
|
||||||
|
localPath(path),
|
||||||
|
messages(msgs) {}
|
||||||
|
|
||||||
|
Core::UrlStorage::UrlInfo::~UrlInfo() {}
|
||||||
|
|
||||||
|
bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id)
|
||||||
|
{
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
if (info.account == acc && info.jid == jid && info.messageId == id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messages.emplace_back(acc, jid, id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const
|
||||||
|
{
|
||||||
|
data << localPath;
|
||||||
|
std::list<Shared::MessageInfo>::size_type size = messages.size();
|
||||||
|
data << quint32(size);
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
data << info.account;
|
||||||
|
data << info.jid;
|
||||||
|
data << info.messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data)
|
||||||
|
{
|
||||||
|
data >> localPath;
|
||||||
|
quint32 size;
|
||||||
|
data >> size;
|
||||||
|
for (quint32 i = 0; i < size; ++i) {
|
||||||
|
messages.emplace_back();
|
||||||
|
Shared::MessageInfo& info = messages.back();
|
||||||
|
data >> info.account;
|
||||||
|
data >> info.jid;
|
||||||
|
data >> info.messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::getMessages(std::list<Shared::MessageInfo>& container) const
|
||||||
|
{
|
||||||
|
for (const Shared::MessageInfo& info : messages) {
|
||||||
|
container.emplace_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::UrlStorage::UrlInfo::getPath() const
|
||||||
|
{
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::UrlStorage::UrlInfo::hasPath() const
|
||||||
|
{
|
||||||
|
return localPath.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Core::UrlStorage::UrlInfo::setPath(const QString& path)
|
||||||
|
{
|
||||||
|
localPath = path;
|
||||||
|
}
|
99
core/storage/urlstorage.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CORE_URLSTORAGE_H
|
||||||
|
#define CORE_URLSTORAGE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <lmdb.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include "archive.h"
|
||||||
|
#include <shared/messageinfo.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class UrlStorage
|
||||||
|
{
|
||||||
|
class UrlInfo;
|
||||||
|
public:
|
||||||
|
UrlStorage(const QString& name);
|
||||||
|
~UrlStorage();
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void addFile(const QString& url);
|
||||||
|
void addFile(const QString& url, const QString& path);
|
||||||
|
void addFile(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id);
|
||||||
|
void addFile(const std::list<Shared::MessageInfo>& msgs, const QString& url, const QString& path); //this one overwrites all that was
|
||||||
|
std::list<Shared::MessageInfo> removeFile(const QString& url); //removes entry like it never was in the database, returns affected message infos
|
||||||
|
std::list<Shared::MessageInfo> deletedFile(const QString& path); //empties the localPath of the entry, returns affected message infos
|
||||||
|
std::list<Shared::MessageInfo> setPath(const QString& url, const QString& path);
|
||||||
|
QString getUrl(const QString& path);
|
||||||
|
QString addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id);
|
||||||
|
std::pair<QString, std::list<Shared::MessageInfo>> getPath(const QString& url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString name;
|
||||||
|
bool opened;
|
||||||
|
MDB_env* environment;
|
||||||
|
MDB_dbi base;
|
||||||
|
MDB_dbi map;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
|
||||||
|
void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false);
|
||||||
|
void readInfo(const QString& key, UrlInfo& info);
|
||||||
|
void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn);
|
||||||
|
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
|
||||||
|
|
||||||
|
private:
|
||||||
|
class UrlInfo {
|
||||||
|
public:
|
||||||
|
UrlInfo(const QString& path);
|
||||||
|
UrlInfo(const QString& path, const std::list<Shared::MessageInfo>& msgs);
|
||||||
|
UrlInfo();
|
||||||
|
~UrlInfo();
|
||||||
|
|
||||||
|
void serialize(QDataStream& data) const;
|
||||||
|
void deserialize(QDataStream& data);
|
||||||
|
|
||||||
|
QString getPath() const;
|
||||||
|
bool hasPath() const;
|
||||||
|
void setPath(const QString& path);
|
||||||
|
|
||||||
|
bool addMessage(const QString& acc, const QString& jid, const QString& id);
|
||||||
|
void getMessages(std::list<Shared::MessageInfo>& container) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString localPath;
|
||||||
|
std::list<Shared::MessageInfo> messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_URLSTORAGE_H
|
2
external/qxmpp
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 067c7743b1c72d055a749a7611efd2f9026fe784
|
Subproject commit fe83e9c3d42c3becf682e2b5ecfc9d77b24c614f
|
10
external/simpleCrypt/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(simplecrypt LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
|
||||||
|
add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h)
|
||||||
|
|
||||||
|
target_link_libraries(simpleCrypt Qt5::Core)
|
248
external/simpleCrypt/simplecrypt.cpp
vendored
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011, Andre Somers
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Rathenau Instituut, Andre Somers nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include "simplecrypt.h"
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
SimpleCrypt::SimpleCrypt():
|
||||||
|
m_key(0),
|
||||||
|
m_compressionMode(CompressionAuto),
|
||||||
|
m_protectionMode(ProtectionChecksum),
|
||||||
|
m_lastError(ErrorNoError) {}
|
||||||
|
|
||||||
|
SimpleCrypt::SimpleCrypt(quint64 key):
|
||||||
|
m_key(key),
|
||||||
|
m_compressionMode(CompressionAuto),
|
||||||
|
m_protectionMode(ProtectionChecksum),
|
||||||
|
m_lastError(ErrorNoError)
|
||||||
|
{
|
||||||
|
splitKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleCrypt::setKey(quint64 key)
|
||||||
|
{
|
||||||
|
m_key = key;
|
||||||
|
splitKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleCrypt::splitKey()
|
||||||
|
{
|
||||||
|
m_keyParts.clear();
|
||||||
|
m_keyParts.resize(8);
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
quint64 part = m_key;
|
||||||
|
for (int j=i; j>0; j--)
|
||||||
|
part = part >> 8;
|
||||||
|
part = part & 0xff;
|
||||||
|
m_keyParts[i] = static_cast<char>(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext)
|
||||||
|
{
|
||||||
|
QByteArray plaintextArray = plaintext.toUtf8();
|
||||||
|
return encryptToByteArray(plaintextArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext)
|
||||||
|
{
|
||||||
|
if (m_keyParts.isEmpty()) {
|
||||||
|
qWarning() << "No key set.";
|
||||||
|
m_lastError = ErrorNoKeySet;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray ba = plaintext;
|
||||||
|
|
||||||
|
CryptoFlags flags = CryptoFlagNone;
|
||||||
|
if (m_compressionMode == CompressionAlways) {
|
||||||
|
ba = qCompress(ba, 9); //maximum compression
|
||||||
|
flags |= CryptoFlagCompression;
|
||||||
|
} else if (m_compressionMode == CompressionAuto) {
|
||||||
|
QByteArray compressed = qCompress(ba, 9);
|
||||||
|
if (compressed.count() < ba.count()) {
|
||||||
|
ba = compressed;
|
||||||
|
flags |= CryptoFlagCompression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray integrityProtection;
|
||||||
|
if (m_protectionMode == ProtectionChecksum) {
|
||||||
|
flags |= CryptoFlagChecksum;
|
||||||
|
QDataStream s(&integrityProtection, QIODevice::WriteOnly);
|
||||||
|
s << qChecksum(ba.constData(), ba.length());
|
||||||
|
} else if (m_protectionMode == ProtectionHash) {
|
||||||
|
flags |= CryptoFlagHash;
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
|
hash.addData(ba);
|
||||||
|
|
||||||
|
integrityProtection += hash.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
//prepend a random char to the string
|
||||||
|
char randomChar = char(QRandomGenerator::global()->generate() & 0xFF);
|
||||||
|
ba = randomChar + integrityProtection + ba;
|
||||||
|
|
||||||
|
int pos(0);
|
||||||
|
char lastChar(0);
|
||||||
|
|
||||||
|
int cnt = ba.count();
|
||||||
|
|
||||||
|
while (pos < cnt) {
|
||||||
|
ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar;
|
||||||
|
lastChar = ba.at(pos);
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray resultArray;
|
||||||
|
resultArray.append(char(0x03)); //version for future updates to algorithm
|
||||||
|
resultArray.append(char(flags)); //encryption flags
|
||||||
|
resultArray.append(ba);
|
||||||
|
|
||||||
|
m_lastError = ErrorNoError;
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SimpleCrypt::encryptToString(const QString& plaintext)
|
||||||
|
{
|
||||||
|
QByteArray plaintextArray = plaintext.toUtf8();
|
||||||
|
QByteArray cypher = encryptToByteArray(plaintextArray);
|
||||||
|
QString cypherString = QString::fromLatin1(cypher.toBase64());
|
||||||
|
return cypherString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SimpleCrypt::encryptToString(QByteArray plaintext)
|
||||||
|
{
|
||||||
|
QByteArray cypher = encryptToByteArray(plaintext);
|
||||||
|
QString cypherString = QString::fromLatin1(cypher.toBase64());
|
||||||
|
return cypherString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SimpleCrypt::decryptToString(const QString &cyphertext)
|
||||||
|
{
|
||||||
|
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
|
||||||
|
QByteArray plaintextArray = decryptToByteArray(cyphertextArray);
|
||||||
|
QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size());
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SimpleCrypt::decryptToString(QByteArray cypher)
|
||||||
|
{
|
||||||
|
QByteArray ba = decryptToByteArray(cypher);
|
||||||
|
QString plaintext = QString::fromUtf8(ba, ba.size());
|
||||||
|
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext)
|
||||||
|
{
|
||||||
|
QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1());
|
||||||
|
QByteArray ba = decryptToByteArray(cyphertextArray);
|
||||||
|
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher)
|
||||||
|
{
|
||||||
|
if (m_keyParts.isEmpty()) {
|
||||||
|
qWarning() << "No key set.";
|
||||||
|
m_lastError = ErrorNoKeySet;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ba = cypher;
|
||||||
|
|
||||||
|
if( cypher.count() < 3 )
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
char version = ba.at(0);
|
||||||
|
|
||||||
|
if (version !=3) { //we only work with version 3
|
||||||
|
m_lastError = ErrorUnknownVersion;
|
||||||
|
qWarning() << "Invalid version or not a cyphertext.";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoFlags flags = CryptoFlags(ba.at(1));
|
||||||
|
|
||||||
|
ba = ba.mid(2);
|
||||||
|
int pos(0);
|
||||||
|
int cnt(ba.count());
|
||||||
|
char lastChar = 0;
|
||||||
|
|
||||||
|
while (pos < cnt) {
|
||||||
|
char currentChar = ba[pos];
|
||||||
|
ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8);
|
||||||
|
lastChar = currentChar;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
ba = ba.mid(1); //chop off the random number at the start
|
||||||
|
|
||||||
|
bool integrityOk(true);
|
||||||
|
if (flags.testFlag(CryptoFlagChecksum)) {
|
||||||
|
if (ba.length() < 2) {
|
||||||
|
m_lastError = ErrorIntegrityFailed;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
quint16 storedChecksum;
|
||||||
|
{
|
||||||
|
QDataStream s(&ba, QIODevice::ReadOnly);
|
||||||
|
s >> storedChecksum;
|
||||||
|
}
|
||||||
|
ba = ba.mid(2);
|
||||||
|
quint16 checksum = qChecksum(ba.constData(), ba.length());
|
||||||
|
integrityOk = (checksum == storedChecksum);
|
||||||
|
} else if (flags.testFlag(CryptoFlagHash)) {
|
||||||
|
if (ba.length() < 20) {
|
||||||
|
m_lastError = ErrorIntegrityFailed;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
QByteArray storedHash = ba.left(20);
|
||||||
|
ba = ba.mid(20);
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
|
hash.addData(ba);
|
||||||
|
integrityOk = (hash.result() == storedHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!integrityOk) {
|
||||||
|
m_lastError = ErrorIntegrityFailed;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.testFlag(CryptoFlagCompression))
|
||||||
|
ba = qUncompress(ba);
|
||||||
|
|
||||||
|
m_lastError = ErrorNoError;
|
||||||
|
return ba;
|
||||||
|
}
|
226
external/simpleCrypt/simplecrypt.h
vendored
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011, Andre Somers
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Rathenau Instituut, Andre Somers nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SIMPLECRYPT_H
|
||||||
|
#define SIMPLECRYPT_H
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QFlags>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
|
/**
|
||||||
|
@ short Simple encrypt*ion and decryption of strings and byte arrays
|
||||||
|
|
||||||
|
This class provides a simple implementation of encryption and decryption
|
||||||
|
of strings and byte arrays.
|
||||||
|
|
||||||
|
@warning The encryption provided by this class is NOT strong encryption. It may
|
||||||
|
help to shield things from curious eyes, but it will NOT stand up to someone
|
||||||
|
determined to break the encryption. Don't say you were not warned.
|
||||||
|
|
||||||
|
The class uses a 64 bit key. Simply create an instance of the class, set the key,
|
||||||
|
and use the encryptToString() method to calculate an encrypted version of the input string.
|
||||||
|
To decrypt that string again, use an instance of SimpleCrypt initialized with
|
||||||
|
the same key, and call the decryptToString() method with the encrypted string. If the key
|
||||||
|
matches, the decrypted version of the string will be returned again.
|
||||||
|
|
||||||
|
If you do not provide a key, or if something else is wrong, the encryption and
|
||||||
|
decryption function will return an empty string or will return a string containing nonsense.
|
||||||
|
lastError() will return a value indicating if the method was succesful, and if not, why not.
|
||||||
|
|
||||||
|
SimpleCrypt is prepared for the case that the encryption and decryption
|
||||||
|
algorithm is changed in a later version, by prepending a version identifier to the cypertext.
|
||||||
|
*/
|
||||||
|
class SimpleCrypt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
CompressionMode describes if compression will be applied to the data to be
|
||||||
|
encrypted.
|
||||||
|
*/
|
||||||
|
enum CompressionMode {
|
||||||
|
CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */
|
||||||
|
CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */
|
||||||
|
CompressionNever /*!< Never apply compression. */
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data
|
||||||
|
or wrong decryption keys.
|
||||||
|
|
||||||
|
Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This
|
||||||
|
increases the length of the resulting cypertext, but makes it possible to check if the plaintext
|
||||||
|
appears to be valid after decryption.
|
||||||
|
*/
|
||||||
|
enum IntegrityProtectionMode {
|
||||||
|
ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */
|
||||||
|
ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */
|
||||||
|
ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Error describes t*he type of error that occured.
|
||||||
|
*/
|
||||||
|
enum Error {
|
||||||
|
ErrorNoError, /*!< No error occurred. */
|
||||||
|
ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */
|
||||||
|
ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */
|
||||||
|
ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Constructor. *
|
||||||
|
|
||||||
|
Constructs a SimpleCrypt instance without a valid key set on it.
|
||||||
|
*/
|
||||||
|
SimpleCrypt();
|
||||||
|
/**
|
||||||
|
Constructor. *
|
||||||
|
|
||||||
|
Constructs a SimpleCrypt instance and initializes it with the given @arg key.
|
||||||
|
*/
|
||||||
|
explicit SimpleCrypt(quint64 key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
( Re-) initializes* the key with the given @arg key.
|
||||||
|
*/
|
||||||
|
void setKey(quint64 key);
|
||||||
|
/**
|
||||||
|
Returns true if SimpleCrypt has been initialized with a key.
|
||||||
|
*/
|
||||||
|
bool hasKey() const {return !m_keyParts.isEmpty();}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the compress*ion mode to use when encrypting data. The default mode is Auto.
|
||||||
|
|
||||||
|
Note that decryption is not influenced by this mode, as the decryption recognizes
|
||||||
|
what mode was used when encrypting.
|
||||||
|
*/
|
||||||
|
void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;}
|
||||||
|
/**
|
||||||
|
Returns the CompressionMode that is currently in use.
|
||||||
|
*/
|
||||||
|
CompressionMode compressionMode() const {return m_compressionMode;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the integrity mode to use when encrypting data. The default mode is Checksum.
|
||||||
|
|
||||||
|
Note that decryption is not influenced by this mode, as the decryption recognizes
|
||||||
|
what mode was used when encrypting.
|
||||||
|
*/
|
||||||
|
void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;}
|
||||||
|
/**
|
||||||
|
Returns the IntegrityProtectionMode that is currently in use.
|
||||||
|
*/
|
||||||
|
IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the last *error that occurred.
|
||||||
|
*/
|
||||||
|
Error lastError() const {return m_lastError;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Encrypts the @arg* plaintext string with the key the class was initialized with, and returns
|
||||||
|
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
|
||||||
|
actual result of the string, so it can be stored easily in a text format.
|
||||||
|
*/
|
||||||
|
QString encryptToString(const QString& plaintext) ;
|
||||||
|
/**
|
||||||
|
Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns
|
||||||
|
a cyphertext the result. The result is a base64 encoded version of the binary array that is the
|
||||||
|
actual result of the encryption, so it can be stored easily in a text format.
|
||||||
|
*/
|
||||||
|
QString encryptToString(QByteArray plaintext) ;
|
||||||
|
/**
|
||||||
|
Encrypts the @arg* plaintext string with the key the class was initialized with, and returns
|
||||||
|
a binary cyphertext in a QByteArray the result.
|
||||||
|
|
||||||
|
This method returns a byte array, that is useable for storing a binary format. If you need
|
||||||
|
a string you can store in a text file, use encryptToString() instead.
|
||||||
|
*/
|
||||||
|
QByteArray encryptToByteArray(const QString& plaintext) ;
|
||||||
|
/**
|
||||||
|
Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns
|
||||||
|
a binary cyphertext in a QByteArray the result.
|
||||||
|
|
||||||
|
This method returns a byte array, that is useable for storing a binary format. If you need
|
||||||
|
a string you can store in a text file, use encryptToString() instead.
|
||||||
|
*/
|
||||||
|
QByteArray encryptToByteArray(QByteArray plaintext) ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Decrypts a cypher*text string encrypted with this class with the set key back to the
|
||||||
|
plain text version.
|
||||||
|
|
||||||
|
If an error occured, such as non-matching keys between encryption and decryption,
|
||||||
|
an empty string or a string containing nonsense may be returned.
|
||||||
|
*/
|
||||||
|
QString decryptToString(const QString& cyphertext) ;
|
||||||
|
/**
|
||||||
|
Decrypts a cypher*text string encrypted with this class with the set key back to the
|
||||||
|
plain text version.
|
||||||
|
|
||||||
|
If an error occured, such as non-matching keys between encryption and decryption,
|
||||||
|
an empty string or a string containing nonsense may be returned.
|
||||||
|
*/
|
||||||
|
QByteArray decryptToByteArray(const QString& cyphertext) ;
|
||||||
|
/**
|
||||||
|
Decrypts a cypher*text binary encrypted with this class with the set key back to the
|
||||||
|
plain text version.
|
||||||
|
|
||||||
|
If an error occured, such as non-matching keys between encryption and decryption,
|
||||||
|
an empty string or a string containing nonsense may be returned.
|
||||||
|
*/
|
||||||
|
QString decryptToString(QByteArray cypher) ;
|
||||||
|
/**
|
||||||
|
Decrypts a cypher*text binary encrypted with this class with the set key back to the
|
||||||
|
plain text version.
|
||||||
|
|
||||||
|
If an error occured, such as non-matching keys between encryption and decryption,
|
||||||
|
an empty string or a string containing nonsense may be returned.
|
||||||
|
*/
|
||||||
|
QByteArray decryptToByteArray(QByteArray cypher) ;
|
||||||
|
|
||||||
|
//enum to describe options that have been used for the encryption. Currently only one, but
|
||||||
|
//that only leaves room for future extensions like adding a cryptographic hash...
|
||||||
|
enum CryptoFlag{CryptoFlagNone = 0,
|
||||||
|
CryptoFlagCompression = 0x01,
|
||||||
|
CryptoFlagChecksum = 0x02,
|
||||||
|
CryptoFlagHash = 0x04
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag);
|
||||||
|
private:
|
||||||
|
|
||||||
|
void splitKey();
|
||||||
|
|
||||||
|
quint64 m_key;
|
||||||
|
QVector<char> m_keyParts;
|
||||||
|
CompressionMode m_compressionMode;
|
||||||
|
IntegrityProtectionMode m_protectionMode;
|
||||||
|
Error m_lastError;
|
||||||
|
};
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags)
|
||||||
|
|
||||||
|
#endif // SimpleCrypt_H
|
765
global.cpp
@ -1,765 +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 "global.h"
|
|
||||||
#include <uuid/uuid.h>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QPalette>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
Shared::Message::Message(Shared::Message::Type p_type):
|
|
||||||
jFrom(),
|
|
||||||
rFrom(),
|
|
||||||
jTo(),
|
|
||||||
rTo(),
|
|
||||||
id(),
|
|
||||||
body(),
|
|
||||||
time(),
|
|
||||||
thread(),
|
|
||||||
type(p_type),
|
|
||||||
outgoing(false),
|
|
||||||
forwarded(false),
|
|
||||||
state(State::delivered),
|
|
||||||
edited(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message::Message():
|
|
||||||
jFrom(),
|
|
||||||
rFrom(),
|
|
||||||
jTo(),
|
|
||||||
rTo(),
|
|
||||||
id(),
|
|
||||||
body(),
|
|
||||||
time(),
|
|
||||||
thread(),
|
|
||||||
type(Message::normal),
|
|
||||||
outgoing(false),
|
|
||||||
forwarded(false),
|
|
||||||
state(State::delivered),
|
|
||||||
edited(false),
|
|
||||||
errorText(),
|
|
||||||
originalMessage(),
|
|
||||||
lastModified()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getBody() const
|
|
||||||
{
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getFrom() const
|
|
||||||
{
|
|
||||||
QString from = jFrom;
|
|
||||||
if (rFrom.size() > 0) {
|
|
||||||
from += "/" + rFrom;
|
|
||||||
}
|
|
||||||
return from;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getTo() const
|
|
||||||
{
|
|
||||||
QString to = jTo;
|
|
||||||
if (rTo.size() > 0) {
|
|
||||||
to += "/" + rTo;
|
|
||||||
}
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getId() const
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime Shared::Message::getTime() const
|
|
||||||
{
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setBody(const QString& p_body)
|
|
||||||
{
|
|
||||||
body = p_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setFrom(const QString& from)
|
|
||||||
{
|
|
||||||
QStringList list = from.split("/");
|
|
||||||
if (list.size() == 1) {
|
|
||||||
jFrom = from;
|
|
||||||
} else {
|
|
||||||
jFrom = list.front();
|
|
||||||
rFrom = list.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setTo(const QString& to)
|
|
||||||
{
|
|
||||||
QStringList list = to.split("/");
|
|
||||||
if (list.size() == 1) {
|
|
||||||
jTo = to;
|
|
||||||
} else {
|
|
||||||
jTo = list.front();
|
|
||||||
rTo = list.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setId(const QString& p_id)
|
|
||||||
{
|
|
||||||
id = p_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setTime(const QDateTime& p_time)
|
|
||||||
{
|
|
||||||
time = p_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getFromJid() const
|
|
||||||
{
|
|
||||||
return jFrom;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getFromResource() const
|
|
||||||
{
|
|
||||||
return rFrom;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getToJid() const
|
|
||||||
{
|
|
||||||
return jTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getToResource() const
|
|
||||||
{
|
|
||||||
return rTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getErrorText() const
|
|
||||||
{
|
|
||||||
return errorText;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getPenPalJid() const
|
|
||||||
{
|
|
||||||
if (outgoing) {
|
|
||||||
return jTo;
|
|
||||||
} else {
|
|
||||||
return jFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getPenPalResource() const
|
|
||||||
{
|
|
||||||
if (outgoing) {
|
|
||||||
return rTo;
|
|
||||||
} else {
|
|
||||||
return rFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message::State Shared::Message::getState() const
|
|
||||||
{
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::getEdited() const
|
|
||||||
{
|
|
||||||
return edited;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setFromJid(const QString& from)
|
|
||||||
{
|
|
||||||
jFrom = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setFromResource(const QString& from)
|
|
||||||
{
|
|
||||||
rFrom = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setToJid(const QString& to)
|
|
||||||
{
|
|
||||||
jTo = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setToResource(const QString& to)
|
|
||||||
{
|
|
||||||
rTo = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setErrorText(const QString& err)
|
|
||||||
{
|
|
||||||
if (state == State::error) {
|
|
||||||
errorText = err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::getOutgoing() const
|
|
||||||
{
|
|
||||||
return outgoing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setOutgoing(bool og)
|
|
||||||
{
|
|
||||||
outgoing = og;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::getForwarded() const
|
|
||||||
{
|
|
||||||
return forwarded;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::generateRandomId()
|
|
||||||
{
|
|
||||||
id = generateUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getThread() const
|
|
||||||
{
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setForwarded(bool fwd)
|
|
||||||
{
|
|
||||||
forwarded = fwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setThread(const QString& p_body)
|
|
||||||
{
|
|
||||||
thread = p_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime Shared::Message::getLastModified() const
|
|
||||||
{
|
|
||||||
return lastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getOriginalBody() const
|
|
||||||
{
|
|
||||||
return originalMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Message::Type Shared::Message::getType() const
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setType(Shared::Message::Type t)
|
|
||||||
{
|
|
||||||
type = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setState(Shared::Message::State p_state)
|
|
||||||
{
|
|
||||||
state = p_state;
|
|
||||||
|
|
||||||
if (state != State::error) {
|
|
||||||
errorText = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::serverStored() const
|
|
||||||
{
|
|
||||||
return state == State::delivered || state == State::sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setEdited(bool p_edited)
|
|
||||||
{
|
|
||||||
edited = p_edited;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::serialize(QDataStream& data) const
|
|
||||||
{
|
|
||||||
data << jFrom;
|
|
||||||
data << rFrom;
|
|
||||||
data << jTo;
|
|
||||||
data << rTo;
|
|
||||||
data << id;
|
|
||||||
data << body;
|
|
||||||
data << time;
|
|
||||||
data << thread;
|
|
||||||
data << (quint8)type;
|
|
||||||
data << outgoing;
|
|
||||||
data << forwarded;
|
|
||||||
data << oob;
|
|
||||||
data << (quint8)state;
|
|
||||||
data << edited;
|
|
||||||
if (state == State::error) {
|
|
||||||
data << errorText;
|
|
||||||
}
|
|
||||||
if (edited) {
|
|
||||||
data << originalMessage;
|
|
||||||
data << lastModified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::deserialize(QDataStream& data)
|
|
||||||
{
|
|
||||||
data >> jFrom;
|
|
||||||
data >> rFrom;
|
|
||||||
data >> jTo;
|
|
||||||
data >> rTo;
|
|
||||||
data >> id;
|
|
||||||
data >> body;
|
|
||||||
data >> time;
|
|
||||||
data >> thread;
|
|
||||||
quint8 t;
|
|
||||||
data >> t;
|
|
||||||
type = static_cast<Type>(t);
|
|
||||||
data >> outgoing;
|
|
||||||
data >> forwarded;
|
|
||||||
data >> oob;
|
|
||||||
quint8 s;
|
|
||||||
data >> s;
|
|
||||||
state = static_cast<State>(s);
|
|
||||||
data >> edited;
|
|
||||||
if (state == State::error) {
|
|
||||||
data >> errorText;
|
|
||||||
}
|
|
||||||
if (edited) {
|
|
||||||
data >> originalMessage;
|
|
||||||
data >> lastModified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
|
||||||
{
|
|
||||||
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setState(static_cast<State>(itr.value().toUInt()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == State::error) {
|
|
||||||
itr = data.find("errorText");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
setErrorText(itr.value().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool idChanged = false;
|
|
||||||
itr = data.find("id");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
QString newId = itr.value().toString();
|
|
||||||
if (id != newId) {
|
|
||||||
setId(newId);
|
|
||||||
idChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
itr = data.find("body");
|
|
||||||
if (itr != data.end()) {
|
|
||||||
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
|
||||||
QDateTime correctionDate;
|
|
||||||
if (dItr != data.end()) {
|
|
||||||
correctionDate = dItr.value().toDateTime();
|
|
||||||
} else {
|
|
||||||
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
|
|
||||||
}
|
|
||||||
if (!edited || lastModified < correctionDate) {
|
|
||||||
originalMessage = body;
|
|
||||||
lastModified = correctionDate;
|
|
||||||
setBody(itr.value().toString());
|
|
||||||
setEdited(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::generateUUID()
|
|
||||||
{
|
|
||||||
uuid_t uuid;
|
|
||||||
uuid_generate(uuid);
|
|
||||||
|
|
||||||
char uuid_str[36];
|
|
||||||
uuid_unparse_lower(uuid, uuid_str);
|
|
||||||
return uuid_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setCurrentTime()
|
|
||||||
{
|
|
||||||
time = QDateTime::currentDateTimeUtc();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::Message::getOutOfBandUrl() const
|
|
||||||
{
|
|
||||||
return oob;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::hasOutOfBandUrl() const
|
|
||||||
{
|
|
||||||
return oob.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::Message::setOutOfBandUrl(const QString& url)
|
|
||||||
{
|
|
||||||
oob = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shared::Message::storable() const
|
|
||||||
{
|
|
||||||
return id.size() > 0 && (body.size() > 0 || oob.size()) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::VCard::Contact::Contact(Shared::VCard::Contact::Role p_role, bool p_prefered):
|
|
||||||
role(p_role),
|
|
||||||
prefered(p_prefered)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Shared::VCard::Email::Email(const QString& addr, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
|
||||||
Contact(p_role, p_prefered),
|
|
||||||
address(addr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Shared::VCard::Phone::Phone(const QString& nmbr, Shared::VCard::Phone::Type p_type, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
|
||||||
Contact(p_role, p_prefered),
|
|
||||||
number(nmbr),
|
|
||||||
type(p_type)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Shared::VCard::Address::Address(const QString& zCode, const QString& cntry, const QString& rgn, const QString& lclty, const QString& strt, const QString& ext, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
|
||||||
Contact(p_role, p_prefered),
|
|
||||||
zipCode(zCode),
|
|
||||||
country(cntry),
|
|
||||||
region(rgn),
|
|
||||||
locality(lclty),
|
|
||||||
street(strt),
|
|
||||||
external(ext)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Shared::VCard::VCard():
|
|
||||||
fullName(),
|
|
||||||
firstName(),
|
|
||||||
middleName(),
|
|
||||||
lastName(),
|
|
||||||
nickName(),
|
|
||||||
description(),
|
|
||||||
url(),
|
|
||||||
organizationName(),
|
|
||||||
organizationUnit(),
|
|
||||||
organizationRole(),
|
|
||||||
jobTitle(),
|
|
||||||
birthday(),
|
|
||||||
photoType(Avatar::empty),
|
|
||||||
photoPath(),
|
|
||||||
receivingTime(QDateTime::currentDateTimeUtc()),
|
|
||||||
emails(),
|
|
||||||
phones(),
|
|
||||||
addresses()
|
|
||||||
{}
|
|
||||||
|
|
||||||
Shared::VCard::VCard(const QDateTime& creationTime):
|
|
||||||
fullName(),
|
|
||||||
firstName(),
|
|
||||||
middleName(),
|
|
||||||
lastName(),
|
|
||||||
nickName(),
|
|
||||||
description(),
|
|
||||||
url(),
|
|
||||||
organizationName(),
|
|
||||||
organizationUnit(),
|
|
||||||
organizationRole(),
|
|
||||||
jobTitle(),
|
|
||||||
birthday(),
|
|
||||||
photoType(Avatar::empty),
|
|
||||||
photoPath(),
|
|
||||||
receivingTime(creationTime),
|
|
||||||
emails(),
|
|
||||||
phones(),
|
|
||||||
addresses()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getAvatarPath() const
|
|
||||||
{
|
|
||||||
return photoPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::Avatar Shared::VCard::getAvatarType() const
|
|
||||||
{
|
|
||||||
return photoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDate Shared::VCard::getBirthday() const
|
|
||||||
{
|
|
||||||
return birthday;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getDescription() const
|
|
||||||
{
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getFirstName() const
|
|
||||||
{
|
|
||||||
return firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getLastName() const
|
|
||||||
{
|
|
||||||
return lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getMiddleName() const
|
|
||||||
{
|
|
||||||
return middleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getNickName() const
|
|
||||||
{
|
|
||||||
return nickName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setAvatarPath(const QString& path)
|
|
||||||
{
|
|
||||||
if (path != photoPath) {
|
|
||||||
photoPath = path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setAvatarType(Shared::Avatar type)
|
|
||||||
{
|
|
||||||
if (photoType != type) {
|
|
||||||
photoType = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setBirthday(const QDate& date)
|
|
||||||
{
|
|
||||||
if (date.isValid() && birthday != date) {
|
|
||||||
birthday = date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setDescription(const QString& descr)
|
|
||||||
{
|
|
||||||
if (description != descr) {
|
|
||||||
description = descr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setFirstName(const QString& first)
|
|
||||||
{
|
|
||||||
if (firstName != first) {
|
|
||||||
firstName = first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setLastName(const QString& last)
|
|
||||||
{
|
|
||||||
if (lastName != last) {
|
|
||||||
lastName = last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setMiddleName(const QString& middle)
|
|
||||||
{
|
|
||||||
if (middleName != middle) {
|
|
||||||
middleName = middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setNickName(const QString& nick)
|
|
||||||
{
|
|
||||||
if (nickName != nick) {
|
|
||||||
nickName = nick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getFullName() const
|
|
||||||
{
|
|
||||||
return fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getUrl() const
|
|
||||||
{
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setFullName(const QString& name)
|
|
||||||
{
|
|
||||||
if (fullName != name) {
|
|
||||||
fullName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setUrl(const QString& u)
|
|
||||||
{
|
|
||||||
if (url != u) {
|
|
||||||
url = u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getOrgName() const
|
|
||||||
{
|
|
||||||
return organizationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getOrgRole() const
|
|
||||||
{
|
|
||||||
return organizationRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getOrgTitle() const
|
|
||||||
{
|
|
||||||
return jobTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Shared::VCard::getOrgUnit() const
|
|
||||||
{
|
|
||||||
return organizationUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setOrgName(const QString& name)
|
|
||||||
{
|
|
||||||
if (organizationName != name) {
|
|
||||||
organizationName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setOrgRole(const QString& role)
|
|
||||||
{
|
|
||||||
if (organizationRole != role) {
|
|
||||||
organizationRole = role;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setOrgTitle(const QString& title)
|
|
||||||
{
|
|
||||||
if (jobTitle != title) {
|
|
||||||
jobTitle = title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shared::VCard::setOrgUnit(const QString& unit)
|
|
||||||
{
|
|
||||||
if (organizationUnit != unit) {
|
|
||||||
organizationUnit = unit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime Shared::VCard::getReceivingTime() const
|
|
||||||
{
|
|
||||||
return receivingTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<Shared::VCard::Email> & Shared::VCard::getEmails()
|
|
||||||
{
|
|
||||||
return emails;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses()
|
|
||||||
{
|
|
||||||
return addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones()
|
|
||||||
{
|
|
||||||
return phones;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::deque<Shared::VCard::Email> & Shared::VCard::getEmails() const
|
|
||||||
{
|
|
||||||
return emails;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses() const
|
|
||||||
{
|
|
||||||
return addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones() const
|
|
||||||
{
|
|
||||||
return phones;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::deque<QString>Shared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"};
|
|
||||||
const std::deque<QString>Shared::VCard::Phone::typeNames = {"Fax", "Pager", "Voice", "Cell", "Video", "Modem", "Other"};
|
|
||||||
|
|
||||||
QIcon Shared::availabilityIcon(Shared::Availability av, bool big)
|
|
||||||
{
|
|
||||||
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
|
||||||
big ?
|
|
||||||
Shared::fallbackAvailabilityThemeIconsDarkBig:
|
|
||||||
Shared::fallbackAvailabilityThemeIconsDarkSmall:
|
|
||||||
big ?
|
|
||||||
Shared::fallbackAvailabilityThemeIconsLightBig:
|
|
||||||
Shared::fallbackAvailabilityThemeIconsLightSmall;
|
|
||||||
|
|
||||||
return QIcon::fromTheme(availabilityThemeIcons[av], QIcon(fallback[av]));
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon Shared::subscriptionStateIcon(Shared::SubscriptionState ss, bool big)
|
|
||||||
{
|
|
||||||
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
|
||||||
big ?
|
|
||||||
Shared::fallbackSubscriptionStateThemeIconsDarkBig:
|
|
||||||
Shared::fallbackSubscriptionStateThemeIconsDarkSmall:
|
|
||||||
big ?
|
|
||||||
Shared::fallbackSubscriptionStateThemeIconsLightBig:
|
|
||||||
Shared::fallbackSubscriptionStateThemeIconsLightSmall;
|
|
||||||
|
|
||||||
return QIcon::fromTheme(subscriptionStateThemeIcons[ss], QIcon(fallback[ss]));
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon Shared::connectionStateIcon(Shared::ConnectionState cs, bool big)
|
|
||||||
{
|
|
||||||
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
|
||||||
big ?
|
|
||||||
Shared::fallbackConnectionStateThemeIconsDarkBig:
|
|
||||||
Shared::fallbackConnectionStateThemeIconsDarkSmall:
|
|
||||||
big ?
|
|
||||||
Shared::fallbackConnectionStateThemeIconsLightBig:
|
|
||||||
Shared::fallbackConnectionStateThemeIconsLightSmall;
|
|
||||||
|
|
||||||
return QIcon::fromTheme(connectionStateThemeIcons[cs], QIcon(fallback[cs]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString ds = ":images/fallback/dark/small/";
|
|
||||||
static const QString db = ":images/fallback/dark/big/";
|
|
||||||
static const QString ls = ":images/fallback/light/small/";
|
|
||||||
static const QString lb = ":images/fallback/light/big/";
|
|
||||||
|
|
||||||
QIcon Shared::icon(const QString& name, bool big)
|
|
||||||
{
|
|
||||||
std::map<QString, std::pair<QString, QString>>::const_iterator itr = icons.find(name);
|
|
||||||
if (itr != icons.end()) {
|
|
||||||
const QString& prefix = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
|
||||||
big ? db : ds:
|
|
||||||
big ? lb : ls;
|
|
||||||
return QIcon::fromTheme(itr->second.first, QIcon(prefix + itr->second.second + ".svg"));
|
|
||||||
} else {
|
|
||||||
qDebug() << "Icon" << name << "not found";
|
|
||||||
return QIcon::fromTheme(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QString Shared::iconPath(const QString& name, bool big)
|
|
||||||
{
|
|
||||||
QString result = "";
|
|
||||||
std::map<QString, std::pair<QString, QString>>::const_iterator itr = icons.find(name);
|
|
||||||
if (itr != icons.end()) {
|
|
||||||
const QString& prefix = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
|
||||||
big ? db : ds:
|
|
||||||
big ? lb : ls;
|
|
||||||
result = prefix + itr->second.second + ".svg";
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
512
global.h
@ -1,512 +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 GLOBAL_H
|
|
||||||
#define GLOBAL_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <deque>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
namespace Shared {
|
|
||||||
|
|
||||||
enum ConnectionState {
|
|
||||||
disconnected,
|
|
||||||
connecting,
|
|
||||||
connected,
|
|
||||||
error
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Availability {
|
|
||||||
online,
|
|
||||||
away,
|
|
||||||
extendedAway,
|
|
||||||
busy,
|
|
||||||
chatty,
|
|
||||||
invisible,
|
|
||||||
offline
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SubscriptionState {
|
|
||||||
none,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
both,
|
|
||||||
unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Affiliation {
|
|
||||||
unspecified,
|
|
||||||
outcast,
|
|
||||||
nobody,
|
|
||||||
member,
|
|
||||||
admin,
|
|
||||||
owner
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Role {
|
|
||||||
unspecified,
|
|
||||||
nobody,
|
|
||||||
visitor,
|
|
||||||
participant,
|
|
||||||
moderator
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Avatar {
|
|
||||||
empty,
|
|
||||||
autocreated,
|
|
||||||
valid
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Availability availabilityHighest = offline;
|
|
||||||
static const Availability availabilityLowest = online;
|
|
||||||
|
|
||||||
static const SubscriptionState subscriptionStateHighest = unknown;
|
|
||||||
static const SubscriptionState subscriptionStateLowest = none;
|
|
||||||
|
|
||||||
static const Affiliation affiliationHighest = Affiliation::owner;
|
|
||||||
static const Affiliation affiliationLowest = Affiliation::unspecified;
|
|
||||||
|
|
||||||
static const Role roleHighest = Role::moderator;
|
|
||||||
static const Role roleLowest = Role::unspecified;
|
|
||||||
|
|
||||||
static const std::deque<QString> connectionStateNames = {"Disconnected", "Connecting", "Connected", "Error"};
|
|
||||||
static const std::deque<QString> connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"};
|
|
||||||
|
|
||||||
static const std::deque<QString> availabilityThemeIcons = {
|
|
||||||
"user-online",
|
|
||||||
"user-away",
|
|
||||||
"user-away-extended",
|
|
||||||
"user-busy",
|
|
||||||
"chatty",
|
|
||||||
"user-invisible",
|
|
||||||
"user-offline"
|
|
||||||
};
|
|
||||||
static const std::deque<QString> availabilityNames = {"Online", "Away", "Absent", "Busy", "Chatty", "Invisible", "Offline"};
|
|
||||||
|
|
||||||
static const std::deque<QString> subscriptionStateThemeIcons = {"edit-none", "arrow-down-double", "arrow-up-double", "dialog-ok", "question"};
|
|
||||||
static const std::deque<QString> subscriptionStateNames = {"None", "From", "To", "Both", "Unknown"};
|
|
||||||
|
|
||||||
static const std::deque<QString> affiliationNames = {"Unspecified", "Outcast", "Nobody", "Member", "Admin", "Owner"};
|
|
||||||
static const std::deque<QString> roleNames = {"Unspecified", "Nobody", "Visitor", "Participant", "Moderator"};
|
|
||||||
|
|
||||||
static const std::deque<QString> messageStateNames = {"Pending", "Sent", "Delivered", "Error"};
|
|
||||||
static const std::deque<QString> messageStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"};
|
|
||||||
|
|
||||||
QString generateUUID();
|
|
||||||
|
|
||||||
static const std::vector<QColor> colorPalette = {
|
|
||||||
QColor(244, 27, 63),
|
|
||||||
QColor(21, 104, 156),
|
|
||||||
QColor(38, 156, 98),
|
|
||||||
QColor(247, 103, 101),
|
|
||||||
QColor(121, 37, 117),
|
|
||||||
QColor(242, 202, 33),
|
|
||||||
QColor(168, 22, 63),
|
|
||||||
QColor(35, 100, 52),
|
|
||||||
QColor(52, 161, 152),
|
|
||||||
QColor(239, 53, 111),
|
|
||||||
QColor(237, 234, 36),
|
|
||||||
QColor(153, 148, 194),
|
|
||||||
QColor(211, 102, 151),
|
|
||||||
QColor(194, 63, 118),
|
|
||||||
QColor(249, 149, 51),
|
|
||||||
QColor(244, 206, 109),
|
|
||||||
QColor(121, 105, 153),
|
|
||||||
QColor(244, 199, 30),
|
|
||||||
QColor(28, 112, 28),
|
|
||||||
QColor(172, 18, 20),
|
|
||||||
QColor(25, 66, 110),
|
|
||||||
QColor(25, 149, 104),
|
|
||||||
QColor(214, 148, 0),
|
|
||||||
QColor(203, 47, 57),
|
|
||||||
QColor(4, 54, 84),
|
|
||||||
QColor(116, 161, 97),
|
|
||||||
QColor(50, 68, 52),
|
|
||||||
QColor(237, 179, 20),
|
|
||||||
QColor(69, 114, 147),
|
|
||||||
QColor(242, 212, 31),
|
|
||||||
QColor(248, 19, 20),
|
|
||||||
QColor(84, 102, 84),
|
|
||||||
QColor(25, 53, 122),
|
|
||||||
QColor(91, 91, 109),
|
|
||||||
QColor(17, 17, 80),
|
|
||||||
QColor(54, 54, 94)
|
|
||||||
};
|
|
||||||
|
|
||||||
class Message {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
error,
|
|
||||||
normal,
|
|
||||||
chat,
|
|
||||||
groupChat,
|
|
||||||
headline
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class State {
|
|
||||||
pending,
|
|
||||||
sent,
|
|
||||||
delivered,
|
|
||||||
error
|
|
||||||
};
|
|
||||||
|
|
||||||
Message(Type p_type);
|
|
||||||
Message();
|
|
||||||
|
|
||||||
void setFrom(const QString& from);
|
|
||||||
void setFromResource(const QString& from);
|
|
||||||
void setFromJid(const QString& from);
|
|
||||||
void setTo(const QString& to);
|
|
||||||
void setToResource(const QString& to);
|
|
||||||
void setToJid(const QString& to);
|
|
||||||
void setTime(const QDateTime& p_time);
|
|
||||||
void setId(const QString& p_id);
|
|
||||||
void setBody(const QString& p_body);
|
|
||||||
void setThread(const QString& p_body);
|
|
||||||
void setOutgoing(bool og);
|
|
||||||
void setForwarded(bool fwd);
|
|
||||||
void setType(Type t);
|
|
||||||
void setCurrentTime();
|
|
||||||
void setOutOfBandUrl(const QString& url);
|
|
||||||
void setState(State p_state);
|
|
||||||
void setEdited(bool p_edited);
|
|
||||||
void setErrorText(const QString& err);
|
|
||||||
bool change(const QMap<QString, QVariant>& data);
|
|
||||||
|
|
||||||
QString getFrom() const;
|
|
||||||
QString getFromJid() const;
|
|
||||||
QString getFromResource() const;
|
|
||||||
QString getTo() const;
|
|
||||||
QString getToJid() const;
|
|
||||||
QString getToResource() const;
|
|
||||||
QDateTime getTime() const;
|
|
||||||
QString getId() const;
|
|
||||||
QString getBody() const;
|
|
||||||
QString getThread() const;
|
|
||||||
bool getOutgoing() const;
|
|
||||||
bool getForwarded() const;
|
|
||||||
Type getType() const;
|
|
||||||
bool hasOutOfBandUrl() const;
|
|
||||||
bool storable() const;
|
|
||||||
QString getOutOfBandUrl() const;
|
|
||||||
State getState() const;
|
|
||||||
bool getEdited() const;
|
|
||||||
QString getErrorText() const;
|
|
||||||
|
|
||||||
QString getPenPalJid() const;
|
|
||||||
QString getPenPalResource() const;
|
|
||||||
void generateRandomId();
|
|
||||||
bool serverStored() const;
|
|
||||||
QDateTime getLastModified() const;
|
|
||||||
QString getOriginalBody() const;
|
|
||||||
|
|
||||||
void serialize(QDataStream& data) const;
|
|
||||||
void deserialize(QDataStream& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString jFrom;
|
|
||||||
QString rFrom;
|
|
||||||
QString jTo;
|
|
||||||
QString rTo;
|
|
||||||
QString id;
|
|
||||||
QString body;
|
|
||||||
QDateTime time;
|
|
||||||
QString thread;
|
|
||||||
Type type;
|
|
||||||
bool outgoing;
|
|
||||||
bool forwarded;
|
|
||||||
QString oob;
|
|
||||||
State state;
|
|
||||||
bool edited;
|
|
||||||
QString errorText;
|
|
||||||
QString originalMessage;
|
|
||||||
QDateTime lastModified;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VCard {
|
|
||||||
class Contact {
|
|
||||||
public:
|
|
||||||
enum Role {
|
|
||||||
none,
|
|
||||||
home,
|
|
||||||
work
|
|
||||||
};
|
|
||||||
static const std::deque<QString> roleNames;
|
|
||||||
|
|
||||||
Contact(Role p_role = none, bool p_prefered = false);
|
|
||||||
|
|
||||||
Role role;
|
|
||||||
bool prefered;
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
class Email : public Contact {
|
|
||||||
public:
|
|
||||||
Email(const QString& address, Role p_role = none, bool p_prefered = false);
|
|
||||||
|
|
||||||
QString address;
|
|
||||||
};
|
|
||||||
class Phone : public Contact {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
fax,
|
|
||||||
pager,
|
|
||||||
voice,
|
|
||||||
cell,
|
|
||||||
video,
|
|
||||||
modem,
|
|
||||||
other
|
|
||||||
};
|
|
||||||
static const std::deque<QString> typeNames;
|
|
||||||
Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false);
|
|
||||||
|
|
||||||
QString number;
|
|
||||||
Type type;
|
|
||||||
};
|
|
||||||
class Address : public Contact {
|
|
||||||
public:
|
|
||||||
Address(
|
|
||||||
const QString& zCode = "",
|
|
||||||
const QString& cntry = "",
|
|
||||||
const QString& rgn = "",
|
|
||||||
const QString& lclty = "",
|
|
||||||
const QString& strt = "",
|
|
||||||
const QString& ext = "",
|
|
||||||
Role p_role = none,
|
|
||||||
bool p_prefered = false
|
|
||||||
);
|
|
||||||
|
|
||||||
QString zipCode;
|
|
||||||
QString country;
|
|
||||||
QString region;
|
|
||||||
QString locality;
|
|
||||||
QString street;
|
|
||||||
QString external;
|
|
||||||
};
|
|
||||||
VCard();
|
|
||||||
VCard(const QDateTime& creationTime);
|
|
||||||
|
|
||||||
QString getFullName() const;
|
|
||||||
void setFullName(const QString& name);
|
|
||||||
QString getFirstName() const;
|
|
||||||
void setFirstName(const QString& first);
|
|
||||||
QString getMiddleName() const;
|
|
||||||
void setMiddleName(const QString& middle);
|
|
||||||
QString getLastName() const;
|
|
||||||
void setLastName(const QString& last);
|
|
||||||
QString getNickName() const;
|
|
||||||
void setNickName(const QString& nick);
|
|
||||||
QString getDescription() const;
|
|
||||||
void setDescription(const QString& descr);
|
|
||||||
QString getUrl() const;
|
|
||||||
void setUrl(const QString& u);
|
|
||||||
QDate getBirthday() const;
|
|
||||||
void setBirthday(const QDate& date);
|
|
||||||
Avatar getAvatarType() const;
|
|
||||||
void setAvatarType(Avatar type);
|
|
||||||
QString getAvatarPath() const;
|
|
||||||
void setAvatarPath(const QString& path);
|
|
||||||
QString getOrgName() const;
|
|
||||||
void setOrgName(const QString& name);
|
|
||||||
QString getOrgUnit() const;
|
|
||||||
void setOrgUnit(const QString& unit);
|
|
||||||
QString getOrgRole() const;
|
|
||||||
void setOrgRole(const QString& role);
|
|
||||||
QString getOrgTitle() const;
|
|
||||||
void setOrgTitle(const QString& title);
|
|
||||||
QDateTime getReceivingTime() const;
|
|
||||||
std::deque<Email>& getEmails();
|
|
||||||
const std::deque<Email>& getEmails() const;
|
|
||||||
std::deque<Phone>& getPhones();
|
|
||||||
const std::deque<Phone>& getPhones() const;
|
|
||||||
std::deque<Address>& getAddresses();
|
|
||||||
const std::deque<Address>& getAddresses() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString fullName;
|
|
||||||
QString firstName;
|
|
||||||
QString middleName;
|
|
||||||
QString lastName;
|
|
||||||
QString nickName;
|
|
||||||
QString description;
|
|
||||||
QString url;
|
|
||||||
QString organizationName;
|
|
||||||
QString organizationUnit;
|
|
||||||
QString organizationRole;
|
|
||||||
QString jobTitle;
|
|
||||||
QDate birthday;
|
|
||||||
Avatar photoType;
|
|
||||||
QString photoPath;
|
|
||||||
QDateTime receivingTime;
|
|
||||||
std::deque<Email> emails;
|
|
||||||
std::deque<Phone> phones;
|
|
||||||
std::deque<Address> addresses;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackAvailabilityThemeIconsLightBig = {
|
|
||||||
":images/fallback/light/big/online.svg",
|
|
||||||
":images/fallback/light/big/away.svg",
|
|
||||||
":images/fallback/light/big/absent.svg",
|
|
||||||
":images/fallback/light/big/busy.svg",
|
|
||||||
":images/fallback/light/big/chatty.svg",
|
|
||||||
":images/fallback/light/big/invisible.svg",
|
|
||||||
":images/fallback/light/big/offline.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackSubscriptionStateThemeIconsLightBig = {
|
|
||||||
":images/fallback/light/big/edit-none.svg",
|
|
||||||
":images/fallback/light/big/arrow-down-double.svg",
|
|
||||||
":images/fallback/light/big/arrow-up-double.svg",
|
|
||||||
":images/fallback/light/big/dialog-ok.svg",
|
|
||||||
":images/fallback/light/big/question.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackConnectionStateThemeIconsLightBig = {
|
|
||||||
":images/fallback/light/big/state-offline.svg",
|
|
||||||
":images/fallback/light/big/state-sync.svg",
|
|
||||||
":images/fallback/light/big/state-ok.svg",
|
|
||||||
":images/fallback/light/big/state-error.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackAvailabilityThemeIconsLightSmall = {
|
|
||||||
":images/fallback/light/small/online.svg",
|
|
||||||
":images/fallback/light/small/away.svg",
|
|
||||||
":images/fallback/light/small/absent.svg",
|
|
||||||
":images/fallback/light/small/busy.svg",
|
|
||||||
":images/fallback/light/small/chatty.svg",
|
|
||||||
":images/fallback/light/small/invisible.svg",
|
|
||||||
":images/fallback/light/small/offline.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackSubscriptionStateThemeIconsLightSmall = {
|
|
||||||
":images/fallback/light/small/edit-none.svg",
|
|
||||||
":images/fallback/light/small/arrow-down-double.svg",
|
|
||||||
":images/fallback/light/small/arrow-up-double.svg",
|
|
||||||
":images/fallback/light/small/dialog-ok.svg",
|
|
||||||
":images/fallback/light/small/question.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackConnectionStateThemeIconsLightSmall = {
|
|
||||||
":images/fallback/light/small/state-offline.svg",
|
|
||||||
":images/fallback/light/small/state-sync.svg",
|
|
||||||
":images/fallback/light/small/state-ok.svg",
|
|
||||||
":images/fallback/light/small/state-error.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackAvailabilityThemeIconsDarkBig = {
|
|
||||||
":images/fallback/dark/big/online.svg",
|
|
||||||
":images/fallback/dark/big/away.svg",
|
|
||||||
":images/fallback/dark/big/absent.svg",
|
|
||||||
":images/fallback/dark/big/busy.svg",
|
|
||||||
":images/fallback/dark/big/chatty.svg",
|
|
||||||
":images/fallback/dark/big/invisible.svg",
|
|
||||||
":images/fallback/dark/big/offline.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackSubscriptionStateThemeIconsDarkBig = {
|
|
||||||
":images/fallback/dark/big/edit-none.svg",
|
|
||||||
":images/fallback/dark/big/arrow-down-double.svg",
|
|
||||||
":images/fallback/dark/big/arrow-up-double.svg",
|
|
||||||
":images/fallback/dark/big/dialog-ok.svg",
|
|
||||||
":images/fallback/dark/big/question.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackConnectionStateThemeIconsDarkBig = {
|
|
||||||
":images/fallback/dark/big/state-offline.svg",
|
|
||||||
":images/fallback/dark/big/state-sync.svg",
|
|
||||||
":images/fallback/dark/big/state-ok.svg",
|
|
||||||
":images/fallback/dark/big/state-error.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackAvailabilityThemeIconsDarkSmall = {
|
|
||||||
":images/fallback/dark/small/online.svg",
|
|
||||||
":images/fallback/dark/small/away.svg",
|
|
||||||
":images/fallback/dark/small/absent.svg",
|
|
||||||
":images/fallback/dark/small/busy.svg",
|
|
||||||
":images/fallback/dark/small/chatty.svg",
|
|
||||||
":images/fallback/dark/small/invisible.svg",
|
|
||||||
":images/fallback/dark/small/offline.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackSubscriptionStateThemeIconsDarkSmall = {
|
|
||||||
":images/fallback/dark/small/edit-none.svg",
|
|
||||||
":images/fallback/dark/small/arrow-down-double.svg",
|
|
||||||
":images/fallback/dark/small/arrow-up-double.svg",
|
|
||||||
":images/fallback/dark/small/dialog-ok.svg",
|
|
||||||
":images/fallback/dark/small/question.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::deque<QString> fallbackConnectionStateThemeIconsDarkSmall = {
|
|
||||||
":images/fallback/dark/small/state-offline.svg",
|
|
||||||
":images/fallback/dark/small/state-sync.svg",
|
|
||||||
":images/fallback/dark/small/state-ok.svg",
|
|
||||||
":images/fallback/dark/small/state-error.svg"
|
|
||||||
};
|
|
||||||
|
|
||||||
QIcon availabilityIcon(Availability av, bool big = false);
|
|
||||||
QIcon subscriptionStateIcon(SubscriptionState ss, bool big = false);
|
|
||||||
QIcon connectionStateIcon(ConnectionState cs, bool big = false);
|
|
||||||
QIcon icon(const QString& name, bool big = false);
|
|
||||||
QString iconPath(const QString& name, bool big = false);
|
|
||||||
|
|
||||||
static const std::map<QString, std::pair<QString, QString>> icons = {
|
|
||||||
{"user-online", {"user-online", "online"}},
|
|
||||||
{"user-away", {"user-away", "away"}},
|
|
||||||
{"user-away-extended", {"user-away-extended", "absent"}},
|
|
||||||
{"user-busy", {"user-busy", "busy"}},
|
|
||||||
{"user-chatty", {"chatty", "chatty"}},
|
|
||||||
{"user-invisible", {"user-invisible", "invisible"}},
|
|
||||||
{"user-offline", {"offline", "offline"}},
|
|
||||||
{"edit-none", {"edit-none", "edit-none"}},
|
|
||||||
{"arrow-down-double", {"arrow-down-double", "arrow-down-double"}},
|
|
||||||
{"arrow-up-double", {"arrow-up-double", "arrow-up-double"}},
|
|
||||||
{"dialog-ok", {"dialog-ok", "dialog-ok"}},
|
|
||||||
{"question", {"question", "question"}},
|
|
||||||
{"state-offline", {"state-offline", "state-offline"}},
|
|
||||||
{"state-sync", {"state-sync", "state-sync"}},
|
|
||||||
{"state-ok", {"state-ok", "state-ok"}},
|
|
||||||
{"state-error", {"state-error", "state-error"}},
|
|
||||||
|
|
||||||
{"edit-copy", {"edit-copy", "copy"}},
|
|
||||||
{"edit-delete", {"edit-delete", "edit-delete"}},
|
|
||||||
{"edit-rename", {"edit-rename", "edit-rename"}},
|
|
||||||
{"mail-message", {"mail-message", "mail-message"}},
|
|
||||||
{"mail-attachment", {"mail-attachment", "mail-attachment"}},
|
|
||||||
{"network-connect", {"network-connect", "network-connect"}},
|
|
||||||
{"network-disconnect", {"network-disconnect", "network-disconnect"}},
|
|
||||||
{"news-subscribe", {"news-subscribe", "news-subscribe"}},
|
|
||||||
{"news-unsubscribe", {"news-unsubscribe", "news-unsubscribe"}},
|
|
||||||
{"view-refresh", {"view-refresh", "view-refresh"}},
|
|
||||||
{"send", {"document-send", "send"}},
|
|
||||||
{"clean", {"edit-clear-all", "clean"}},
|
|
||||||
{"user", {"user", "user"}},
|
|
||||||
{"user-properties", {"user-properties", "user-properties"}},
|
|
||||||
{"group", {"group", "group"}},
|
|
||||||
{"group-new", {"resurce-group-new", "group-new"}},
|
|
||||||
{"favorite", {"favorite", "favorite"}},
|
|
||||||
{"unfavorite", {"draw-star", "unfavorite"}},
|
|
||||||
{"list-add", {"list-add", "add"}},
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // GLOBAL_H
|
|
156
main.cpp
@ -1,156 +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 "ui/squawk.h"
|
|
||||||
#include "core/squawk.h"
|
|
||||||
#include "signalcatcher.h"
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <QLibraryInfo>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
|
||||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
|
||||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
|
||||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
SignalCatcher sc(&app);
|
|
||||||
|
|
||||||
QApplication::setApplicationName("squawk");
|
|
||||||
QApplication::setApplicationDisplayName("Squawk");
|
|
||||||
QApplication::setApplicationVersion("0.1.3");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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, qOverload<const QString&, const Shared::Message&>(&Squawk::sendMessage),
|
|
||||||
squawk, qOverload<const QString&, const Shared::Message&>(&Core::Squawk::sendMessage));
|
|
||||||
QObject::connect(&w, qOverload<const QString&, const Shared::Message&, const QString&>(&Squawk::sendMessage),
|
|
||||||
squawk, qOverload<const QString&, const Shared::Message&, const QString&>(&Core::Squawk::sendMessage));
|
|
||||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
|
||||||
QObject::connect(&w, &Squawk::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::fileLocalPathRequest, squawk, &Core::Squawk::fileLocalPathRequest);
|
|
||||||
QObject::connect(&w, &Squawk::downloadFileRequest, squawk, &Core::Squawk::downloadFileRequest);
|
|
||||||
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(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::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::fileProgress);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::fileError);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
|
||||||
|
|
||||||
coreThread->start();
|
|
||||||
w.readSettings();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
7
main/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
main.cpp
|
||||||
|
application.cpp
|
||||||
|
application.h
|
||||||
|
dialogqueue.cpp
|
||||||
|
dialogqueue.h
|
||||||
|
)
|
566
main/application.cpp
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
// 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()
|
||||||
|
{
|
||||||
|
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
||||||
|
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
||||||
|
|
||||||
|
|
||||||
|
//connecting myself to the backed
|
||||||
|
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(&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, this, &Application::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 (!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::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::requestVCard, core, &Core::Squawk::requestVCard);
|
||||||
|
connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard);
|
||||||
|
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
|
||||||
|
dialogueQueue.setParentWidnow(squawk);
|
||||||
|
squawk->stateChanged(availability);
|
||||||
|
squawk->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkClosing()
|
||||||
|
{
|
||||||
|
dialogueQueue.setParentWidnow(nullptr);
|
||||||
|
|
||||||
|
if (!nowQuitting) {
|
||||||
|
disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyingSquawk = true;
|
||||||
|
squawk->deleteLater();
|
||||||
|
squawk = nullptr;
|
||||||
|
|
||||||
|
//for now
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkDestroyed() {
|
||||||
|
destroyingSquawk = false;
|
||||||
|
if (nowQuitting) {
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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.endGroup();
|
||||||
|
|
||||||
|
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
|
createMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::writeSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("availability", static_cast<int>(availability));
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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::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::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
roster.addContact(account, jid, group, data);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);}
|
118
main/application.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// 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 <QObject>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
|
||||||
|
#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 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 addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createMainWindow();
|
||||||
|
void subscribeConversation(Conversation* conv);
|
||||||
|
void checkForTheLastWindow();
|
||||||
|
void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = "");
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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
|
140
main/main.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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 "shared/pathcheck.h"
|
||||||
|
#include "main/application.h"
|
||||||
|
#include "core/signalcatcher.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
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::setOrganizationName("macaw.me");
|
||||||
|
QApplication::setApplicationDisplayName("Squawk");
|
||||||
|
QApplication::setApplicationVersion("0.2.2");
|
||||||
|
app.setDesktopFileName("squawk");
|
||||||
|
|
||||||
|
QTranslator qtTranslator;
|
||||||
|
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
|
app.installTranslator(&qtTranslator);
|
||||||
|
|
||||||
|
QTranslator myappTranslator;
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
bool found = false;
|
||||||
|
for (QString share : shares) {
|
||||||
|
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.installTranslator(&myappTranslator);
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":images/logo.svg", QSize(16, 16));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(24, 24));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(32, 32));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(48, 48));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(64, 64));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(96, 96));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(128, 128));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(256, 256));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(512, 512));
|
||||||
|
QApplication::setWindowIcon(icon);
|
||||||
|
|
||||||
|
new Shared::Global(); //translates enums
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QVariant vs = settings.value("style");
|
||||||
|
if (vs.isValid()) {
|
||||||
|
QString style = vs.toString().toLower();
|
||||||
|
if (style != "system") {
|
||||||
|
Shared::Global::setStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Shared::Global::supported("colorSchemeTools")) {
|
||||||
|
QVariant vt = settings.value("theme");
|
||||||
|
if (vt.isValid()) {
|
||||||
|
QString theme = vt.toString();
|
||||||
|
if (theme.toLower() != "system") {
|
||||||
|
Shared::Global::setTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString path = Shared::downloadsPathCheck();
|
||||||
|
if (path.size() > 0) {
|
||||||
|
settings.setValue("downloadsPath", path);
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't initialize directory for downloads, quitting";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Squawk* squawk = new Core::Squawk();
|
||||||
|
QThread* coreThread = new QThread();
|
||||||
|
squawk->moveToThread(coreThread);
|
||||||
|
|
||||||
|
Application application(squawk);
|
||||||
|
|
||||||
|
QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit);
|
||||||
|
|
||||||
|
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
||||||
|
QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop);
|
||||||
|
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
coreThread->start();
|
||||||
|
int result = app.exec();
|
||||||
|
|
||||||
|
if (coreThread->isRunning()) {
|
||||||
|
//coreThread->wait();
|
||||||
|
//todo if I uncomment that, the app will not quit if it has reconnected at least once
|
||||||
|
//it feels like a symptom of something badly desinged in the core thread
|
||||||
|
//need to investigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,20 @@
|
|||||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||||
pkgname=squawk
|
pkgname=squawk
|
||||||
pkgver=0.1.3
|
pkgver=0.2.2
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
||||||
arch=('i686' 'x86_64')
|
arch=('i686' 'x86_64')
|
||||||
url="https://git.macaw.me/blue/squawk"
|
url="https://git.macaw.me/blue/squawk"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
|
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
|
||||||
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
|
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost')
|
||||||
|
optdepends=('kwallet: secure password storage (requires rebuild)'
|
||||||
|
'kconfig: system themes support (requires rebuild)'
|
||||||
|
'kconfigwidgets: system themes support (requires rebuild)'
|
||||||
|
'kio: better show in folder action (requires rebuild)')
|
||||||
|
|
||||||
source=("$pkgname-$pkgver.tar.gz")
|
source=("$pkgname-$pkgver.tar.gz")
|
||||||
sha256sums=('adb172bb7d5b81bd9b83b192481a79ac985877e81604f401b3f2a08613b359bc')
|
sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e')
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||||
|
3
packaging/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
configure_file(squawk.desktop squawk.desktop COPYONLY)
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
@ -5,8 +5,10 @@ Version=1.0
|
|||||||
Name=Squawk
|
Name=Squawk
|
||||||
GenericName=Instant Messenger
|
GenericName=Instant Messenger
|
||||||
GenericName[ru]=Мгновенные сообщения
|
GenericName[ru]=Мгновенные сообщения
|
||||||
|
GenericName[pt_BR]=Mensageiro instantâneo
|
||||||
Comment=XMPP (Jabber) instant messenger client
|
Comment=XMPP (Jabber) instant messenger client
|
||||||
Comment[ru]=XMPP (Jabber) клиент обмена мгновенными сообщениями
|
Comment[ru]=XMPP (Jabber) клиент обмена мгновенными сообщениями
|
||||||
|
Comment[pt_BR]=Cliente de mensagem instantânea XMPP (Jabber)
|
||||||
Exec=squawk %u
|
Exec=squawk %u
|
||||||
Icon=squawk
|
Icon=squawk
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
14
plugins/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
if (WITH_KIO)
|
||||||
|
add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp)
|
||||||
|
target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets)
|
||||||
|
|
||||||
|
install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
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})
|
||||||
|
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;
|
||||||
|
}
|
26
plugins/openfilemanagerwindowjob.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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 <QObject>
|
||||||
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
|
||||||
|
extern "C" void highlightInFileManager(const QUrl& url) {
|
||||||
|
KIO::OpenFileManagerWindowJob* job = KIO::highlightInFileManager({url});
|
||||||
|
QObject::connect(job, &KIO::OpenFileManagerWindowJob::result, job, &KIO::OpenFileManagerWindowJob::deleteLater);
|
||||||
|
}
|
59
resources/CMakeLists.txt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
target_sources(squawk PRIVATE resources.qrc)
|
||||||
|
|
||||||
|
configure_file(images/logo.svg squawk.svg COPYONLY)
|
||||||
|
configure_file(squawk.rc squawk.rc COPYONLY)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(CONVERT_BIN magick convert)
|
||||||
|
else(WIN32)
|
||||||
|
set(CONVERT_BIN convert)
|
||||||
|
endif(WIN32)
|
||||||
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND ${CONVERT_BIN} -background none -size 256x256 squawk.svg squawk256.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
execute_process(COMMAND ${CONVERT_BIN} squawk48.png squawk64.png squawk256.png squawk.ico WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc")
|
||||||
|
set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE)
|
||||||
|
target_sources(squawk PRIVATE ${SQUAWK_WIN_RC})
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset")
|
||||||
|
execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE squawk.icns)
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE)
|
||||||
|
set(APP_ICON_MACOSX ${CMAKE_CURRENT_BINARY_DIR}/squawk.icns)
|
||||||
|
set(APP_ICON_MACOSX ${APP_ICON_MACOSX} PARENT_SCOPE)
|
||||||
|
target_sources(squawk PRIVATE ${APP_ICON_MACOSX})
|
||||||
|
set_source_files_properties(${APP_ICON_MACOSX} TARGET_DIRECTORY squawk PROPERTIES
|
||||||
|
MACOSX_PACKAGE_LOCATION "Resources")
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
if (APPLE)
|
||||||
|
set_target_properties(squawk PROPERTIES
|
||||||
|
MACOSX_BUNDLE_EXECUTABLE_NAME "Squawk"
|
||||||
|
MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "Squawk"
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in)
|
||||||
|
endif(APPLE)
|
||||||
|
endif()
|
||||||
|
endif (APPLE)
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
|
||||||
|
|
11
resources/images/fallback/dark/big/document-preview.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 807 B |
21
resources/images/fallback/dark/big/folder.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
||||||
|
<defs id="defs5455">
|
||||||
|
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
||||||
|
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
||||||
|
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
||||||
|
</defs>
|
||||||
|
<metadata id="metadata5458"/>
|
||||||
|
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
||||||
|
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
||||||
|
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
12
resources/images/fallback/dark/small/document-preview.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1010 B |
13
resources/images/fallback/dark/small/folder.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#232629;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 609 B |
11
resources/images/fallback/light/big/document-preview.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 4 3 L 4 19 L 15 19 L 15 18 L 5 18 L 5 4 L 13 4 L 13 8 L 17 8 L 17 16 L 18 16 L 18 7 L 14 3 L 4 3 Z M 13 11 C 11.338 11 10 12.338 10 14 C 10 15.662 11.338 17 13 17 C 13.6494 17 14.2464 16.7914 14.7363 16.4434 L 17.293 19 L 18 18.293 L 15.4434 15.7363 C 15.7914 15.2464 16 14.6494 16 14 C 16 12.338 14.662 11 13 11 Z M 13 12 C 14.108 12 15 12.892 15 14 C 15 15.108 14.108 16 13 16 C 11.892 16 11 15.108 11 14 C 11 12.892 11.892 12 13 12 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 807 B |
21
resources/images/fallback/light/big/folder.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<svg width="32" version="1.1" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 32 32" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
|
||||||
|
<defs id="defs5455">
|
||||||
|
<linearGradient inkscape:collect="always" id="linearGradient4172-5">
|
||||||
|
<stop style="stop-color:#3daee9" id="stop4174-6"/>
|
||||||
|
<stop offset="1" style="stop-color:#6cc1ef" id="stop4176-6"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4172-5" id="linearGradient4342" y1="29" y2="8" x2="0" gradientUnits="userSpaceOnUse"/>
|
||||||
|
</defs>
|
||||||
|
<metadata id="metadata5458"/>
|
||||||
|
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" transform="matrix(1 0 0 1 -384.57143 -515.798)">
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#147eb8;fill-rule:evenodd" id="path4308" d="m 386.57144,518.798 0,7 0,1 28,0 0,-6 -14.00001,0 -2,-2 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill-opacity:0.235294;fill-rule:evenodd" id="path4306" d="m 397.57143,523.798 -1.99999,1 -9,0 0,1 6.99999,0 3,0 z"/>
|
||||||
|
<path style="fill:url(#linearGradient4342)" id="rect4294" d="M 13 8 L 11 10 L 2 10 L 1 10 L 1 29 L 12 29 L 13 29 L 31 29 L 31 8 L 13 8 z " transform="matrix(1 0 0 1 384.57143 515.798)"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4304" d="m 397.57143,523.798 -2,2 -10,0 0,1 11,0 z"/>
|
||||||
|
<path inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:0.235294;fill-rule:evenodd" id="path4310" d="m 398.57143,518.798 1,3 15.00001,0 0,-1 -14.00001,0 z"/>
|
||||||
|
<rect width="30" x="385.57144" y="543.79797" height="1" style="fill-opacity:0.235294" id="rect4292"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
12
resources/images/fallback/light/small/document-preview.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE svg>
|
||||||
|
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 3 2 L 3 14 L 10 14 L 10 13 L 4 13 L 4 3 L 9 3 L 9 6 L 12 6 L 12 11 L 13 11 L 13 5 L 10 2 L 3 2 Z"/>
|
||||||
|
<path class="ColorScheme-Text" style="fill:currentColor; fill-opacity:1; stroke:none" d="M 8.48828 7 C 7.10757 7 5.98828 8.11929 5.98828 9.5 C 5.98828 10.8807 7.10757 12 8.48828 12 C 8.97811 11.9992 9.45691 11.8546 9.86523 11.584 L 12.2813 14 L 12.9883 13.293 L 10.5723 10.877 C 10.8428 10.4686 10.9875 9.98983 10.9883 9.5 C 10.9883 8.11929 9.86899 7 8.48828 7 Z M 8.48828 8 C 9.31671 8 9.98828 8.67157 9.98828 9.5 C 9.98828 10.3284 9.31671 11 8.48828 11 C 7.65985 11 6.98828 10.3284 6.98828 9.5 C 6.98828 8.67157 7.65985 8 8.48828 8 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1010 B |
13
resources/images/fallback/light/small/folder.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#eff0f1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 2 2 L 2 3 L 2 6 L 2 7 L 2 13 L 2 14 L 14 14 L 14 13 L 14 6 L 14 5 L 14 4 L 9.0078125 4 L 7.0078125 2 L 7 2.0078125 L 7 2 L 3 2 L 2 2 z M 3 3 L 6.5917969 3 L 7.59375 4 L 7 4 L 7 4.0078125 L 6.9921875 4 L 4.9921875 6 L 3 6 L 3 3 z M 3 7 L 13 7 L 13 13 L 3 13 L 3 7 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 609 B |
@ -40,6 +40,8 @@
|
|||||||
<file>images/fallback/dark/big/favorite.svg</file>
|
<file>images/fallback/dark/big/favorite.svg</file>
|
||||||
<file>images/fallback/dark/big/unfavorite.svg</file>
|
<file>images/fallback/dark/big/unfavorite.svg</file>
|
||||||
<file>images/fallback/dark/big/add.svg</file>
|
<file>images/fallback/dark/big/add.svg</file>
|
||||||
|
<file>images/fallback/dark/big/folder.svg</file>
|
||||||
|
<file>images/fallback/dark/big/document-preview.svg</file>
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/dark/small/absent.svg</file>
|
<file>images/fallback/dark/small/absent.svg</file>
|
||||||
@ -80,6 +82,8 @@
|
|||||||
<file>images/fallback/dark/small/favorite.svg</file>
|
<file>images/fallback/dark/small/favorite.svg</file>
|
||||||
<file>images/fallback/dark/small/unfavorite.svg</file>
|
<file>images/fallback/dark/small/unfavorite.svg</file>
|
||||||
<file>images/fallback/dark/small/add.svg</file>
|
<file>images/fallback/dark/small/add.svg</file>
|
||||||
|
<file>images/fallback/dark/small/folder.svg</file>
|
||||||
|
<file>images/fallback/dark/small/document-preview.svg</file>
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/light/big/absent.svg</file>
|
<file>images/fallback/light/big/absent.svg</file>
|
||||||
@ -120,6 +124,8 @@
|
|||||||
<file>images/fallback/light/big/favorite.svg</file>
|
<file>images/fallback/light/big/favorite.svg</file>
|
||||||
<file>images/fallback/light/big/unfavorite.svg</file>
|
<file>images/fallback/light/big/unfavorite.svg</file>
|
||||||
<file>images/fallback/light/big/add.svg</file>
|
<file>images/fallback/light/big/add.svg</file>
|
||||||
|
<file>images/fallback/light/big/folder.svg</file>
|
||||||
|
<file>images/fallback/light/big/document-preview.svg</file>
|
||||||
|
|
||||||
|
|
||||||
<file>images/fallback/light/small/absent.svg</file>
|
<file>images/fallback/light/small/absent.svg</file>
|
||||||
@ -160,5 +166,7 @@
|
|||||||
<file>images/fallback/light/small/favorite.svg</file>
|
<file>images/fallback/light/small/favorite.svg</file>
|
||||||
<file>images/fallback/light/small/unfavorite.svg</file>
|
<file>images/fallback/light/small/unfavorite.svg</file>
|
||||||
<file>images/fallback/light/small/add.svg</file>
|
<file>images/fallback/light/small/add.svg</file>
|
||||||
|
<file>images/fallback/light/small/folder.svg</file>
|
||||||
|
<file>images/fallback/light/small/document-preview.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
1
resources/squawk.rc
Normal file
@ -0,0 +1 @@
|
|||||||
|
IDI_ICON1 ICON "squawk.ico"
|
21
shared/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
enums.h
|
||||||
|
global.cpp
|
||||||
|
global.h
|
||||||
|
exception.cpp
|
||||||
|
exception.h
|
||||||
|
icons.cpp
|
||||||
|
icons.h
|
||||||
|
message.cpp
|
||||||
|
message.h
|
||||||
|
messageinfo.cpp
|
||||||
|
messageinfo.h
|
||||||
|
order.h
|
||||||
|
shared.h
|
||||||
|
utils.cpp
|
||||||
|
utils.h
|
||||||
|
vcard.cpp
|
||||||
|
vcard.h
|
||||||
|
pathcheck.cpp
|
||||||
|
pathcheck.h
|
||||||
|
)
|
121
shared/enums.h
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_ENUMS_H
|
||||||
|
#define SHARED_ENUMS_H
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
enum class ConnectionState {
|
||||||
|
disconnected,
|
||||||
|
connecting,
|
||||||
|
connected,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(ConnectionState)
|
||||||
|
static const std::deque<QString> connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"};
|
||||||
|
static const ConnectionState ConnectionStateHighest = ConnectionState::error;
|
||||||
|
static const ConnectionState ConnectionStateLowest = ConnectionState::disconnected;
|
||||||
|
|
||||||
|
enum class Availability {
|
||||||
|
online,
|
||||||
|
away,
|
||||||
|
extendedAway,
|
||||||
|
busy,
|
||||||
|
chatty,
|
||||||
|
invisible,
|
||||||
|
offline
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(Availability)
|
||||||
|
static const Availability AvailabilityHighest = Availability::offline;
|
||||||
|
static const Availability AvailabilityLowest = Availability::online;
|
||||||
|
static const std::deque<QString> availabilityThemeIcons = {
|
||||||
|
"user-online",
|
||||||
|
"user-away",
|
||||||
|
"user-away-extended",
|
||||||
|
"user-busy",
|
||||||
|
"chatty",
|
||||||
|
"user-invisible",
|
||||||
|
"user-offline"
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SubscriptionState {
|
||||||
|
none,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
both,
|
||||||
|
unknown
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(SubscriptionState)
|
||||||
|
static const SubscriptionState SubscriptionStateHighest = SubscriptionState::unknown;
|
||||||
|
static const SubscriptionState SubscriptionStateLowest = SubscriptionState::none;
|
||||||
|
static const std::deque<QString> subscriptionStateThemeIcons = {"edit-none", "arrow-down-double", "arrow-up-double", "dialog-ok", "question"};
|
||||||
|
|
||||||
|
enum class Affiliation {
|
||||||
|
unspecified,
|
||||||
|
outcast,
|
||||||
|
nobody,
|
||||||
|
member,
|
||||||
|
admin,
|
||||||
|
owner
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(Affiliation)
|
||||||
|
static const Affiliation AffiliationHighest = Affiliation::owner;
|
||||||
|
static const Affiliation AffiliationLowest = Affiliation::unspecified;
|
||||||
|
|
||||||
|
enum class Role {
|
||||||
|
unspecified,
|
||||||
|
nobody,
|
||||||
|
visitor,
|
||||||
|
participant,
|
||||||
|
moderator
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(Role)
|
||||||
|
static const Role RoleHighest = Role::moderator;
|
||||||
|
static const Role RoleLowest = Role::unspecified;
|
||||||
|
|
||||||
|
enum class Avatar {
|
||||||
|
empty,
|
||||||
|
autocreated,
|
||||||
|
valid
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(Avatar)
|
||||||
|
static const Avatar AvatarHighest = Avatar::valid;
|
||||||
|
static const Avatar AvatarLowest = Avatar::empty;
|
||||||
|
|
||||||
|
|
||||||
|
static const std::deque<QString> messageStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"};
|
||||||
|
|
||||||
|
enum class AccountPassword {
|
||||||
|
plain,
|
||||||
|
jammed,
|
||||||
|
alwaysAsk,
|
||||||
|
kwallet
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(AccountPassword)
|
||||||
|
static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet;
|
||||||
|
static const AccountPassword AccountPasswordLowest = AccountPassword::plain;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // SHARED_ENUMS_H
|
@ -28,5 +28,6 @@ Utils::Exception::~Exception()
|
|||||||
|
|
||||||
const char* Utils::Exception::what() const noexcept( true )
|
const char* Utils::Exception::what() const noexcept( true )
|
||||||
{
|
{
|
||||||
return getMessage().c_str();
|
std::string* msg = new std::string(getMessage());
|
||||||
|
return msg->c_str();
|
||||||
}
|
}
|
372
shared/global.cpp
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
/*
|
||||||
|
* 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 "global.h"
|
||||||
|
|
||||||
|
#include "enums.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
|
||||||
|
Shared::Global* Shared::Global::instance = 0;
|
||||||
|
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
||||||
|
|
||||||
|
#ifdef WITH_KIO
|
||||||
|
QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob");
|
||||||
|
Shared::Global::HighlightInFileManager Shared::Global::hfm = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_KCONFIG
|
||||||
|
QLibrary Shared::Global::colorSchemeTools("colorSchemeTools");
|
||||||
|
Shared::Global::CreatePreview Shared::Global::createPreview = 0;
|
||||||
|
Shared::Global::DeletePreview Shared::Global::deletePreview = 0;
|
||||||
|
Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0;
|
||||||
|
Shared::Global::CreatePalette Shared::Global::createPalette = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Shared::Global::Global():
|
||||||
|
availability({
|
||||||
|
tr("Online", "Availability"),
|
||||||
|
tr("Away", "Availability"),
|
||||||
|
tr("Absent", "Availability"),
|
||||||
|
tr("Busy", "Availability"),
|
||||||
|
tr("Chatty", "Availability"),
|
||||||
|
tr("Invisible", "Availability"),
|
||||||
|
tr("Offline", "Availability")
|
||||||
|
}),
|
||||||
|
connectionState({
|
||||||
|
tr("Disconnected", "ConnectionState"),
|
||||||
|
tr("Connecting", "ConnectionState"),
|
||||||
|
tr("Connected", "ConnectionState"),
|
||||||
|
tr("Error", "ConnectionState")
|
||||||
|
}),
|
||||||
|
subscriptionState({
|
||||||
|
tr("None", "SubscriptionState"),
|
||||||
|
tr("From", "SubscriptionState"),
|
||||||
|
tr("To", "SubscriptionState"),
|
||||||
|
tr("Both", "SubscriptionState"),
|
||||||
|
tr("Unknown", "SubscriptionState")
|
||||||
|
}),
|
||||||
|
affiliation({
|
||||||
|
tr("Unspecified", "Affiliation"),
|
||||||
|
tr("Outcast", "Affiliation"),
|
||||||
|
tr("Nobody", "Affiliation"),
|
||||||
|
tr("Member", "Affiliation"),
|
||||||
|
tr("Admin", "Affiliation"),
|
||||||
|
tr("Owner", "Affiliation")
|
||||||
|
}),
|
||||||
|
role({
|
||||||
|
tr("Unspecified", "Role"),
|
||||||
|
tr("Nobody", "Role"),
|
||||||
|
tr("Visitor", "Role"),
|
||||||
|
tr("Participant", "Role"),
|
||||||
|
tr("Moderator", "Role")
|
||||||
|
}),
|
||||||
|
messageState({
|
||||||
|
tr("Pending", "MessageState"),
|
||||||
|
tr("Sent", "MessageState"),
|
||||||
|
tr("Delivered", "MessageState"),
|
||||||
|
tr("Error", "MessageState")
|
||||||
|
}),
|
||||||
|
accountPassword({
|
||||||
|
tr("Plain", "AccountPassword"),
|
||||||
|
tr("Jammed", "AccountPassword"),
|
||||||
|
tr("Always Ask", "AccountPassword"),
|
||||||
|
tr("KWallet", "AccountPassword")
|
||||||
|
}),
|
||||||
|
accountPasswordDescription({
|
||||||
|
tr("Your password is going to be stored in config file in plain text", "AccountPasswordDescription"),
|
||||||
|
tr("Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"),
|
||||||
|
tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"),
|
||||||
|
tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription")
|
||||||
|
}),
|
||||||
|
defaultSystemStyle(QApplication::style()->objectName()),
|
||||||
|
defaultSystemPalette(QApplication::palette()),
|
||||||
|
pluginSupport({
|
||||||
|
{"KWallet", false},
|
||||||
|
{"openFileManagerWindowJob", false},
|
||||||
|
{"colorSchemeTools", false}
|
||||||
|
}),
|
||||||
|
fileCache()
|
||||||
|
{
|
||||||
|
if (instance != 0) {
|
||||||
|
throw 551;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
|
||||||
|
#ifdef WITH_KIO
|
||||||
|
openFileManagerWindowJob.load();
|
||||||
|
if (openFileManagerWindowJob.isLoaded()) {
|
||||||
|
hfm = (HighlightInFileManager) openFileManagerWindowJob.resolve("highlightInFileManager");
|
||||||
|
if (hfm) {
|
||||||
|
setSupported("openFileManagerWindowJob", true);
|
||||||
|
qDebug() << "KIO::OpenFileManagerWindow support enabled";
|
||||||
|
} else {
|
||||||
|
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't resolve required methods in the library";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_KCONFIG
|
||||||
|
colorSchemeTools.load();
|
||||||
|
if (colorSchemeTools.isLoaded()) {
|
||||||
|
createPreview = (CreatePreview) colorSchemeTools.resolve("createPreview");
|
||||||
|
deletePreview = (DeletePreview) colorSchemeTools.resolve("deletePreview");
|
||||||
|
colorSchemeName = (ColorSchemeName) colorSchemeTools.resolve("colorSchemeName");
|
||||||
|
createPalette = (CreatePalette) colorSchemeTools.resolve("createPalette");
|
||||||
|
if (createPreview && deletePreview && colorSchemeName && createPalette) {
|
||||||
|
setSupported("colorSchemeTools", true);
|
||||||
|
qDebug() << "Color Schemes support enabled";
|
||||||
|
} else {
|
||||||
|
qDebug() << "Color Schemes support disabled: couldn't resolve required methods in the library";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << "Color Schemes support disabled: couldn't load the library" << colorSchemeTools.errorString();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const QSize defaultIconFileInfoHeight(50, 50);
|
||||||
|
Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
||||||
|
{
|
||||||
|
std::map<QString, FileInfo>::const_iterator itr = instance->fileCache.find(path);
|
||||||
|
if (itr == instance->fileCache.end()) {
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType type = db.mimeTypeForFile(path);
|
||||||
|
QStringList parts = type.name().split("/");
|
||||||
|
QString big = parts.front();
|
||||||
|
QFileInfo info(path);
|
||||||
|
|
||||||
|
FileInfo::Preview p = FileInfo::Preview::none;
|
||||||
|
QSize size;
|
||||||
|
if (big == "image") {
|
||||||
|
QMovie mov(path);
|
||||||
|
if (mov.isValid() && mov.frameCount() > 1) {
|
||||||
|
p = FileInfo::Preview::animation;
|
||||||
|
} else {
|
||||||
|
p = FileInfo::Preview::picture;
|
||||||
|
}
|
||||||
|
QImageReader img(path);
|
||||||
|
size = img.size();
|
||||||
|
// } else if (big == "video") {
|
||||||
|
// p = FileInfo::Preview::movie;
|
||||||
|
// QMovie mov(path);
|
||||||
|
// size = mov.scaledSize();
|
||||||
|
// qDebug() << mov.isValid();
|
||||||
|
} else {
|
||||||
|
size = defaultIconFileInfoHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.absoluteFilePath(), info.fileName(), size, type, p}))).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Shared::Global * Shared::Global::getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Message::State rl)
|
||||||
|
{
|
||||||
|
return instance->messageState[static_cast<int>(rl)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::Affiliation af)
|
||||||
|
{
|
||||||
|
return instance->affiliation[static_cast<int>(af)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::Availability av)
|
||||||
|
{
|
||||||
|
return instance->availability[static_cast<int>(av)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::ConnectionState cs)
|
||||||
|
{
|
||||||
|
return instance->connectionState[static_cast<int>(cs)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::Role rl)
|
||||||
|
{
|
||||||
|
return instance->role[static_cast<int>(rl)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::SubscriptionState ss)
|
||||||
|
{
|
||||||
|
return instance->subscriptionState[static_cast<int>(ss)];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getName(Shared::AccountPassword ap)
|
||||||
|
{
|
||||||
|
return instance->accountPassword[static_cast<int>(ap)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Global::setSupported(const QString& pluginName, bool support)
|
||||||
|
{
|
||||||
|
std::map<QString, bool>::iterator itr = instance->pluginSupport.find(pluginName);
|
||||||
|
if (itr != instance->pluginSupport.end()) {
|
||||||
|
itr->second = support;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Global::supported(const QString& pluginName)
|
||||||
|
{
|
||||||
|
std::map<QString, bool>::iterator itr = instance->pluginSupport.find(pluginName);
|
||||||
|
if (itr != instance->pluginSupport.end()) {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getDescription(Shared::AccountPassword ap)
|
||||||
|
{
|
||||||
|
return instance->accountPasswordDescription[static_cast<int>(ap)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const QStringList query = {"query", "default", "inode/directory"};
|
||||||
|
static const QRegularExpression dolphinReg("[Dd]olphin");
|
||||||
|
static const QRegularExpression nautilusReg("[Nn]autilus");
|
||||||
|
static const QRegularExpression cajaReg("[Cc]aja");
|
||||||
|
static const QRegularExpression nemoReg("[Nn]emo");
|
||||||
|
static const QRegularExpression konquerorReg("kfmclient");
|
||||||
|
static const QRegularExpression pcmanfmQtReg("pcmanfm-qt");
|
||||||
|
static const QRegularExpression pcmanfmReg("pcmanfm");
|
||||||
|
static const QRegularExpression thunarReg("thunar");
|
||||||
|
|
||||||
|
void Shared::Global::highlightInFileManager(const QString& path)
|
||||||
|
{
|
||||||
|
#ifdef WITH_KIO
|
||||||
|
if (supported("openFileManagerWindowJob")) {
|
||||||
|
hfm(path);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: KIO plugin isn't loaded, trying fallback";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: squawk wasn't compiled to support it, trying fallback";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QFileInfo info = path;
|
||||||
|
if (info.exists()) {
|
||||||
|
QProcess proc;
|
||||||
|
proc.start("xdg-mime", query);
|
||||||
|
proc.waitForFinished();
|
||||||
|
QString output = proc.readLine().simplified();
|
||||||
|
|
||||||
|
QString folder;
|
||||||
|
if (info.isDir()) {
|
||||||
|
folder = info.canonicalFilePath();
|
||||||
|
} else {
|
||||||
|
folder = info.canonicalPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output.contains(dolphinReg)) {
|
||||||
|
//there is a bug on current (21.04.0) dolphin, it works correct only if you already have dolphin launched
|
||||||
|
proc.startDetached("dolphin", QStringList() << "--select" << info.canonicalFilePath());
|
||||||
|
//KIO::highlightInFileManager({QUrl(info.canonicalFilePath())});
|
||||||
|
} else if (output.contains(nautilusReg)) {
|
||||||
|
proc.startDetached("nautilus", QStringList() << "--select" << info.canonicalFilePath()); //this worked on nautilus
|
||||||
|
} else if (output.contains(cajaReg)) {
|
||||||
|
proc.startDetached("caja", QStringList() << folder); //caja doesn't seem to support file selection command line, gonna just open directory
|
||||||
|
} else if (output.contains(nemoReg)) {
|
||||||
|
proc.startDetached("nemo", QStringList() << info.canonicalFilePath()); //nemo supports selecting files without keys
|
||||||
|
} else if (output.contains(konquerorReg)) {
|
||||||
|
proc.startDetached("konqueror", QStringList() << "--select" << info.canonicalFilePath()); //this worked on konqueror
|
||||||
|
} else if (output.contains(pcmanfmQtReg)) {
|
||||||
|
proc.startDetached("pcmanfm-qt", QStringList() << folder); //pcmanfm-qt doesn't seem to support open with selection, gonna just open directory
|
||||||
|
} else if (output.contains(pcmanfmReg)) {
|
||||||
|
proc.startDetached("pcmanfm", QStringList() << folder); //pcmanfm also doesn't seem to support open with selection, gonna just open directory
|
||||||
|
} else if (output.contains(thunarReg)) {
|
||||||
|
proc.startDetached("thunar", QStringList() << folder); //thunar doesn't seem to support open with selection, gonna just open directory
|
||||||
|
} else {
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Shared::Global::createThemePreview(const QString& path)
|
||||||
|
{
|
||||||
|
if (supported("colorSchemeTools")) {
|
||||||
|
QIcon* icon = createPreview(path);
|
||||||
|
QIcon localIcon = *icon;
|
||||||
|
deletePreview(icon);
|
||||||
|
return localIcon;
|
||||||
|
} else {
|
||||||
|
return QIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Global::getColorSchemeName(const QString& path)
|
||||||
|
{
|
||||||
|
if (supported("colorSchemeTools")) {
|
||||||
|
QString res;
|
||||||
|
colorSchemeName(path, res);
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Global::setTheme(const QString& path)
|
||||||
|
{
|
||||||
|
if (supported("colorSchemeTools")) {
|
||||||
|
if (path.toLower() == "system") {
|
||||||
|
QApplication::setPalette(getInstance()->defaultSystemPalette);
|
||||||
|
} else {
|
||||||
|
QPalette pallete;
|
||||||
|
createPalette(path, pallete);
|
||||||
|
QApplication::setPalette(pallete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Global::setStyle(const QString& style)
|
||||||
|
{
|
||||||
|
if (style.toLower() == "system") {
|
||||||
|
QApplication::setStyle(getInstance()->defaultSystemStyle);
|
||||||
|
} else {
|
||||||
|
QApplication::setStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FROM_INT_INPL(Enum) \
|
||||||
|
template<> \
|
||||||
|
Enum Shared::Global::fromInt(int src) \
|
||||||
|
{ \
|
||||||
|
if (src < static_cast<int>(Enum##Lowest) && src > static_cast<int>(Enum##Highest)) { \
|
||||||
|
throw EnumOutOfRange(#Enum); \
|
||||||
|
} \
|
||||||
|
return static_cast<Enum>(src); \
|
||||||
|
} \
|
||||||
|
template<> \
|
||||||
|
Enum Shared::Global::fromInt(unsigned int src) {return fromInt<Enum>(static_cast<int>(src));}
|
||||||
|
|
||||||
|
FROM_INT_INPL(Shared::Message::State)
|
||||||
|
FROM_INT_INPL(Shared::Affiliation)
|
||||||
|
FROM_INT_INPL(Shared::ConnectionState)
|
||||||
|
FROM_INT_INPL(Shared::Role)
|
||||||
|
FROM_INT_INPL(Shared::SubscriptionState)
|
||||||
|
FROM_INT_INPL(Shared::AccountPassword)
|
||||||
|
FROM_INT_INPL(Shared::Avatar)
|
||||||
|
FROM_INT_INPL(Shared::Availability)
|
152
shared/global.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_GLOBAL_H
|
||||||
|
#define SHARED_GLOBAL_H
|
||||||
|
|
||||||
|
#include "enums.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "exception.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMimeType>
|
||||||
|
#include <QMimeDatabase>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QMovie>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QLibrary>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
class Global {
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(Global)
|
||||||
|
public:
|
||||||
|
struct FileInfo {
|
||||||
|
enum class Preview {
|
||||||
|
none,
|
||||||
|
picture,
|
||||||
|
animation
|
||||||
|
};
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
QString name;
|
||||||
|
QSize size;
|
||||||
|
QMimeType mime;
|
||||||
|
Preview preview;
|
||||||
|
};
|
||||||
|
|
||||||
|
Global();
|
||||||
|
|
||||||
|
static Global* getInstance();
|
||||||
|
static QString getName(Availability av);
|
||||||
|
static QString getName(ConnectionState cs);
|
||||||
|
static QString getName(SubscriptionState ss);
|
||||||
|
static QString getName(Affiliation af);
|
||||||
|
static QString getName(Role rl);
|
||||||
|
static QString getName(Message::State rl);
|
||||||
|
static QString getName(AccountPassword ap);
|
||||||
|
|
||||||
|
static QString getDescription(AccountPassword ap);
|
||||||
|
|
||||||
|
const std::deque<QString> availability;
|
||||||
|
const std::deque<QString> connectionState;
|
||||||
|
const std::deque<QString> subscriptionState;
|
||||||
|
const std::deque<QString> affiliation;
|
||||||
|
const std::deque<QString> role;
|
||||||
|
const std::deque<QString> messageState;
|
||||||
|
const std::deque<QString> accountPassword;
|
||||||
|
|
||||||
|
const std::deque<QString> accountPasswordDescription;
|
||||||
|
|
||||||
|
const QString defaultSystemStyle;
|
||||||
|
const QPalette defaultSystemPalette;
|
||||||
|
|
||||||
|
static bool supported(const QString& pluginName);
|
||||||
|
static void setSupported(const QString& pluginName, bool support);
|
||||||
|
|
||||||
|
static const std::set<QString> supportedImagesExts;
|
||||||
|
|
||||||
|
static FileInfo getFileInfo(const QString& path);
|
||||||
|
static void highlightInFileManager(const QString& path);
|
||||||
|
static QIcon createThemePreview(const QString& path);
|
||||||
|
static QString getColorSchemeName(const QString& path);
|
||||||
|
static void setTheme(const QString& path);
|
||||||
|
static void setStyle(const QString& style);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static T fromInt(int src);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static T fromInt(unsigned int src);
|
||||||
|
|
||||||
|
class EnumOutOfRange:
|
||||||
|
public Utils::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EnumOutOfRange(const std::string& p_name):Exception(), name(p_name) {}
|
||||||
|
|
||||||
|
std::string getMessage() const{
|
||||||
|
return "An attempt to get enum " + name + " from integer out of range of that enum";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Global* instance;
|
||||||
|
|
||||||
|
std::map<QString, bool> pluginSupport;
|
||||||
|
std::map<QString, FileInfo> fileCache;
|
||||||
|
|
||||||
|
#ifdef WITH_KIO
|
||||||
|
static QLibrary openFileManagerWindowJob;
|
||||||
|
|
||||||
|
typedef void (*HighlightInFileManager)(const QUrl &);
|
||||||
|
|
||||||
|
static HighlightInFileManager hfm;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_KCONFIG
|
||||||
|
static QLibrary colorSchemeTools;
|
||||||
|
|
||||||
|
typedef QIcon* (*CreatePreview)(const QString&);
|
||||||
|
typedef void (*DeletePreview)(QIcon*);
|
||||||
|
typedef void (*ColorSchemeName)(const QString&, QString&);
|
||||||
|
typedef void (*CreatePalette)(const QString&, QPalette&);
|
||||||
|
|
||||||
|
static CreatePreview createPreview;
|
||||||
|
static DeletePreview deletePreview;
|
||||||
|
static ColorSchemeName colorSchemeName;
|
||||||
|
static CreatePalette createPalette;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARED_GLOBAL_H
|
96
shared/icons.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 "icons.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
QIcon Shared::availabilityIcon(Shared::Availability av, bool big)
|
||||||
|
{
|
||||||
|
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||||
|
big ?
|
||||||
|
Shared::fallbackAvailabilityThemeIconsDarkBig:
|
||||||
|
Shared::fallbackAvailabilityThemeIconsDarkSmall:
|
||||||
|
big ?
|
||||||
|
Shared::fallbackAvailabilityThemeIconsLightBig:
|
||||||
|
Shared::fallbackAvailabilityThemeIconsLightSmall;
|
||||||
|
|
||||||
|
return QIcon::fromTheme(availabilityThemeIcons[static_cast<int>(av)], QIcon(fallback[static_cast<int>(av)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Shared::subscriptionStateIcon(Shared::SubscriptionState ss, bool big)
|
||||||
|
{
|
||||||
|
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||||
|
big ?
|
||||||
|
Shared::fallbackSubscriptionStateThemeIconsDarkBig:
|
||||||
|
Shared::fallbackSubscriptionStateThemeIconsDarkSmall:
|
||||||
|
big ?
|
||||||
|
Shared::fallbackSubscriptionStateThemeIconsLightBig:
|
||||||
|
Shared::fallbackSubscriptionStateThemeIconsLightSmall;
|
||||||
|
|
||||||
|
return QIcon::fromTheme(subscriptionStateThemeIcons[static_cast<int>(ss)], QIcon(fallback[static_cast<int>(ss)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Shared::connectionStateIcon(Shared::ConnectionState cs, bool big)
|
||||||
|
{
|
||||||
|
const std::deque<QString>& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||||
|
big ?
|
||||||
|
Shared::fallbackConnectionStateThemeIconsDarkBig:
|
||||||
|
Shared::fallbackConnectionStateThemeIconsDarkSmall:
|
||||||
|
big ?
|
||||||
|
Shared::fallbackConnectionStateThemeIconsLightBig:
|
||||||
|
Shared::fallbackConnectionStateThemeIconsLightSmall;
|
||||||
|
|
||||||
|
return QIcon::fromTheme(connectionStateThemeIcons[static_cast<int>(cs)], QIcon(fallback[static_cast<int>(cs)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QString ds = ":images/fallback/dark/small/";
|
||||||
|
static const QString db = ":images/fallback/dark/big/";
|
||||||
|
static const QString ls = ":images/fallback/light/small/";
|
||||||
|
static const QString lb = ":images/fallback/light/big/";
|
||||||
|
|
||||||
|
QIcon Shared::icon(const QString& name, bool big)
|
||||||
|
{
|
||||||
|
std::map<QString, std::pair<QString, QString>>::const_iterator itr = icons.find(name);
|
||||||
|
if (itr != icons.end()) {
|
||||||
|
const QString& prefix = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||||
|
big ? db : ds:
|
||||||
|
big ? lb : ls;
|
||||||
|
return QIcon::fromTheme(itr->second.first, QIcon(prefix + itr->second.second + ".svg"));
|
||||||
|
} else {
|
||||||
|
qDebug() << "Icon" << name << "not found";
|
||||||
|
return QIcon::fromTheme(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString Shared::iconPath(const QString& name, bool big)
|
||||||
|
{
|
||||||
|
QString result = "";
|
||||||
|
std::map<QString, std::pair<QString, QString>>::const_iterator itr = icons.find(name);
|
||||||
|
if (itr != icons.end()) {
|
||||||
|
const QString& prefix = QApplication::palette().window().color().lightnessF() > 0.5 ?
|
||||||
|
big ? db : ds:
|
||||||
|
big ? lb : ls;
|
||||||
|
result = prefix + itr->second.second + ".svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
178
shared/icons.h
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_ICONS_H
|
||||||
|
#define SHARED_ICONS_H
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "enums.h"
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackAvailabilityThemeIconsLightBig = {
|
||||||
|
":images/fallback/light/big/online.svg",
|
||||||
|
":images/fallback/light/big/away.svg",
|
||||||
|
":images/fallback/light/big/absent.svg",
|
||||||
|
":images/fallback/light/big/busy.svg",
|
||||||
|
":images/fallback/light/big/chatty.svg",
|
||||||
|
":images/fallback/light/big/invisible.svg",
|
||||||
|
":images/fallback/light/big/offline.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackSubscriptionStateThemeIconsLightBig = {
|
||||||
|
":images/fallback/light/big/edit-none.svg",
|
||||||
|
":images/fallback/light/big/arrow-down-double.svg",
|
||||||
|
":images/fallback/light/big/arrow-up-double.svg",
|
||||||
|
":images/fallback/light/big/dialog-ok.svg",
|
||||||
|
":images/fallback/light/big/question.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackConnectionStateThemeIconsLightBig = {
|
||||||
|
":images/fallback/light/big/state-offline.svg",
|
||||||
|
":images/fallback/light/big/state-sync.svg",
|
||||||
|
":images/fallback/light/big/state-ok.svg",
|
||||||
|
":images/fallback/light/big/state-error.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackAvailabilityThemeIconsLightSmall = {
|
||||||
|
":images/fallback/light/small/online.svg",
|
||||||
|
":images/fallback/light/small/away.svg",
|
||||||
|
":images/fallback/light/small/absent.svg",
|
||||||
|
":images/fallback/light/small/busy.svg",
|
||||||
|
":images/fallback/light/small/chatty.svg",
|
||||||
|
":images/fallback/light/small/invisible.svg",
|
||||||
|
":images/fallback/light/small/offline.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackSubscriptionStateThemeIconsLightSmall = {
|
||||||
|
":images/fallback/light/small/edit-none.svg",
|
||||||
|
":images/fallback/light/small/arrow-down-double.svg",
|
||||||
|
":images/fallback/light/small/arrow-up-double.svg",
|
||||||
|
":images/fallback/light/small/dialog-ok.svg",
|
||||||
|
":images/fallback/light/small/question.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackConnectionStateThemeIconsLightSmall = {
|
||||||
|
":images/fallback/light/small/state-offline.svg",
|
||||||
|
":images/fallback/light/small/state-sync.svg",
|
||||||
|
":images/fallback/light/small/state-ok.svg",
|
||||||
|
":images/fallback/light/small/state-error.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackAvailabilityThemeIconsDarkBig = {
|
||||||
|
":images/fallback/dark/big/online.svg",
|
||||||
|
":images/fallback/dark/big/away.svg",
|
||||||
|
":images/fallback/dark/big/absent.svg",
|
||||||
|
":images/fallback/dark/big/busy.svg",
|
||||||
|
":images/fallback/dark/big/chatty.svg",
|
||||||
|
":images/fallback/dark/big/invisible.svg",
|
||||||
|
":images/fallback/dark/big/offline.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackSubscriptionStateThemeIconsDarkBig = {
|
||||||
|
":images/fallback/dark/big/edit-none.svg",
|
||||||
|
":images/fallback/dark/big/arrow-down-double.svg",
|
||||||
|
":images/fallback/dark/big/arrow-up-double.svg",
|
||||||
|
":images/fallback/dark/big/dialog-ok.svg",
|
||||||
|
":images/fallback/dark/big/question.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackConnectionStateThemeIconsDarkBig = {
|
||||||
|
":images/fallback/dark/big/state-offline.svg",
|
||||||
|
":images/fallback/dark/big/state-sync.svg",
|
||||||
|
":images/fallback/dark/big/state-ok.svg",
|
||||||
|
":images/fallback/dark/big/state-error.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackAvailabilityThemeIconsDarkSmall = {
|
||||||
|
":images/fallback/dark/small/online.svg",
|
||||||
|
":images/fallback/dark/small/away.svg",
|
||||||
|
":images/fallback/dark/small/absent.svg",
|
||||||
|
":images/fallback/dark/small/busy.svg",
|
||||||
|
":images/fallback/dark/small/chatty.svg",
|
||||||
|
":images/fallback/dark/small/invisible.svg",
|
||||||
|
":images/fallback/dark/small/offline.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackSubscriptionStateThemeIconsDarkSmall = {
|
||||||
|
":images/fallback/dark/small/edit-none.svg",
|
||||||
|
":images/fallback/dark/small/arrow-down-double.svg",
|
||||||
|
":images/fallback/dark/small/arrow-up-double.svg",
|
||||||
|
":images/fallback/dark/small/dialog-ok.svg",
|
||||||
|
":images/fallback/dark/small/question.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::deque<QString> fallbackConnectionStateThemeIconsDarkSmall = {
|
||||||
|
":images/fallback/dark/small/state-offline.svg",
|
||||||
|
":images/fallback/dark/small/state-sync.svg",
|
||||||
|
":images/fallback/dark/small/state-ok.svg",
|
||||||
|
":images/fallback/dark/small/state-error.svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
QIcon availabilityIcon(Availability av, bool big = false);
|
||||||
|
QIcon subscriptionStateIcon(SubscriptionState ss, bool big = false);
|
||||||
|
QIcon connectionStateIcon(ConnectionState cs, bool big = false);
|
||||||
|
QIcon icon(const QString& name, bool big = false);
|
||||||
|
QString iconPath(const QString& name, bool big = false);
|
||||||
|
|
||||||
|
static const std::map<QString, std::pair<QString, QString>> icons = {
|
||||||
|
{"user-online", {"user-online", "online"}},
|
||||||
|
{"user-away", {"user-away", "away"}},
|
||||||
|
{"user-away-extended", {"user-away-extended", "absent"}},
|
||||||
|
{"user-busy", {"user-busy", "busy"}},
|
||||||
|
{"user-chatty", {"chatty", "chatty"}},
|
||||||
|
{"user-invisible", {"user-invisible", "invisible"}},
|
||||||
|
{"user-offline", {"offline", "offline"}},
|
||||||
|
{"edit-none", {"edit-none", "edit-none"}},
|
||||||
|
{"arrow-down-double", {"arrow-down-double", "arrow-down-double"}},
|
||||||
|
{"arrow-up-double", {"arrow-up-double", "arrow-up-double"}},
|
||||||
|
{"dialog-ok", {"dialog-ok", "dialog-ok"}},
|
||||||
|
{"question", {"question", "question"}},
|
||||||
|
{"state-offline", {"state-offline", "state-offline"}},
|
||||||
|
{"state-sync", {"state-sync", "state-sync"}},
|
||||||
|
{"state-ok", {"state-ok", "state-ok"}},
|
||||||
|
{"state-error", {"state-error", "state-error"}},
|
||||||
|
|
||||||
|
{"edit-copy", {"edit-copy", "copy"}},
|
||||||
|
{"edit-delete", {"edit-delete", "edit-delete"}},
|
||||||
|
{"edit-rename", {"edit-rename", "edit-rename"}},
|
||||||
|
{"mail-message", {"mail-message", "mail-message"}},
|
||||||
|
{"mail-attachment", {"mail-attachment", "mail-attachment"}},
|
||||||
|
{"network-connect", {"network-connect", "network-connect"}},
|
||||||
|
{"network-disconnect", {"network-disconnect", "network-disconnect"}},
|
||||||
|
{"news-subscribe", {"news-subscribe", "news-subscribe"}},
|
||||||
|
{"news-unsubscribe", {"news-unsubscribe", "news-unsubscribe"}},
|
||||||
|
{"view-refresh", {"view-refresh", "view-refresh"}},
|
||||||
|
{"send", {"document-send", "send"}},
|
||||||
|
{"clean", {"edit-clear-all", "clean"}},
|
||||||
|
{"user", {"user", "user"}},
|
||||||
|
{"user-properties", {"user-properties", "user-properties"}},
|
||||||
|
{"group", {"group", "group"}},
|
||||||
|
{"group-new", {"resurce-group-new", "group-new"}},
|
||||||
|
{"favorite", {"favorite", "favorite"}},
|
||||||
|
{"unfavorite", {"draw-star", "unfavorite"}},
|
||||||
|
{"list-add", {"list-add", "add"}},
|
||||||
|
{"folder", {"folder", "folder"}},
|
||||||
|
{"document-preview", {"document-preview", "document-preview"}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARED_ICONS_H
|
491
shared/message.cpp
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "message.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
Shared::Message::Message(Shared::Message::Type p_type):
|
||||||
|
jFrom(),
|
||||||
|
rFrom(),
|
||||||
|
jTo(),
|
||||||
|
rTo(),
|
||||||
|
id(),
|
||||||
|
body(),
|
||||||
|
time(),
|
||||||
|
thread(),
|
||||||
|
type(p_type),
|
||||||
|
outgoing(false),
|
||||||
|
forwarded(false),
|
||||||
|
state(State::delivered),
|
||||||
|
edited(false),
|
||||||
|
errorText(),
|
||||||
|
originalMessage(),
|
||||||
|
lastModified(),
|
||||||
|
stanzaId(),
|
||||||
|
attachPath()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Shared::Message::Message():
|
||||||
|
jFrom(),
|
||||||
|
rFrom(),
|
||||||
|
jTo(),
|
||||||
|
rTo(),
|
||||||
|
id(),
|
||||||
|
body(),
|
||||||
|
time(),
|
||||||
|
thread(),
|
||||||
|
type(Message::normal),
|
||||||
|
outgoing(false),
|
||||||
|
forwarded(false),
|
||||||
|
state(State::delivered),
|
||||||
|
edited(false),
|
||||||
|
errorText(),
|
||||||
|
originalMessage(),
|
||||||
|
lastModified(),
|
||||||
|
stanzaId(),
|
||||||
|
attachPath()
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString Shared::Message::getBody() const
|
||||||
|
{
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getFrom() const
|
||||||
|
{
|
||||||
|
QString from = jFrom;
|
||||||
|
if (rFrom.size() > 0) {
|
||||||
|
from += "/" + rFrom;
|
||||||
|
}
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getTo() const
|
||||||
|
{
|
||||||
|
QString to = jTo;
|
||||||
|
if (rTo.size() > 0) {
|
||||||
|
to += "/" + rTo;
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getId() const
|
||||||
|
{
|
||||||
|
if (id.size() > 0) {
|
||||||
|
return id;
|
||||||
|
} else {
|
||||||
|
return stanzaId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime Shared::Message::getTime() const
|
||||||
|
{
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setBody(const QString& p_body)
|
||||||
|
{
|
||||||
|
body = p_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setFrom(const QString& from)
|
||||||
|
{
|
||||||
|
QStringList list = from.split("/");
|
||||||
|
if (list.size() == 1) {
|
||||||
|
jFrom = from.toLower();
|
||||||
|
} else {
|
||||||
|
jFrom = list.front().toLower();
|
||||||
|
rFrom = list.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setTo(const QString& to)
|
||||||
|
{
|
||||||
|
QStringList list = to.split("/");
|
||||||
|
if (list.size() == 1) {
|
||||||
|
jTo = to.toLower();
|
||||||
|
} else {
|
||||||
|
jTo = list.front().toLower();
|
||||||
|
rTo = list.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setId(const QString& p_id)
|
||||||
|
{
|
||||||
|
id = p_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setTime(const QDateTime& p_time)
|
||||||
|
{
|
||||||
|
time = p_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getFromJid() const
|
||||||
|
{
|
||||||
|
return jFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getFromResource() const
|
||||||
|
{
|
||||||
|
return rFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getToJid() const
|
||||||
|
{
|
||||||
|
return jTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getToResource() const
|
||||||
|
{
|
||||||
|
return rTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getErrorText() const
|
||||||
|
{
|
||||||
|
return errorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getPenPalJid() const
|
||||||
|
{
|
||||||
|
if (outgoing) {
|
||||||
|
return jTo;
|
||||||
|
} else {
|
||||||
|
return jFrom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getPenPalResource() const
|
||||||
|
{
|
||||||
|
if (outgoing) {
|
||||||
|
return rTo;
|
||||||
|
} else {
|
||||||
|
return rFrom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message::State Shared::Message::getState() const
|
||||||
|
{
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::getEdited() const
|
||||||
|
{
|
||||||
|
return edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setFromJid(const QString& from)
|
||||||
|
{
|
||||||
|
jFrom = from.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setFromResource(const QString& from)
|
||||||
|
{
|
||||||
|
rFrom = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setToJid(const QString& to)
|
||||||
|
{
|
||||||
|
jTo = to.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setToResource(const QString& to)
|
||||||
|
{
|
||||||
|
rTo = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setErrorText(const QString& err)
|
||||||
|
{
|
||||||
|
if (state == State::error) {
|
||||||
|
errorText = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::getOutgoing() const
|
||||||
|
{
|
||||||
|
return outgoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setOutgoing(bool og)
|
||||||
|
{
|
||||||
|
outgoing = og;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::getForwarded() const
|
||||||
|
{
|
||||||
|
return forwarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::generateRandomId()
|
||||||
|
{
|
||||||
|
id = generateUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getThread() const
|
||||||
|
{
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setForwarded(bool fwd)
|
||||||
|
{
|
||||||
|
forwarded = fwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setThread(const QString& p_body)
|
||||||
|
{
|
||||||
|
thread = p_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime Shared::Message::getLastModified() const
|
||||||
|
{
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getOriginalBody() const
|
||||||
|
{
|
||||||
|
return originalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message::Type Shared::Message::getType() const
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setType(Shared::Message::Type t)
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setState(Shared::Message::State p_state)
|
||||||
|
{
|
||||||
|
state = p_state;
|
||||||
|
|
||||||
|
if (state != State::error) {
|
||||||
|
errorText = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::serverStored() const
|
||||||
|
{
|
||||||
|
return state == State::delivered || state == State::sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setEdited(bool p_edited)
|
||||||
|
{
|
||||||
|
edited = p_edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::serialize(QDataStream& data) const
|
||||||
|
{
|
||||||
|
data << jFrom;
|
||||||
|
data << rFrom;
|
||||||
|
data << jTo;
|
||||||
|
data << rTo;
|
||||||
|
data << id;
|
||||||
|
data << body;
|
||||||
|
data << time;
|
||||||
|
data << thread;
|
||||||
|
data << (quint8)type;
|
||||||
|
data << outgoing;
|
||||||
|
data << forwarded;
|
||||||
|
data << oob;
|
||||||
|
data << (quint8)state;
|
||||||
|
data << edited;
|
||||||
|
if (state == State::error) {
|
||||||
|
data << errorText;
|
||||||
|
}
|
||||||
|
if (edited) {
|
||||||
|
data << originalMessage;
|
||||||
|
data << lastModified;
|
||||||
|
}
|
||||||
|
data << stanzaId;
|
||||||
|
data << attachPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::deserialize(QDataStream& data)
|
||||||
|
{
|
||||||
|
data >> jFrom;
|
||||||
|
data >> rFrom;
|
||||||
|
data >> jTo;
|
||||||
|
data >> rTo;
|
||||||
|
data >> id;
|
||||||
|
data >> body;
|
||||||
|
data >> time;
|
||||||
|
data >> thread;
|
||||||
|
quint8 t;
|
||||||
|
data >> t;
|
||||||
|
type = static_cast<Type>(t);
|
||||||
|
data >> outgoing;
|
||||||
|
data >> forwarded;
|
||||||
|
data >> oob;
|
||||||
|
quint8 s;
|
||||||
|
data >> s;
|
||||||
|
state = static_cast<State>(s);
|
||||||
|
data >> edited;
|
||||||
|
if (state == State::error) {
|
||||||
|
data >> errorText;
|
||||||
|
}
|
||||||
|
if (edited) {
|
||||||
|
data >> originalMessage;
|
||||||
|
data >> lastModified;
|
||||||
|
}
|
||||||
|
data >> stanzaId;
|
||||||
|
data >> attachPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::change(const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
QMap<QString, QVariant>::const_iterator itr = data.find("state");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setState(static_cast<State>(itr.value().toUInt()));
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = data.find("outOfBandUrl");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setOutOfBandUrl(itr.value().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = data.find("attachPath");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAttachPath(itr.value().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == State::error) {
|
||||||
|
itr = data.find("errorText");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setErrorText(itr.value().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool idChanged = false;
|
||||||
|
itr = data.find("id");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
QString newId = itr.value().toString();
|
||||||
|
if (id != newId) {
|
||||||
|
setId(newId);
|
||||||
|
idChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = data.find("stanzaId");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
QString newId = itr.value().toString();
|
||||||
|
if (stanzaId != newId) {
|
||||||
|
setStanzaId(newId);
|
||||||
|
if (id.size() == 0) {
|
||||||
|
idChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itr = data.find("body");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
QString b = itr.value().toString();
|
||||||
|
if (body != b) {
|
||||||
|
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
||||||
|
QDateTime correctionDate;
|
||||||
|
if (dItr != data.end()) {
|
||||||
|
correctionDate = dItr.value().toDateTime();
|
||||||
|
} else {
|
||||||
|
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
|
||||||
|
}
|
||||||
|
if (!edited || lastModified < correctionDate) {
|
||||||
|
if (!edited) {
|
||||||
|
originalMessage = body;
|
||||||
|
}
|
||||||
|
lastModified = correctionDate;
|
||||||
|
setBody(b);
|
||||||
|
setEdited(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
|
||||||
|
if (dItr != data.end()) {
|
||||||
|
QDateTime ntime = dItr.value().toDateTime();
|
||||||
|
if (time != ntime) {
|
||||||
|
setTime(ntime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setCurrentTime()
|
||||||
|
{
|
||||||
|
time = QDateTime::currentDateTimeUtc();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getOutOfBandUrl() const
|
||||||
|
{
|
||||||
|
return oob;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::hasOutOfBandUrl() const
|
||||||
|
{
|
||||||
|
return oob.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setOutOfBandUrl(const QString& url)
|
||||||
|
{
|
||||||
|
oob = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::storable() const
|
||||||
|
{
|
||||||
|
return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setStanzaId(const QString& sid)
|
||||||
|
{
|
||||||
|
stanzaId = sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getStanzaId() const
|
||||||
|
{
|
||||||
|
return stanzaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::Message::getAttachPath() const
|
||||||
|
{
|
||||||
|
return attachPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::setAttachPath(const QString& path)
|
||||||
|
{
|
||||||
|
attachPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Message::Change::Change(const QMap<QString, QVariant>& _data):
|
||||||
|
data(_data),
|
||||||
|
idModified(false) {}
|
||||||
|
|
||||||
|
void Shared::Message::Change::operator()(Shared::Message& msg)
|
||||||
|
{
|
||||||
|
idModified = msg.change(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::Message::Change::operator()(Shared::Message* msg)
|
||||||
|
{
|
||||||
|
idModified = msg->change(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::Message::Change::hasIdBeenModified() const
|
||||||
|
{
|
||||||
|
return idModified;
|
||||||
|
}
|
||||||
|
|
146
shared/message.h
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* 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 SHAPER_MESSAGE_H
|
||||||
|
#define SHAPER_MESSAGE_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class Message {
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
error,
|
||||||
|
normal,
|
||||||
|
chat,
|
||||||
|
groupChat,
|
||||||
|
headline
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
pending,
|
||||||
|
sent,
|
||||||
|
delivered,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
|
||||||
|
static const State StateHighest = State::error;
|
||||||
|
static const State StateLowest = State::pending;
|
||||||
|
|
||||||
|
struct Change //change functor, stores in idModified if ID has been modified during change
|
||||||
|
{
|
||||||
|
Change(const QMap<QString, QVariant>& _data);
|
||||||
|
void operator() (Message& msg);
|
||||||
|
void operator() (Message* msg);
|
||||||
|
bool hasIdBeenModified() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QMap<QString, QVariant>& data;
|
||||||
|
bool idModified;
|
||||||
|
};
|
||||||
|
|
||||||
|
Message(Type p_type);
|
||||||
|
Message();
|
||||||
|
|
||||||
|
void setFrom(const QString& from);
|
||||||
|
void setFromResource(const QString& from);
|
||||||
|
void setFromJid(const QString& from);
|
||||||
|
void setTo(const QString& to);
|
||||||
|
void setToResource(const QString& to);
|
||||||
|
void setToJid(const QString& to);
|
||||||
|
void setTime(const QDateTime& p_time);
|
||||||
|
void setId(const QString& p_id);
|
||||||
|
void setBody(const QString& p_body);
|
||||||
|
void setThread(const QString& p_body);
|
||||||
|
void setOutgoing(bool og);
|
||||||
|
void setForwarded(bool fwd);
|
||||||
|
void setType(Type t);
|
||||||
|
void setCurrentTime();
|
||||||
|
void setOutOfBandUrl(const QString& url);
|
||||||
|
void setState(State p_state);
|
||||||
|
void setEdited(bool p_edited);
|
||||||
|
void setErrorText(const QString& err);
|
||||||
|
bool change(const QMap<QString, QVariant>& data);
|
||||||
|
void setStanzaId(const QString& sid);
|
||||||
|
void setAttachPath(const QString& path);
|
||||||
|
|
||||||
|
QString getFrom() const;
|
||||||
|
QString getFromJid() const;
|
||||||
|
QString getFromResource() const;
|
||||||
|
QString getTo() const;
|
||||||
|
QString getToJid() const;
|
||||||
|
QString getToResource() const;
|
||||||
|
QDateTime getTime() const;
|
||||||
|
QString getId() const;
|
||||||
|
QString getBody() const;
|
||||||
|
QString getThread() const;
|
||||||
|
bool getOutgoing() const;
|
||||||
|
bool getForwarded() const;
|
||||||
|
Type getType() const;
|
||||||
|
bool hasOutOfBandUrl() const;
|
||||||
|
bool storable() const;
|
||||||
|
QString getOutOfBandUrl() const;
|
||||||
|
State getState() const;
|
||||||
|
bool getEdited() const;
|
||||||
|
QString getErrorText() const;
|
||||||
|
|
||||||
|
QString getPenPalJid() const;
|
||||||
|
QString getPenPalResource() const;
|
||||||
|
void generateRandomId();
|
||||||
|
bool serverStored() const;
|
||||||
|
QDateTime getLastModified() const;
|
||||||
|
QString getOriginalBody() const;
|
||||||
|
QString getStanzaId() const;
|
||||||
|
QString getAttachPath() const;
|
||||||
|
|
||||||
|
void serialize(QDataStream& data) const;
|
||||||
|
void deserialize(QDataStream& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString jFrom;
|
||||||
|
QString rFrom;
|
||||||
|
QString jTo;
|
||||||
|
QString rTo;
|
||||||
|
QString id;
|
||||||
|
QString body;
|
||||||
|
QDateTime time;
|
||||||
|
QString thread;
|
||||||
|
Type type;
|
||||||
|
bool outgoing;
|
||||||
|
bool forwarded;
|
||||||
|
QString oob;
|
||||||
|
State state;
|
||||||
|
bool edited;
|
||||||
|
QString errorText;
|
||||||
|
QString originalMessage;
|
||||||
|
QDateTime lastModified;
|
||||||
|
QString stanzaId;
|
||||||
|
QString attachPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHAPER_MESSAGE_H
|
45
shared/messageinfo.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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 "messageinfo.h"
|
||||||
|
|
||||||
|
using namespace Shared;
|
||||||
|
|
||||||
|
Shared::MessageInfo::MessageInfo():
|
||||||
|
account(),
|
||||||
|
jid(),
|
||||||
|
messageId() {}
|
||||||
|
|
||||||
|
Shared::MessageInfo::MessageInfo(const QString& acc, const QString& j, const QString& id):
|
||||||
|
account(acc),
|
||||||
|
jid(j),
|
||||||
|
messageId(id) {}
|
||||||
|
|
||||||
|
Shared::MessageInfo::MessageInfo(const Shared::MessageInfo& other):
|
||||||
|
account(other.account),
|
||||||
|
jid(other.jid),
|
||||||
|
messageId(other.messageId) {}
|
||||||
|
|
||||||
|
Shared::MessageInfo & Shared::MessageInfo::operator=(const Shared::MessageInfo& other)
|
||||||
|
{
|
||||||
|
account = other.account;
|
||||||
|
jid = other.jid;
|
||||||
|
messageId = other.messageId;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
43
shared/messageinfo.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_MESSAGEINFO_H
|
||||||
|
#define SHARED_MESSAGEINFO_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
struct MessageInfo {
|
||||||
|
MessageInfo();
|
||||||
|
MessageInfo(const QString& acc, const QString& j, const QString& id);
|
||||||
|
MessageInfo(const MessageInfo& other);
|
||||||
|
|
||||||
|
QString account;
|
||||||
|
QString jid;
|
||||||
|
QString messageId;
|
||||||
|
|
||||||
|
MessageInfo& operator=(const MessageInfo& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARED_MESSAGEINFO_H
|
@ -29,7 +29,7 @@ namespace W
|
|||||||
template <typename data_type, typename comparator = std::less<data_type>>
|
template <typename data_type, typename comparator = std::less<data_type>>
|
||||||
class Order
|
class Order
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
class Duplicates:
|
class Duplicates:
|
||||||
public Utils::Exception
|
public Utils::Exception
|
||||||
{
|
{
|
96
shared/pathcheck.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 "pathcheck.h"
|
||||||
|
|
||||||
|
QRegularExpression squawk("^squawk:\\/\\/");
|
||||||
|
QString Shared::downloadsPathCheck()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
QVariant dpv = settings.value("downloadsPath");
|
||||||
|
QString path;
|
||||||
|
if (!dpv.isValid()) {
|
||||||
|
path = defaultDownloadsPath();
|
||||||
|
qDebug() << "no downloadsPath variable in config, using default" << path;
|
||||||
|
path = getAbsoluteWritablePath(path);
|
||||||
|
return path;
|
||||||
|
} else {
|
||||||
|
path = dpv.toString();
|
||||||
|
path = getAbsoluteWritablePath(path);
|
||||||
|
if (path.size() == 0) {
|
||||||
|
path = defaultDownloadsPath();
|
||||||
|
qDebug() << "falling back to the default downloads path" << path;
|
||||||
|
path = getAbsoluteWritablePath(path);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::defaultDownloadsPath()
|
||||||
|
{
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + QApplication::applicationName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::getAbsoluteWritablePath(const QString& path)
|
||||||
|
{
|
||||||
|
QDir location(path);
|
||||||
|
if (!location.exists()) {
|
||||||
|
bool res = location.mkpath(location.absolutePath());
|
||||||
|
if (!res) {
|
||||||
|
qDebug() << "couldn't create directory" << path;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QFileInfo info(location.absolutePath());
|
||||||
|
if (info.isWritable()) {
|
||||||
|
return location.absolutePath();
|
||||||
|
} else {
|
||||||
|
qDebug() << "directory" << path << "is not writable";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::resolvePath(QString path)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
QVariant dpv = settings.value("downloadsPath");
|
||||||
|
return path.replace(squawk, dpv.toString() + "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::squawkifyPath(QString path)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
QString current = settings.value("downloadsPath").toString();
|
||||||
|
|
||||||
|
if (path.startsWith(current)) {
|
||||||
|
path.replace(0, current.size() + 1, "squawk://");
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Shared::isSubdirectoryOfSettings(const QString& path)
|
||||||
|
{
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QDir oldPath(settings.value("downloadsPath").toString());
|
||||||
|
QDir newPath(path);
|
||||||
|
|
||||||
|
return newPath.canonicalPath().startsWith(oldPath.canonicalPath());
|
||||||
|
}
|
||||||
|
|
44
shared/pathcheck.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 PATHCHECK_H
|
||||||
|
#define PATHCHECK_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
QString downloadsPathCheck();
|
||||||
|
QString downloadsPathCheck(QString path);
|
||||||
|
QString defaultDownloadsPath();
|
||||||
|
|
||||||
|
QString getAbsoluteWritablePath(const QString& path);
|
||||||
|
QString resolvePath(QString path);
|
||||||
|
QString squawkifyPath(QString path);
|
||||||
|
bool isSubdirectoryOfSettings(const QString& path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PATHCHECK_H
|
30
shared/shared.h
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_H
|
||||||
|
#define SHARED_H
|
||||||
|
|
||||||
|
#include "enums.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "icons.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "messageinfo.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "vcard.h"
|
||||||
|
|
||||||
|
#endif // SHARED_H
|
44
shared/utils.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 "utils.h"
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
QString Shared::generateUUID()
|
||||||
|
{
|
||||||
|
return QUuid::createUuid().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const QRegularExpression urlReg("(?<!<a\\shref=['\"])(?<!<img\\ssrc=['\"])("
|
||||||
|
"(?:https?|ftp):\\/\\/"
|
||||||
|
"\\w+"
|
||||||
|
"(?:"
|
||||||
|
"[\\w\\.\\,\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]?"
|
||||||
|
"(?:"
|
||||||
|
"\\([\\w\\.\\,\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]+\\)"
|
||||||
|
")?"
|
||||||
|
")*"
|
||||||
|
")");
|
||||||
|
|
||||||
|
QString Shared::processMessageBody(const QString& msg)
|
||||||
|
{
|
||||||
|
QString processed = msg.toHtmlEscaped();
|
||||||
|
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
|
||||||
|
return "<p style=\"white-space: pre-wrap; line-height: 1em;\">" + processed + "</p>";
|
||||||
|
}
|
80
shared/utils.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_UTILS_H
|
||||||
|
#define SHARED_UTILS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
QString generateUUID();
|
||||||
|
QString processMessageBody(const QString& msg);
|
||||||
|
|
||||||
|
static const std::vector<QColor> colorPalette = {
|
||||||
|
QColor(244, 27, 63),
|
||||||
|
QColor(21, 104, 156),
|
||||||
|
QColor(38, 156, 98),
|
||||||
|
QColor(247, 103, 101),
|
||||||
|
QColor(121, 37, 117),
|
||||||
|
QColor(242, 202, 33),
|
||||||
|
QColor(168, 22, 63),
|
||||||
|
QColor(35, 100, 52),
|
||||||
|
QColor(52, 161, 152),
|
||||||
|
QColor(239, 53, 111),
|
||||||
|
QColor(237, 234, 36),
|
||||||
|
QColor(153, 148, 194),
|
||||||
|
QColor(211, 102, 151),
|
||||||
|
QColor(194, 63, 118),
|
||||||
|
QColor(249, 149, 51),
|
||||||
|
QColor(244, 206, 109),
|
||||||
|
QColor(121, 105, 153),
|
||||||
|
QColor(244, 199, 30),
|
||||||
|
QColor(28, 112, 28),
|
||||||
|
QColor(172, 18, 20),
|
||||||
|
QColor(25, 66, 110),
|
||||||
|
QColor(25, 149, 104),
|
||||||
|
QColor(214, 148, 0),
|
||||||
|
QColor(203, 47, 57),
|
||||||
|
QColor(4, 54, 84),
|
||||||
|
QColor(116, 161, 97),
|
||||||
|
QColor(50, 68, 52),
|
||||||
|
QColor(237, 179, 20),
|
||||||
|
QColor(69, 114, 147),
|
||||||
|
QColor(242, 212, 31),
|
||||||
|
QColor(248, 19, 20),
|
||||||
|
QColor(84, 102, 84),
|
||||||
|
QColor(25, 53, 122),
|
||||||
|
QColor(91, 91, 109),
|
||||||
|
QColor(17, 17, 80),
|
||||||
|
QColor(54, 54, 94)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Hover {
|
||||||
|
nothing,
|
||||||
|
text,
|
||||||
|
anchor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARED_UTILS_H
|
288
shared/vcard.cpp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* 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 "vcard.h"
|
||||||
|
|
||||||
|
Shared::VCard::Contact::Contact(Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||||
|
role(p_role),
|
||||||
|
prefered(p_prefered) {}
|
||||||
|
|
||||||
|
Shared::VCard::Email::Email(const QString& addr, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||||
|
Contact(p_role, p_prefered),
|
||||||
|
address(addr) {}
|
||||||
|
|
||||||
|
Shared::VCard::Phone::Phone(const QString& nmbr, Shared::VCard::Phone::Type p_type, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||||
|
Contact(p_role, p_prefered),
|
||||||
|
number(nmbr),
|
||||||
|
type(p_type) {}
|
||||||
|
|
||||||
|
Shared::VCard::Address::Address(const QString& zCode, const QString& cntry, const QString& rgn, const QString& lclty, const QString& strt, const QString& ext, Shared::VCard::Contact::Role p_role, bool p_prefered):
|
||||||
|
Contact(p_role, p_prefered),
|
||||||
|
zipCode(zCode),
|
||||||
|
country(cntry),
|
||||||
|
region(rgn),
|
||||||
|
locality(lclty),
|
||||||
|
street(strt),
|
||||||
|
external(ext) {}
|
||||||
|
|
||||||
|
Shared::VCard::VCard():
|
||||||
|
fullName(),
|
||||||
|
firstName(),
|
||||||
|
middleName(),
|
||||||
|
lastName(),
|
||||||
|
nickName(),
|
||||||
|
description(),
|
||||||
|
url(),
|
||||||
|
organizationName(),
|
||||||
|
organizationUnit(),
|
||||||
|
organizationRole(),
|
||||||
|
jobTitle(),
|
||||||
|
birthday(),
|
||||||
|
photoType(Avatar::empty),
|
||||||
|
photoPath(),
|
||||||
|
receivingTime(QDateTime::currentDateTimeUtc()),
|
||||||
|
emails(),
|
||||||
|
phones(),
|
||||||
|
addresses() {}
|
||||||
|
|
||||||
|
Shared::VCard::VCard(const QDateTime& creationTime):
|
||||||
|
fullName(),
|
||||||
|
firstName(),
|
||||||
|
middleName(),
|
||||||
|
lastName(),
|
||||||
|
nickName(),
|
||||||
|
description(),
|
||||||
|
url(),
|
||||||
|
organizationName(),
|
||||||
|
organizationUnit(),
|
||||||
|
organizationRole(),
|
||||||
|
jobTitle(),
|
||||||
|
birthday(),
|
||||||
|
photoType(Avatar::empty),
|
||||||
|
photoPath(),
|
||||||
|
receivingTime(creationTime),
|
||||||
|
emails(),
|
||||||
|
phones(),
|
||||||
|
addresses() {}
|
||||||
|
|
||||||
|
QString Shared::VCard::getAvatarPath() const
|
||||||
|
{
|
||||||
|
return photoPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Avatar Shared::VCard::getAvatarType() const
|
||||||
|
{
|
||||||
|
return photoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDate Shared::VCard::getBirthday() const
|
||||||
|
{
|
||||||
|
return birthday;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getDescription() const
|
||||||
|
{
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getFirstName() const
|
||||||
|
{
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getLastName() const
|
||||||
|
{
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getMiddleName() const
|
||||||
|
{
|
||||||
|
return middleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getNickName() const
|
||||||
|
{
|
||||||
|
return nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setAvatarPath(const QString& path)
|
||||||
|
{
|
||||||
|
if (path != photoPath) {
|
||||||
|
photoPath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setAvatarType(Shared::Avatar type)
|
||||||
|
{
|
||||||
|
if (photoType != type) {
|
||||||
|
photoType = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setBirthday(const QDate& date)
|
||||||
|
{
|
||||||
|
if (date.isValid() && birthday != date) {
|
||||||
|
birthday = date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setDescription(const QString& descr)
|
||||||
|
{
|
||||||
|
if (description != descr) {
|
||||||
|
description = descr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setFirstName(const QString& first)
|
||||||
|
{
|
||||||
|
if (firstName != first) {
|
||||||
|
firstName = first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setLastName(const QString& last)
|
||||||
|
{
|
||||||
|
if (lastName != last) {
|
||||||
|
lastName = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setMiddleName(const QString& middle)
|
||||||
|
{
|
||||||
|
if (middleName != middle) {
|
||||||
|
middleName = middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setNickName(const QString& nick)
|
||||||
|
{
|
||||||
|
if (nickName != nick) {
|
||||||
|
nickName = nick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getFullName() const
|
||||||
|
{
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getUrl() const
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setFullName(const QString& name)
|
||||||
|
{
|
||||||
|
if (fullName != name) {
|
||||||
|
fullName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setUrl(const QString& u)
|
||||||
|
{
|
||||||
|
if (url != u) {
|
||||||
|
url = u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getOrgName() const
|
||||||
|
{
|
||||||
|
return organizationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getOrgRole() const
|
||||||
|
{
|
||||||
|
return organizationRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getOrgTitle() const
|
||||||
|
{
|
||||||
|
return jobTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Shared::VCard::getOrgUnit() const
|
||||||
|
{
|
||||||
|
return organizationUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setOrgName(const QString& name)
|
||||||
|
{
|
||||||
|
if (organizationName != name) {
|
||||||
|
organizationName = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setOrgRole(const QString& role)
|
||||||
|
{
|
||||||
|
if (organizationRole != role) {
|
||||||
|
organizationRole = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setOrgTitle(const QString& title)
|
||||||
|
{
|
||||||
|
if (jobTitle != title) {
|
||||||
|
jobTitle = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shared::VCard::setOrgUnit(const QString& unit)
|
||||||
|
{
|
||||||
|
if (organizationUnit != unit) {
|
||||||
|
organizationUnit = unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime Shared::VCard::getReceivingTime() const
|
||||||
|
{
|
||||||
|
return receivingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<Shared::VCard::Email> & Shared::VCard::getEmails()
|
||||||
|
{
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses()
|
||||||
|
{
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones()
|
||||||
|
{
|
||||||
|
return phones;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::deque<Shared::VCard::Email> & Shared::VCard::getEmails() const
|
||||||
|
{
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::deque<Shared::VCard::Address> & Shared::VCard::getAddresses() const
|
||||||
|
{
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::deque<Shared::VCard::Phone> & Shared::VCard::getPhones() const
|
||||||
|
{
|
||||||
|
return phones;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::deque<QString>Shared::VCard::Contact::roleNames = {"Not specified", "Personal", "Business"};
|
||||||
|
const std::deque<QString>Shared::VCard::Phone::typeNames = {"Fax", "Pager", "Voice", "Cell", "Video", "Modem", "Other"};
|
||||||
|
|
152
shared/vcard.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARED_VCARD_H
|
||||||
|
#define SHARED_VCARD_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include "enums.h"
|
||||||
|
|
||||||
|
namespace Shared {
|
||||||
|
|
||||||
|
class VCard {
|
||||||
|
class Contact {
|
||||||
|
public:
|
||||||
|
enum Role {
|
||||||
|
none,
|
||||||
|
home,
|
||||||
|
work
|
||||||
|
};
|
||||||
|
static const std::deque<QString> roleNames;
|
||||||
|
|
||||||
|
Contact(Role p_role = none, bool p_prefered = false);
|
||||||
|
|
||||||
|
Role role;
|
||||||
|
bool prefered;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
class Email : public Contact {
|
||||||
|
public:
|
||||||
|
Email(const QString& address, Role p_role = none, bool p_prefered = false);
|
||||||
|
|
||||||
|
QString address;
|
||||||
|
};
|
||||||
|
class Phone : public Contact {
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
fax,
|
||||||
|
pager,
|
||||||
|
voice,
|
||||||
|
cell,
|
||||||
|
video,
|
||||||
|
modem,
|
||||||
|
other
|
||||||
|
};
|
||||||
|
static const std::deque<QString> typeNames;
|
||||||
|
Phone(const QString& number, Type p_type = voice, Role p_role = none, bool p_prefered = false);
|
||||||
|
|
||||||
|
QString number;
|
||||||
|
Type type;
|
||||||
|
};
|
||||||
|
class Address : public Contact {
|
||||||
|
public:
|
||||||
|
Address(
|
||||||
|
const QString& zCode = "",
|
||||||
|
const QString& cntry = "",
|
||||||
|
const QString& rgn = "",
|
||||||
|
const QString& lclty = "",
|
||||||
|
const QString& strt = "",
|
||||||
|
const QString& ext = "",
|
||||||
|
Role p_role = none,
|
||||||
|
bool p_prefered = false
|
||||||
|
);
|
||||||
|
|
||||||
|
QString zipCode;
|
||||||
|
QString country;
|
||||||
|
QString region;
|
||||||
|
QString locality;
|
||||||
|
QString street;
|
||||||
|
QString external;
|
||||||
|
};
|
||||||
|
VCard();
|
||||||
|
VCard(const QDateTime& creationTime);
|
||||||
|
|
||||||
|
QString getFullName() const;
|
||||||
|
void setFullName(const QString& name);
|
||||||
|
QString getFirstName() const;
|
||||||
|
void setFirstName(const QString& first);
|
||||||
|
QString getMiddleName() const;
|
||||||
|
void setMiddleName(const QString& middle);
|
||||||
|
QString getLastName() const;
|
||||||
|
void setLastName(const QString& last);
|
||||||
|
QString getNickName() const;
|
||||||
|
void setNickName(const QString& nick);
|
||||||
|
QString getDescription() const;
|
||||||
|
void setDescription(const QString& descr);
|
||||||
|
QString getUrl() const;
|
||||||
|
void setUrl(const QString& u);
|
||||||
|
QDate getBirthday() const;
|
||||||
|
void setBirthday(const QDate& date);
|
||||||
|
Avatar getAvatarType() const;
|
||||||
|
void setAvatarType(Avatar type);
|
||||||
|
QString getAvatarPath() const;
|
||||||
|
void setAvatarPath(const QString& path);
|
||||||
|
QString getOrgName() const;
|
||||||
|
void setOrgName(const QString& name);
|
||||||
|
QString getOrgUnit() const;
|
||||||
|
void setOrgUnit(const QString& unit);
|
||||||
|
QString getOrgRole() const;
|
||||||
|
void setOrgRole(const QString& role);
|
||||||
|
QString getOrgTitle() const;
|
||||||
|
void setOrgTitle(const QString& title);
|
||||||
|
QDateTime getReceivingTime() const;
|
||||||
|
std::deque<Email>& getEmails();
|
||||||
|
const std::deque<Email>& getEmails() const;
|
||||||
|
std::deque<Phone>& getPhones();
|
||||||
|
const std::deque<Phone>& getPhones() const;
|
||||||
|
std::deque<Address>& getAddresses();
|
||||||
|
const std::deque<Address>& getAddresses() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString fullName;
|
||||||
|
QString firstName;
|
||||||
|
QString middleName;
|
||||||
|
QString lastName;
|
||||||
|
QString nickName;
|
||||||
|
QString description;
|
||||||
|
QString url;
|
||||||
|
QString organizationName;
|
||||||
|
QString organizationUnit;
|
||||||
|
QString organizationRole;
|
||||||
|
QString jobTitle;
|
||||||
|
QDate birthday;
|
||||||
|
Avatar photoType;
|
||||||
|
QString photoPath;
|
||||||
|
QDateTime receivingTime;
|
||||||
|
std::deque<Email> emails;
|
||||||
|
std::deque<Phone> phones;
|
||||||
|
std::deque<Address> addresses;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARED_VCARD_H
|
12
translations/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
find_package(Qt5LinguistTools)
|
||||||
|
|
||||||
|
set(TS_FILES
|
||||||
|
squawk.en.ts
|
||||||
|
squawk.ru.ts
|
||||||
|
squawk.pt_BR.ts
|
||||||
|
)
|
||||||
|
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||||
|
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
||||||
|
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n)
|
||||||
|
|
||||||
|
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
1420
translations/squawk.en.ts
Normal file
1103
translations/squawk.pt_BR.ts
Normal file
@ -1,44 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
target_sources(squawk PRIVATE
|
||||||
project(squawkUI)
|
squawk.cpp
|
||||||
|
squawk.h
|
||||||
# Instruct CMake to run moc automatically when needed.
|
squawk.ui
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
# Instruct CMake to create code from Qt designer ui files
|
|
||||||
set(CMAKE_AUTOUIC ON)
|
|
||||||
|
|
||||||
# Find the QtWidgets library
|
|
||||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
|
||||||
find_package(Qt5DBus CONFIG REQUIRED)
|
|
||||||
|
|
||||||
add_subdirectory(widgets)
|
|
||||||
|
|
||||||
set(squawkUI_SRC
|
|
||||||
squawk.cpp
|
|
||||||
models/accounts.cpp
|
|
||||||
models/roster.cpp
|
|
||||||
models/item.cpp
|
|
||||||
models/account.cpp
|
|
||||||
models/contact.cpp
|
|
||||||
models/presence.cpp
|
|
||||||
models/group.cpp
|
|
||||||
models/room.cpp
|
|
||||||
models/abstractparticipant.cpp
|
|
||||||
models/participant.cpp
|
|
||||||
utils/messageline.cpp
|
|
||||||
utils//message.cpp
|
|
||||||
utils/resizer.cpp
|
|
||||||
utils/image.cpp
|
|
||||||
utils/flowlayout.cpp
|
|
||||||
utils/badge.cpp
|
|
||||||
utils/progress.cpp
|
|
||||||
utils/comboboxdelegate.cpp
|
|
||||||
utils/dropshadoweffect.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
add_subdirectory(models)
|
||||||
add_library(squawkUI ${squawkUI_SRC})
|
add_subdirectory(utils)
|
||||||
|
add_subdirectory(widgets)
|
||||||
# Use the Widgets module from Qt 5.
|
|
||||||
target_link_libraries(squawkUI squawkWidgets)
|
|
||||||
target_link_libraries(squawkUI Qt5::Widgets)
|
|
||||||
target_link_libraries(squawkUI Qt5::DBus)
|
|
||||||
|
26
ui/models/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
abstractparticipant.cpp
|
||||||
|
abstractparticipant.h
|
||||||
|
account.cpp
|
||||||
|
account.h
|
||||||
|
accounts.cpp
|
||||||
|
accounts.h
|
||||||
|
contact.cpp
|
||||||
|
contact.h
|
||||||
|
element.cpp
|
||||||
|
element.h
|
||||||
|
group.cpp
|
||||||
|
group.h
|
||||||
|
item.cpp
|
||||||
|
item.h
|
||||||
|
participant.cpp
|
||||||
|
participant.h
|
||||||
|
presence.cpp
|
||||||
|
presence.h
|
||||||
|
reference.cpp
|
||||||
|
reference.h
|
||||||
|
room.cpp
|
||||||
|
room.h
|
||||||
|
roster.cpp
|
||||||
|
roster.h
|
||||||
|
)
|
@ -22,7 +22,7 @@ using namespace Models;
|
|||||||
|
|
||||||
Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, const QMap<QString, QVariant>& data, Models::Item* parentItem):
|
Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, const QMap<QString, QVariant>& data, Models::Item* parentItem):
|
||||||
Item(p_type, data, parentItem),
|
Item(p_type, data, parentItem),
|
||||||
availability(Shared::offline),
|
availability(Shared::Availability::offline),
|
||||||
lastActivity(data.value("lastActivity").toDateTime()),
|
lastActivity(data.value("lastActivity").toDateTime()),
|
||||||
status(data.value("status").toString())
|
status(data.value("status").toString())
|
||||||
{
|
{
|
||||||
@ -58,7 +58,7 @@ QVariant Models::AbstractParticipant::data(int column) const
|
|||||||
case 1:
|
case 1:
|
||||||
return lastActivity;
|
return lastActivity;
|
||||||
case 2:
|
case 2:
|
||||||
return availability;
|
return QVariant::fromValue(availability);
|
||||||
case 3:
|
case 3:
|
||||||
return status;
|
return status;
|
||||||
default:
|
default:
|
||||||
@ -91,15 +91,9 @@ void Models::AbstractParticipant::setAvailability(Shared::Availability p_avail)
|
|||||||
|
|
||||||
void Models::AbstractParticipant::setAvailability(unsigned int avail)
|
void Models::AbstractParticipant::setAvailability(unsigned int avail)
|
||||||
{
|
{
|
||||||
if (avail <= Shared::availabilityHighest) {
|
setAvailability(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
Shared::Availability state = static_cast<Shared::Availability>(avail);
|
|
||||||
setAvailability(state);
|
|
||||||
} else {
|
|
||||||
qDebug("An attempt to set wrong state to the contact");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time)
|
void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time)
|
||||||
{
|
{
|
||||||
if (lastActivity != p_time) {
|
if (lastActivity != p_time) {
|
||||||
|