31 Commits

Author SHA1 Message Date
Blue 1706b93b59 Merge pull request 'Add Brazilian Portuguese translations' (#49) from brunofontes/squawk:ptBR_translations into master 3 days ago
Bruno F. Fontes 87c216b491
Add Brazilian Portuguese translations 2 months ago
Blue ef1a9846bf found a way to avoid requesting all the MUCs history alltogether on startup 1 year ago
Blue 3a120c773a reconnection issues 1 year ago
Blue 5f64321c2a fix compilation on older qt versions 1 year ago
Blue a543eb1aef 0.1.5 1 year ago
Blue 480c78cf61 non lower cased jids error handled 1 year ago
Blue 0dcfc5eedc some better cleanup and restore state on connect disconnect, workaround for that wired undefined condition error on every other reconnection 1 year ago
Blue 87426ee20f account refactoring 1 year ago
Blue 20bcae5ab2 finally history works in mucs 1 year ago
Blue 6b65910ded stanzaId based muc archive request, account object refactoring 1 year ago
Blue 9ca4aa29d4 started account refactoring 1 year ago
Blue a625ecb47b Merge pull request 'rosterReferences' (#39) from rosterReferences into master 1 year ago
Blue 55ae5858b5 some workaround about disconnection segfault 1 year ago
Blue 9c855553c5 referencing seems to be working now 1 year ago
Blue 83a2e6af85 first prototype 1 year ago
Blue 494afcf2b5 initial class reference 1 year ago
Blue a8698cc94f minor bugfixes 1 year ago
Blue b50ce146b8 attaching messages fix, bad alloc fix 1 year ago
Blue 6657dc79ce qxmpp bump, ifdef to assemble on lower versions 1 year ago
Blue bdca0aa6b1 Merge pull request 'safePasswords' (#36) from safePasswords into master 1 year ago
Blue cb44b12a7e 0.1.4 kwallet optimisation related fix, DnD files into convs, visual fixes 1 year ago
Blue 21c7d65027 offline avatars in mucs 1 year ago
Blue 29c7d31c89 context menu now doesn't select items, just temporarily, statuses and messahes html wrapping fix 1 year ago
Blue a77dfd191a single window mode 1 year ago
Blue b95028e33e testing, ability to build without kwallet, translations, disabling unsupported storage types in combobox 1 year ago
Blue 543538fc56 first working prototype of dynamically loaded kwallet storage 1 year ago
Blue 7ce27d1c11 pasword storing options: jammed an alwaysAsk, external lib for password jamming, changelog 1 year ago
Blue 95f0d4008a minor fix about updating muc avatars 1 year ago
Blue 3477226367 first moves to safe pasword storing, preparing the structure 1 year ago
Blue ddfb3419cc Shared namespace refactoring, enum class refactoring, going offline related crash fix 1 year ago
  1. 98
      CHANGELOG.md
  2. 62
      CMakeLists.txt
  3. 10
      README.md
  4. 11
      core/CMakeLists.txt
  5. 1299
      core/account.cpp
  6. 114
      core/account.h
  7. 165
      core/archive.cpp
  8. 19
      core/archive.h
  9. 98
      core/conference.cpp
  10. 15
      core/conference.h
  11. 2
      core/contact.cpp
  12. 371
      core/handlers/messagehandler.cpp
  13. 76
      core/handlers/messagehandler.h
  14. 599
      core/handlers/rosterhandler.cpp
  15. 115
      core/handlers/rosterhandler.h
  16. 13
      core/networkaccess.cpp
  17. 37
      core/passwordStorageEngines/CMakeLists.txt
  18. 230
      core/passwordStorageEngines/kwallet.cpp
  19. 112
      core/passwordStorageEngines/kwallet.h
  20. 33
      core/passwordStorageEngines/wrappers/kwallet.cpp
  21. 137
      core/rosteritem.cpp
  22. 16
      core/rosteritem.h
  23. 283
      core/squawk.cpp
  24. 55
      core/squawk.h
  25. 2
      external/qxmpp
  26. 16
      external/simpleCrypt/CMakeLists.txt
  27. 248
      external/simpleCrypt/simplecrypt.cpp
  28. 226
      external/simpleCrypt/simplecrypt.h
  29. 765
      global.cpp
  30. 512
      global.h
  31. 11
      main.cpp
  32. 2
      order.h
  33. 6
      packaging/Archlinux/PKGBUILD
  34. 2
      packaging/squawk.desktop
  35. 29
      shared.h
  36. 121
      shared/enums.h
  37. 174
      shared/global.cpp
  38. 93
      shared/global.h
  39. 96
      shared/icons.cpp
  40. 176
      shared/icons.h
  41. 434
      shared/message.cpp
  42. 130
      shared/message.h
  43. 48
      shared/utils.cpp
  44. 74
      shared/utils.h
  45. 288
      shared/vcard.cpp
  46. 152
      shared/vcard.h
  47. 4
      signalcatcher.cpp
  48. 795
      translations/squawk.pt_BR.ts
  49. 424
      translations/squawk.ru.ts
  50. 1
      ui/CMakeLists.txt
  51. 12
      ui/models/abstractparticipant.cpp
  52. 6
      ui/models/abstractparticipant.h
  53. 63
      ui/models/account.cpp
  54. 13
      ui/models/account.h
  55. 2
      ui/models/accounts.cpp
  56. 72
      ui/models/contact.cpp
  57. 13
      ui/models/contact.h
  58. 42
      ui/models/group.cpp
  59. 6
      ui/models/group.h
  60. 143
      ui/models/item.cpp
  61. 32
      ui/models/item.h
  62. 29
      ui/models/participant.cpp
  63. 1
      ui/models/participant.h
  64. 1
      ui/models/presence.cpp
  65. 4
      ui/models/presence.h
  66. 177
      ui/models/reference.cpp
  67. 66
      ui/models/reference.h
  68. 37
      ui/models/room.cpp
  69. 5
      ui/models/room.h
  70. 320
      ui/models/roster.cpp
  71. 8
      ui/models/roster.h
  72. 378
      ui/squawk.cpp
  73. 24
      ui/squawk.h
  74. 160
      ui/squawk.ui
  75. 2
      ui/utils/flowlayout.cpp
  76. 33
      ui/utils/message.cpp
  77. 5
      ui/utils/message.h
  78. 86
      ui/utils/messageline.cpp
  79. 6
      ui/utils/messageline.h
  80. 2
      ui/utils/progress.cpp
  81. 2
      ui/utils/progress.h
  82. 29
      ui/widgets/account.cpp
  83. 8
      ui/widgets/account.h
  84. 34
      ui/widgets/account.ui
  85. 17
      ui/widgets/accounts.cpp
  86. 10
      ui/widgets/accounts.h
  87. 15
      ui/widgets/chat.cpp
  88. 6
      ui/widgets/chat.h
  89. 144
      ui/widgets/conversation.cpp
  90. 17
      ui/widgets/conversation.h
  91. 53
      ui/widgets/conversation.ui
  92. 18
      ui/widgets/room.cpp
  93. 2
      ui/widgets/room.h
  94. 3
      ui/widgets/vcard/emailsmodel.cpp
  95. 2
      ui/widgets/vcard/emailsmodel.h
  96. 3
      ui/widgets/vcard/phonesmodel.cpp
  97. 2
      ui/widgets/vcard/phonesmodel.h
  98. 8
      ui/widgets/vcard/vcard.cpp
  99. 2
      ui/widgets/vcard/vcard.h

98
CHANGELOG.md

@ -0,0 +1,98 @@
# Changelog
## Squawk 0.2.0 (Unreleased)
### 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
### Improvements
- slightly reduced the traffic on the startup by not requesting history of all MUCs
## 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

62
CMakeLists.txt

@ -25,9 +25,25 @@ message("Build type: ${CMAKE_BUILD_TYPE}")
set(squawk_SRC
main.cpp
global.cpp
exception.cpp
signalcatcher.cpp
shared/global.cpp
shared/utils.cpp
shared/message.cpp
shared/vcard.cpp
shared/icons.cpp
)
set(squawk_HEAD
exception.h
signalcatcher.h
shared.h
shared/enums.h
shared/message.h
shared/global.h
shared/utils.h
shared/vcard.h
shared/icons.h
)
configure_file(resources/images/logo.svg squawk.svg COPYONLY)
@ -46,18 +62,44 @@ 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(WITH_KWALLET "Build KWallet support module" ON)
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)
add_subdirectory(external/qxmpp)
endif()
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()
add_definitions(-DWITH_KWALLET)
message("Building with support of KWallet")
endif()
endif()
add_executable(squawk ${squawk_SRC} ${squawk_HEAD} ${RCC})
target_link_libraries(squawk Qt5::Widgets)
add_subdirectory(ui)
add_subdirectory(core)
add_subdirectory(external/simpleCrypt)
target_link_libraries(squawk squawkUI)
target_link_libraries(squawk squawkCORE)
target_link_libraries(squawk uuid)
@ -67,9 +109,9 @@ add_dependencies(${CMAKE_PROJECT_NAME} translations)
# Install the executable
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
install(FILES squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
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)
install(FILES squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
install(FILES squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
install(FILES squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk48.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk64.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk128.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk256.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps RENAME squawk.png)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)

10
README.md

@ -4,7 +4,7 @@
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
![Squawk screenshot](https://macaw.me/images/squawk/0.1.3.png)
![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png)
### Prerequisites
@ -13,6 +13,7 @@
- lmdb
- CMake 3.0 or higher
- qxmpp 1.1.0 or higher
- kwallet (optional)
### Getting
@ -60,6 +61,13 @@ $ cmake .. -D SYSTEM_QXMPP=False
$ cmake --build .
```
### 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`)
## License
This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details

11
core/CMakeLists.txt

@ -18,18 +18,21 @@ set(squawkCORE_SRC
storage.cpp
networkaccess.cpp
adapterFuctions.cpp
handlers/messagehandler.cpp
handlers/rosterhandler.cpp
)
add_subdirectory(passwordStorageEngines)
# 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)
@ -37,3 +40,7 @@ target_link_libraries(squawkCORE Qt5::Gui)
target_link_libraries(squawkCORE Qt5::Xml)
target_link_libraries(squawkCORE qxmpp)
target_link_libraries(squawkCORE lmdb)
target_link_libraries(squawkCORE simpleCrypt)
if (WITH_KWALLET)
target_link_libraries(squawkCORE kwalletPSE)
endif()

1299
core/account.cpp
File diff suppressed because it is too large
View File

114
core/account.h

@ -25,6 +25,7 @@
#include <QMimeDatabase>
#include <QStandardPaths>
#include <QDir>
#include <QTimer>
#include <map>
#include <set>
@ -38,30 +39,36 @@
#include <QXmppBookmarkManager.h>
#include <QXmppBookmarkSet.h>
#include <QXmppUploadRequestManager.h>
#include <QXmppHttpUploadIq.h>
#include <QXmppVCardIq.h>
#include <QXmppVCardManager.h>
#include <QXmppMessageReceiptManager.h>
#include "global.h"
#include "shared.h"
#include "contact.h"
#include "conference.h"
#include "networkaccess.h"
#include "handlers/messagehandler.h"
#include "handlers/rosterhandler.h"
namespace Core
{
class Account : public QObject
{
Q_OBJECT
friend class MessageHandler;
friend class RosterHandler;
public:
Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0);
Account(
const QString& p_login,
const QString& p_server,
const QString& p_password,
const QString& p_name,
NetworkAccess* p_net,
QObject* parent = 0);
~Account();
void connect();
void disconnect();
void reconnect();
Shared::ConnectionState getState() const;
QString getName() const;
QString getLogin() const;
@ -70,6 +77,7 @@ public:
QString getResource() const;
QString getAvatarPath() const;
Shared::Availability getAvailability() const;
Shared::AccountPassword getPasswordType() const;
void setName(const QString& p_name);
void setLogin(const QString& p_login);
@ -77,11 +85,11 @@ public:
void setPassword(const QString& p_password);
void setResource(const QString& p_resource);
void setAvailability(Shared::Availability avail);
void setPasswordType(Shared::AccountPassword pt);
QString getFullJid() const;
void sendMessage(Shared::Message data);
void sendMessage(const Shared::Message& data);
void sendMessage(const Shared::Message& data, const QString& path);
void requestArchive(const QString& jid, int count, const QString& before);
void setReconnectTimes(unsigned int times);
void subscribeToContact(const QString& jid, const QString& reason);
void unsubscribeFromContact(const QString& jid, const QString& reason);
void removeContactRequest(const QString& jid);
@ -97,12 +105,15 @@ public:
void uploadVCard(const Shared::VCard& card);
public slots:
void connect();
void disconnect();
void reconnect();
void requestVCard(const QString& jid);
signals:
void changed(const QMap<QString, QVariant>& data);
void connectionStateChanged(int);
void availabilityChanged(int);
void connectionStateChanged(Shared::ConnectionState);
void availabilityChanged(Shared::Availability);
void addGroup(const QString& name);
void removeGroup(const QString& name);
void addRoom(const QString& jid, const QMap<QString, QVariant>& data);
@ -127,12 +138,11 @@ signals:
private:
QString name;
std::map<QString, QString> achiveQueries;
std::map<QString, QString> archiveQueries;
QXmppClient client;
QXmppConfiguration config;
QXmppPresence presence;
Shared::ConnectionState state;
std::map<QString, std::set<QString>> groups;
QXmppCarbonManager* cm;
QXmppMamManager* am;
QXmppMucManager* mm;
@ -142,93 +152,41 @@ private:
QXmppUploadRequestManager* um;
QXmppDiscoveryManager* dm;
QXmppMessageReceiptManager* rcpm;
std::map<QString, Contact*> contacts;
std::map<QString, Conference*> conferences;
unsigned int maxReconnectTimes;
unsigned int reconnectTimes;
bool reconnectScheduled;
QTimer* reconnectTimer;
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;
std::map<QString, QString> pendingStateMessages;
Shared::AccountPassword passwordType;
MessageHandler* mh;
RosterHandler* rh;
private slots:
void onClientConnected();
void onClientDisconnected();
void onClientStateChange(QXmppClient::State state);
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 onMessageReceived(const QXmppMessage& message);
void onCarbonMessageReceived(const QXmppMessage& message);
void onCarbonMessageSent(const QXmppMessage& message);
void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at);
void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message);
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 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 onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
void onReceiptReceived(const QString& jid, const QString &id);
private:
void addedAccount(const QString &bareJid);
void handleNewContact(Contact* contact);
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 handleDisconnection();
void onReconnectTimer();
};
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);

165
core/archive.cpp

@ -67,6 +67,7 @@ void Core::Archive::open(const QString& account)
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
mdb_txn_commit(txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
@ -99,6 +100,7 @@ void Core::Archive::open(const QString& account)
void Core::Archive::close()
{
if (opened) {
mdb_dbi_close(environment, sid);
mdb_dbi_close(environment, avatars);
mdb_dbi_close(environment, stats);
mdb_dbi_close(environment, order);
@ -139,12 +141,36 @@ bool Core::Archive::addElement(const Shared::Message& message)
mdb_txn_abort(txn);
return false;
} else {
rc = mdb_txn_commit(txn);
if (rc) {
qDebug() << "A transaction error: " << mdb_strerror(rc);
return false;
if (message.getStanzaId().size() > 0) {
const std::string& szid = message.getStanzaId().toStdString();
lmdbKey.mv_size = szid.size();
lmdbKey.mv_data = (char*)szid.c_str();
lmdbData.mv_size = id.size();
lmdbData.mv_data = (uint8_t*)id.data();
rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
if (rc) {
qDebug() << "An element stanzaId to id pair couldn't be inserted into the archive" << mdb_strerror(rc);
mdb_txn_abort(txn);
return false;
} else {
rc = mdb_txn_commit(txn);
if (rc) {
qDebug() << "A transaction error: " << mdb_strerror(rc);
return false;
}
return true;
}
} else {
rc = mdb_txn_commit(txn);
if (rc) {
qDebug() << "A transaction error: " << mdb_strerror(rc);
return false;
}
return true;
}
return true;
}
} else {
qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc);
@ -164,10 +190,12 @@ void Core::Archive::clear()
mdb_drop(txn, main, 0);
mdb_drop(txn, order, 0);
mdb_drop(txn, stats, 0);
mdb_drop(txn, avatars, 0);
mdb_drop(txn, sid, 0);
mdb_txn_commit(txn);
}
Shared::Message Core::Archive::getElement(const QString& id)
Shared::Message Core::Archive::getElement(const QString& id) const
{
if (!opened) {
throw Closed("getElement", jid.toStdString());
@ -186,7 +214,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;
lmdbKey.mv_size = id.size();
@ -220,6 +268,7 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
std::string strId(id.toStdString());
try {
Shared::Message msg = getMessage(strId, txn);
bool hadStanzaId = msg.getStanzaId().size() > 0;
QDateTime oTime = msg.getTime();
bool idChange = msg.change(data);
@ -250,6 +299,19 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
}
if (msg.getStanzaId().size() > 0 && (idChange || !hadStanzaId)) {
const std::string& szid = msg.getStanzaId().toStdString();
lmdbData.mv_size = szid.size();
lmdbData.mv_data = (char*)szid.c_str();
rc = mdb_put(txn, sid, &lmdbData, &lmdbKey, 0);
if (rc != 0) {
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
};
lmdbData.mv_size = ba.size();
lmdbData.mv_data = (uint8_t*)ba.data();
rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0);
@ -395,7 +457,20 @@ unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messag
if (rc) {
qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc);
} 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++;
}
} else {
@ -536,6 +611,46 @@ void Core::Archive::setFromTheBeginning(bool is)
}
}
QString Core::Archive::idByStanzaId(const QString& stanzaId) const
{
if (!opened) {
throw Closed("idByStanzaId", jid.toStdString());
}
QString id;
std::string ssid = stanzaId.toStdString();
MDB_txn *txn;
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = ssid.size();
lmdbKey.mv_data = (char*)ssid.c_str();
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
int rc = mdb_get(txn, sid, &lmdbKey, &lmdbData);
if (rc == 0) {
id = QString::fromStdString(std::string((char*)lmdbData.mv_data, lmdbData.mv_size));
}
mdb_txn_abort(txn);
return id;
}
QString Core::Archive::stanzaIdById(const QString& id) const
{
if (!opened) {
throw Closed("stanzaIdById", jid.toStdString());
}
try {
Shared::Message msg = getElement(id);
return msg.getStanzaId();
} catch (const NotFound& e) {
return QString();
} catch (const Empty& e) {
return QString();
} catch (...) {
throw;
}
}
void Core::Archive::printOrder()
{
qDebug() << "Printing order";
@ -675,7 +790,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) {
throw Closed("setAvatar", jid.toStdString());
@ -726,7 +841,9 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QStr
MDB_val lmdbKey, lmdbData;
QByteArray value;
AvatarInfo newInfo(ext, newHash, generated);
newInfo.type = ext;
newInfo.hash = newHash;
newInfo.autogenerated = generated;
newInfo.serialize(&value);
lmdbKey.mv_size = res.size();
lmdbKey.mv_data = (char*)res.c_str();
@ -802,6 +919,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
{
if (!opened) {

19
core/archive.h

@ -24,7 +24,7 @@
#include <QMimeDatabase>
#include <QMimeType>
#include "global.h"
#include "shared/message.h"
#include "exception.h"
#include <lmdb.h>
#include <list>
@ -45,7 +45,8 @@ public:
bool addElement(const Shared::Message& message);
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);
Shared::Message oldest();
QString oldestId();
@ -56,9 +57,12 @@ public:
std::list<Shared::Message> getBefore(int count, const QString& id);
bool isFromTheBeginning();
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;
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
QString idByStanzaId(const QString& stanzaId) const;
QString stanzaIdById(const QString& id) const;
public:
const QString jid;
@ -168,10 +172,11 @@ private:
bool opened;
bool fromTheBeginning;
MDB_env* environment;
MDB_dbi main;
MDB_dbi order;
MDB_dbi main; //id to message
MDB_dbi order; //time to id
MDB_dbi stats;
MDB_dbi avatars;
MDB_dbi avatars;
MDB_dbi sid; //stanzaId to id
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
@ -182,7 +187,7 @@ private:
void printOrder();
void printKeys();
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 edge(bool end);
};

98
core/conference.cpp

@ -25,7 +25,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
nick(p_nick),
room(p_room),
joined(false),
autoJoin(p_autoJoin)
autoJoin(p_autoJoin),
exParticipants()
{
muc = true;
name = p_name;
@ -44,6 +45,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
if (autoJoin) {
room->join();
}
archive->readAllResourcesAvatars(exParticipants);
}
Core::Conference::~Conference()
@ -134,17 +137,20 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
{
QStringList comps = p_name.split("/");
QString resource = comps.back();
QXmppPresence pres = room->participantPresence(p_name);
QXmppMucItem mi = pres.mucItem();
if (resource == jid) {
qDebug() << "Room" << jid << "is reporting of adding itself to the list participants. Not sure what to do with that yet, skipping";
} else {
QXmppPresence pres = room->participantPresence(p_name);
resource = "";
}
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
bool hasAvatar = itr != exParticipants.end();
if (resource.size() > 0) {
QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc();
}
QXmppMucItem mi = pres.mucItem();
Archive::AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, resource);
QMap<QString, QVariant> cData = {
{"lastActivity", lastInteraction},
@ -155,12 +161,12 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
};
if (hasAvatar) {
if (info.autogenerated) {
if (itr->second.autogenerated) {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
}
cData.insert("avatarPath", avatarPath(resource) + "." + info.type);
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
} else {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarPath", "");
@ -169,22 +175,43 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
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)
{
QStringList comps = p_name.split("/");
QString resource = comps.back();
if (resource == jid) {
qDebug() << "Room" << jid << "is reporting of changing his own presence. Not sure what to do with that yet, skipping";
} else {
QXmppPresence pres = room->participantPresence(p_name);
QXmppPresence pres = room->participantPresence(p_name);
QXmppMucItem mi = pres.mucItem();
handlePresence(pres);
if (resource != jid) {
QDateTime lastInteraction = pres.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTimeUtc();
}
QXmppMucItem mi = pres.mucItem();
handlePresence(pres);
emit changeParticipant(resource, {
{"lastActivity", lastInteraction},
@ -261,30 +288,46 @@ void Core::Conference::handlePresence(const QXmppPresence& pres)
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) {
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, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
{"availability", avatarPath(resource) + ".png"}
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
});
}
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 (data.size() > 0) {
QMimeDatabase db;
QMimeType type = db.mimeTypeForData(data);
QString ext = type.preferredSuffix();
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr == exParticipants.end()) {
exParticipants.insert(std::make_pair(resource, info));
} else {
itr->second = info;
}
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
{"avatarPath", avatarPath(resource) + "." + ext}
{"avatarPath", avatarPath(resource) + "." + info.type}
});
} else {
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
if (itr != exParticipants.end()) {
exParticipants.erase(itr);
}
emit changeParticipant(resource, {
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
{"avatarPath", ""}
@ -309,3 +352,12 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co
return result;
}
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
{
QMap<QString, QVariant> result;
for (const std::pair<QString, Archive::AvatarInfo>& pair : exParticipants) {
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
}
return result;
}

15
core/conference.h

@ -19,9 +19,15 @@
#ifndef CORE_CONFERENCE_H
#define CORE_CONFERENCE_H
#include "rosteritem.h"
#include <QDir>
#include <QXmppMucManager.h>
#include <set>
#include "rosteritem.h"
#include "shared/global.h"
namespace Core
{
@ -46,8 +52,8 @@ public:
void setAutoJoin(bool p_autoJoin);
void handlePresence(const QXmppPresence & pres) 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;
QMap<QString, QVariant> getAllAvatars() const;
signals:
void nickChanged(const QString& nick);
@ -58,11 +64,16 @@ signals:
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
void removeParticipant(const QString& name);
protected:
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
private:
QString nick;
QXmppMucRoom* room;
bool joined;
bool autoJoin;
std::map<QString, Archive::AvatarInfo> exParticipants;
static const std::set<QString> supportedList;
private slots:
void onRoomJoined();

2
core/contact.cpp

@ -22,7 +22,7 @@
Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent):
RosterItem(pJid, account, parent),
groups(),
subscriptionState(Shared::unknown)
subscriptionState(Shared::SubscriptionState::unknown)
{
}

371
core/handlers/messagehandler.cpp

@ -0,0 +1,371 @@
/*
* 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(),
pendingMessages(),
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: {
QString id = msg.id();
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
if (itr != pendingStateMessages.end()) {
QString jid = itr->second;
RosterItem* cnt = acc->rh->getRosterItem(jid);
QMap<QString, QVariant> cData = {
{"state", static_cast<uint>(Shared::Message::State::error)},
{"errorText", msg.error().text()}
};
if (cnt != 0) {
cnt->changeMessage(id, cData);
}
;
emit acc->changeMessage(jid, id, cData);
pendingStateMessages.erase(itr);
handled = true;
} else {
qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping";
}
}
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)
{
const QString& body(msg.body());
if (body.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);
}
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) {
QString id = msg.id();
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::map<QString, QString>::const_iterator pItr = pendingStateMessages.find(id);
if (pItr != pendingStateMessages.end()) {
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
cnt->changeMessage(id, cData);
pendingStateMessages.erase(pItr);
emit acc->changeMessage(jid, id, cData);
} else {
QString oId = msg.replaceId();
if (oId.size() > 0) {
QMap<QString, QVariant> cData = {
{"body", sMsg.getBody()},
{"stamp", sMsg.getTime()}
};
cnt->correctMessageInArchive(oId, sMsg);
emit acc->changeMessage(jid, oId, cData);
} else {
cnt->appendMessageToArchive(sMsg);
QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60);
if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt
emit acc->message(sMsg);
} else {
//qDebug() << "Delayed delivery: ";
}
}
}
return true;
}
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());
#else
id = source.id();
#endif
target.setId(id);
if (target.getId().size() == 0) {
target.generateRandomId(); //TODO out of desperation, I need at least a random ID
}
target.setFrom(source.from());
target.setTo(source.to());
target.setBody(source.body());
target.setForwarded(forwarded);
target.setOutOfBandUrl(source.outOfBandUrl());
if (guessing) {
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
outgoing = true;
} else {
outgoing = false;
}
}
target.setOutgoing(outgoing);
if (time.isValid()) {
target.setTime(time);
} else {
target.setCurrentTime();
}
}
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);
}
void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id)
{
std::map<QString, QString>::const_iterator itr = pendingStateMessages.find(id);
if (itr != pendingStateMessages.end()) {
QMap<QString, QVariant> cData = {{"state", static_cast<uint>(Shared::Message::State::delivered)}};
RosterItem* ri = acc->rh->getRosterItem(itr->second);
if (ri != 0) {
ri->changeMessage(id, cData);
}
pendingStateMessages.erase(itr);
emit acc->changeMessage(itr->second, id, cData);
}
}
void Core::MessageHandler::sendMessage(Shared::Message data)
{
QString jid = data.getPenPalJid();
QString id = data.getId();
RosterItem* ri = acc->rh->getRosterItem(jid);
if (acc->state == Shared::ConnectionState::connected) {
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
#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);
bool sent = acc->client.sendPacket(msg);
if (sent) {
data.setState(Shared::Message::State::sent);
} else {
data.setState(Shared::Message::State::error);