59 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
Blue b309100f99 0.1.3 release 2 years ago
Blue c793f56647 a little bit of restyling, now the integration with transparent themes is supposed to be better 2 years ago
Blue ff2c9831cf corrected messages now are supposed to display correctly 2 years ago
Blue fe1ae8567a Message receipt manager not takes care of the message reception, switched on embedded reconnect 2 years ago
Blue 57d6e3adab Message error handling as state, errorText to store, fake ID for message without 2 years ago
Blue 91cc851bfc delivery statuses now actually mean something for MUC messages 2 years ago
Blue 6d1b83d0f8 ui logick of changing message, not connected yet 2 years ago
Blue ed56cca2e7 some visual tweaks, moving on message delivery statuses 2 years ago
Blue a4136ff9fe progress spinner fix, new lines in messages now display again 2 years ago
Blue 565449f176 bug with selection, some right padding to the avatars 2 years ago
Blue 626227db93 file comment fix, avatar dropping bug fix, url detection bug fix 2 years ago
Blue 13a1970047 a method for setting states to messages 2 years ago
Blue ad1977f05f badges and screenshots in readme typo 2 years ago
Blue 0a4c4aa042 badges and screenshots in readme typo 2 years ago
Blue 5e7c247bb4 badges and screenshots in readme 2 years ago
Blue 5a59d54b18 first thoughts on message states 2 years ago
Blue 52efc2b1a4 now we have avatars in muc chats 2 years ago
Blue 55703c2007 muc participant avatars 2 years ago
Blue efc90e18c3 verion bump 2 years ago
Blue 3e594c7e13 connectivity, roster position size and state, expanded anccounts and groups restoration with the settings 2 years ago
Blue 0bcfd779b8 some bugs and corner cases 2 years ago
Blue dd62f84acc pal avatars in one on one dialogs 2 years ago
Blue f13b43d38b some bug fixes, initial work on avatars in dialog windows 2 years ago
Blue 867c3a18e9 avatars for mucs, some tooltip tweaks 2 years ago
Blue f367502b8e Merge branch 'master' of git.macaw.me:blue/squawk 2 years ago
Blue fe27955689 uploading multiple messages fix, some warnings fix 2 years ago
Blue 0c33d81c59 insignifican fixes about urls parsing 2 years ago
Blue 0cb25a25cf uploading multiple messages fix, some warnings fix 2 years ago
  1. 98
      CHANGELOG.md
  2. 62
      CMakeLists.txt
  3. 16
      README.md
  4. 11
      core/CMakeLists.txt
  5. 1336
      core/account.cpp
  6. 116
      core/account.h
  7. 629
      core/archive.cpp
  8. 67
      core/archive.h
  9. 187
      core/conference.cpp
  10. 17
      core/conference.h
  11. 32
      core/contact.cpp
  12. 1
      core/contact.h
  13. 371
      core/handlers/messagehandler.cpp
  14. 76
      core/handlers/messagehandler.h
  15. 599
      core/handlers/rosterhandler.cpp
  16. 115
      core/handlers/rosterhandler.h
  17. 35
      core/networkaccess.cpp
  18. 37
      core/passwordStorageEngines/CMakeLists.txt
  19. 230
      core/passwordStorageEngines/kwallet.cpp
  20. 112
      core/passwordStorageEngines/kwallet.h
  21. 33
      core/passwordStorageEngines/wrappers/kwallet.cpp
  22. 285
      core/rosteritem.cpp
  23. 34
      core/rosteritem.h
  24. 291
      core/squawk.cpp
  25. 57
      core/squawk.h
  26. 2
      external/qxmpp
  27. 16
      external/simpleCrypt/CMakeLists.txt
  28. 248
      external/simpleCrypt/simplecrypt.cpp
  29. 226
      external/simpleCrypt/simplecrypt.h
  30. 630
      global.cpp
  31. 483
      global.h
  32. 15
      main.cpp
  33. 2
      order.h
  34. 8
      packaging/Archlinux/PKGBUILD
  35. 2
      packaging/squawk.desktop
  36. 29
      shared.h
  37. 121
      shared/enums.h
  38. 174
      shared/global.cpp
  39. 93
      shared/global.h
  40. 96
      shared/icons.cpp
  41. 176
      shared/icons.h
  42. 434
      shared/message.cpp
  43. 130
      shared/message.h
  44. 48
      shared/utils.cpp
  45. 74
      shared/utils.h
  46. 288
      shared/vcard.cpp
  47. 152
      shared/vcard.h
  48. 4
      signalcatcher.cpp
  49. 795
      translations/squawk.pt_BR.ts
  50. 420
      translations/squawk.ru.ts
  51. 2
      ui/CMakeLists.txt
  52. 12
      ui/models/abstractparticipant.cpp
  53. 6
      ui/models/abstractparticipant.h
  54. 63
      ui/models/account.cpp
  55. 13
      ui/models/account.h
  56. 2
      ui/models/accounts.cpp
  57. 95
      ui/models/contact.cpp
  58. 14
      ui/models/contact.h
  59. 42
      ui/models/group.cpp
  60. 6
      ui/models/group.h
  61. 143
      ui/models/item.cpp
  62. 32
      ui/models/item.h
  63. 64
      ui/models/participant.cpp
  64. 11
      ui/models/participant.h
  65. 14
      ui/models/presence.cpp
  66. 5
      ui/models/presence.h
  67. 177
      ui/models/reference.cpp
  68. 66
      ui/models/reference.h
  69. 127
      ui/models/room.cpp
  70. 19
      ui/models/room.h
  71. 384
      ui/models/roster.cpp
  72. 15
      ui/models/roster.h
  73. 513
      ui/squawk.cpp
  74. 29
      ui/squawk.h
  75. 152
      ui/squawk.ui
  76. 701
      ui/utils/dropshadoweffect.cpp
  77. 93
      ui/utils/dropshadoweffect.h
  78. 2
      ui/utils/flowlayout.cpp
  79. 40
      ui/utils/image.cpp
  80. 6
      ui/utils/image.h
  81. 133
      ui/utils/message.cpp
  82. 25
      ui/utils/message.h
  83. 202
      ui/utils/messageline.cpp
  84. 12
      ui/utils/messageline.h
  85. 6
      ui/utils/progress.cpp
  86. 2
      ui/utils/progress.h
  87. 29
      ui/widgets/account.cpp
  88. 8
      ui/widgets/account.h
  89. 34
      ui/widgets/account.ui
  90. 17
      ui/widgets/accounts.cpp
  91. 10
      ui/widgets/accounts.h
  92. 37
      ui/widgets/chat.cpp
  93. 9
      ui/widgets/chat.h
  94. 236
      ui/widgets/conversation.cpp
  95. 42
      ui/widgets/conversation.h
  96. 805
      ui/widgets/conversation.ui
  97. 61
      ui/widgets/room.cpp
  98. 6
      ui/widgets/room.h
  99. 13
      ui/widgets/vcard/emailsmodel.cpp
  100. 10
      ui/widgets/vcard/emailsmodel.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)

16
README.md

@ -1,6 +1,10 @@
# Sqwawk
# Squawk - a compact XMPP desktop messenger
A compact XMPP desktop messenger
[![AUR license](https://img.shields.io/aur/license/squawk?style=flat-square)](https://git.macaw.me/blue/squawk/raw/branch/master/LICENSE.md)
[![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.4.png)
### Prerequisites
@ -9,6 +13,7 @@ A compact XMPP desktop messenger
- lmdb
- CMake 3.0 or higher
- qxmpp 1.1.0 or higher
- kwallet (optional)
### Getting
@ -56,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()

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

116
core/account.h

@ -25,6 +25,7 @@
#include <QMimeDatabase>
#include <QStandardPaths>
#include <QDir>
#include <QTimer>
#include <map>
#include <set>
@ -38,28 +39,36 @@
#include <QXmppBookmarkManager.h>
#include <QXmppBookmarkSet.h>
#include <QXmppUploadRequestManager.h>
#include <QXmppHttpUploadIq.h>
#include <QXmppVCardIq.h>
#include <QXmppVCardManager.h>
#include "global.h"
#include <QXmppMessageReceiptManager.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;
@ -68,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);
@ -75,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(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);
@ -92,13 +102,18 @@ public:
void setRoomAutoJoin(const QString& jid, bool joined);
void removeRoomRequest(const QString& jid);
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
void requestVCard(const QString& jid);
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);
@ -111,6 +126,7 @@ signals:
void addPresence(const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removePresence(const QString& jid, const QString& name);
void message(const Shared::Message& 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 error(const QString& text);
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
@ -122,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;
@ -136,89 +151,42 @@ private:
QXmppVCardManager* vm;
QXmppUploadRequestManager* um;
QXmppDiscoveryManager* dm;
std::map<QString, Contact*> contacts;
std::map<QString, Conference*> conferences;
unsigned int maxReconnectTimes;
unsigned int reconnectTimes;
QXmppMessageReceiptManager* rcpm;
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;
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);
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);
void handleDisconnection();
void onReconnectTimer();
};
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);

629
core/archive.cpp

@ -33,10 +33,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent):
main(),
order(),
stats(),
hasAvatar(false),
avatarAutoGenerated(false),
avatarHash(),
avatarType()
avatars()
{
}
@ -60,7 +57,7 @@ void Core::Archive::open(const QString& account)
}
}
mdb_env_set_maxdbs(environment, 4);
mdb_env_set_maxdbs(environment, 5);
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
@ -69,43 +66,26 @@ void Core::Archive::open(const QString& account)
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
mdb_dbi_open(txn, "sid", MDB_CREATE, &sid);
mdb_txn_commit(txn);
mdb_txn_begin(environment, NULL, 0, &txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
try {
fromTheBeginning = getStatBoolValue("beginning", txn);
} catch (NotFound e) {
} catch (const NotFound& e) {
fromTheBeginning = false;
}
try {
hasAvatar = getStatBoolValue("hasAvatar", txn);
} catch (NotFound e) {
hasAvatar = false;
}
if (hasAvatar) {
try {
avatarAutoGenerated = getStatBoolValue("avatarAutoGenerated", txn);
} catch (NotFound e) {
avatarAutoGenerated = false;
}
avatarType = getStatStringValue("avatarType", txn).c_str();
if (avatarAutoGenerated) {
avatarHash = "";
} else {
avatarHash = getStatStringValue("avatarHash", txn).c_str();
}
} else {
avatarAutoGenerated = false;
avatarHash = "";
avatarType = "";
}
std::string sJid = jid.toStdString();
AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, sJid, txn);
mdb_txn_abort(txn);
if (hasAvatar) {
QFile ava(path + "/avatar." + avatarType);
QFile ava(path + "/" + sJid.c_str() + "." + info.type);
if (!ava.exists()) {
bool success = dropAvatar();
bool success = dropAvatar(sJid);
if (!success) {
qDebug() << "error opening archive" << jid << "for account" << account
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
@ -120,6 +100,8 @@ 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);
mdb_dbi_close(environment, main);
@ -159,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);
@ -184,44 +190,146 @@ 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());
}
std::string strKey = id.toStdString();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = strKey.size();
lmdbKey.mv_data = (char*)strKey.c_str();
try {
Shared::Message msg = getMessage(id.toStdString(), txn);
mdb_txn_abort(txn);
return msg;
} catch (...) {
mdb_txn_abort(txn);
throw;
}
}
bool Core::Archive::hasElement(const QString& id) const
{
if (!opened) {
throw Closed("hasElement", jid.toStdString());
}
MDB_txn *txn;
int rc;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
if (rc) {
qDebug() <<"Get error: " << mdb_strerror(rc);
mdb_txn_abort(txn);
throw NotFound(id.toStdString(), jid.toStdString());
} else {
bool has;
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.toStdString().c_str();
int rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
has = rc == 0;
mdb_txn_abort(txn);
return has;
}
Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) const
{
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
int rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
if (rc == 0) {
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
QDataStream ds(&ba, QIODevice::ReadOnly);
Shared::Message msg;
msg.deserialize(ds);
mdb_txn_abort(txn);
return msg;
} else if (rc == MDB_NOTFOUND) {
throw NotFound(id, jid.toStdString());
} else {
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
}
void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVariant>& data)
{
if (!opened) {
throw Closed("setMessageState", jid.toStdString());
}
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
std::string strId(id.toStdString());
try {
Shared::Message msg = getMessage(strId, txn);
bool hadStanzaId = msg.getStanzaId().size() > 0;
QDateTime oTime = msg.getTime();
bool idChange = msg.change(data);
MDB_val lmdbKey, lmdbData;
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
msg.serialize(ds);
lmdbKey.mv_size = strId.size();
lmdbKey.mv_data = (char*)strId.c_str();
int rc;
if (idChange) {
rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
if (rc == 0) {
strId = msg.getId().toStdString();
lmdbKey.mv_size = strId.size();
lmdbKey.mv_data = (char*)strId.c_str();
quint64 stamp = oTime.toMSecsSinceEpoch();
lmdbData.mv_data = (quint8*)&stamp;
lmdbData.mv_size = 8;
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);
if (rc != 0) {
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
} else {
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
}
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);
if (rc == 0) {
rc = mdb_txn_commit(txn);
} else {
throw Unknown(jid.toStdString(), mdb_strerror(rc));
}
} catch (...) {
mdb_txn_abort(txn);
throw;
}
}
Shared::Message Core::Archive::newest()
{
QString id = newestId();
return getElement(id);
return edge(true);
}
QString Core::Archive::newestId()
@ -229,25 +337,8 @@ QString Core::Archive::newestId()
if (!opened) {
throw Closed("newestId", jid.toStdString());
}
MDB_txn *txn;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_cursor* cursor;
rc = mdb_cursor_open(txn, order, &cursor);
MDB_val lmdbKey, lmdbData;
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST);
if (rc) {
qDebug() << "Error geting newestId " << mdb_strerror(rc);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
throw Empty(jid.toStdString());
} else {
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
return sId.c_str();
}
Shared::Message msg = newest();
return msg.getId();
}
QString Core::Archive::oldestId()
@ -255,30 +346,79 @@ QString Core::Archive::oldestId()
if (!opened) {
throw Closed("oldestId", jid.toStdString());
}
Shared::Message msg = oldest();
return msg.getId();
}
Shared::Message Core::Archive::oldest()
{
return edge(false);
}
Shared::Message Core::Archive::edge(bool end)
{
QString name;
MDB_cursor_op begin;
MDB_cursor_op iteration;
if (end) {
name = "newest";
begin = MDB_LAST;
iteration = MDB_PREV;
} else {
name = "oldest";
begin = MDB_FIRST;
iteration = MDB_NEXT;
}
if (!opened) {
throw Closed(name.toStdString(), jid.toStdString());
}
MDB_txn *txn;
MDB_cursor* cursor;
MDB_val lmdbKey, lmdbData;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_cursor* cursor;
rc = mdb_cursor_open(txn, order, &cursor);
MDB_val lmdbKey, lmdbData;
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, begin);
Shared::Message msg = getStoredMessage(txn, cursor, iteration, &lmdbKey, &lmdbData, rc);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
if (rc) {
qDebug() << "Error geting oldestId " << mdb_strerror(rc);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
qDebug() << "Error geting" << name << "message" << mdb_strerror(rc);
throw Empty(jid.toStdString());
} else {
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
return sId.c_str();
return msg;
}
}
Shared::Message Core::Archive::oldest()
Shared::Message Core::Archive::getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc)
{
return getElement(oldestId());
Shared::Message msg;
std::string sId;
while (true) {
if (rc) {
break;
}
sId = std::string((char*)value->mv_data, value->mv_size);
try {
msg = getMessage(sId, txn);
if (msg.serverStored()) {
break;
} else {
rc = mdb_cursor_get(cursor, key, value, op);
}
} catch (...) {
break;
}
}
return msg;
}
unsigned int Core::Archive::addElements(const std::list<Shared::Message>& messages)
@ -317,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 {
@ -346,8 +499,7 @@ long unsigned int Core::Archive::size() const
throw Closed("size", jid.toStdString());
}
MDB_txn *txn;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_stat stat;
mdb_stat(txn, order, &stat);
mdb_txn_abort(txn);
@ -365,8 +517,8 @@ std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id
MDB_val lmdbKey, lmdbData;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
rc = mdb_cursor_open(txn, order, &cursor);
if (id == "") {
rc = mdb_cursor_open(txn, order, &cursor);
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST);
if (rc) {
qDebug() << "Error getting before" << mdb_strerror(rc) << ", id:" << id;
@ -377,40 +529,29 @@ std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id
}
} else {
std::string stdId(id.toStdString());
lmdbKey.mv_size = stdId.size();
lmdbKey.mv_data = (char*)stdId.c_str();
rc = mdb_get(txn, main, &lmdbKey, &lmdbData);
if (rc) {
qDebug() <<"Error getting before: no reference message" << mdb_strerror(rc) << ", id:" << id;
mdb_txn_abort(txn);
throw NotFound(stdId, jid.toStdString());
} else {
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
QDataStream ds(&ba, QIODevice::ReadOnly);
Shared::Message msg;
msg.deserialize(ds);
try {
Shared::Message msg = getMessage(stdId, txn);
quint64 stamp = msg.getTime().toMSecsSinceEpoch();
lmdbKey.mv_data = (quint8*)&stamp;
lmdbKey.mv_size = 8;
rc = mdb_cursor_open(txn, order, &cursor);
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET);
if (rc) {
qDebug() << "Error getting before: couldn't set " << mdb_strerror(rc);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
throw NotFound(stdId, jid.toStdString());
} else {
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV);
if (rc) {
qDebug() << "Error getting before, couldn't prev " << mdb_strerror(rc);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
throw NotFound(stdId, jid.toStdString());
}
}
} catch (...) {
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
throw;
}
}
@ -422,6 +563,7 @@ std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id
if (rc) {
qDebug() <<"Get error: " << mdb_strerror(rc);
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
throw NotFound(sId, jid.toStdString());
} else {
@ -469,17 +611,56 @@ 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";
MDB_txn *txn;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_cursor* cursor;
rc = mdb_cursor_open(txn, order, &cursor);
mdb_cursor_open(txn, order, &cursor);
MDB_val lmdbKey, lmdbData;
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
do {
std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size);
@ -493,13 +674,12 @@ void Core::Archive::printOrder()
void Core::Archive::printKeys()
{
MDB_txn *txn;
int rc;
rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
MDB_cursor* cursor;
rc = mdb_cursor_open(txn, main, &cursor);
mdb_cursor_open(txn, main, &cursor);
MDB_val lmdbKey, lmdbData;
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
do {
std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size);
@ -521,8 +701,9 @@ bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
if (rc == MDB_NOTFOUND) {
throw NotFound(id, jid.toStdString());
} else if (rc) {
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
throw 15; //TODO proper exception
std::string err(mdb_strerror(rc));
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
throw Unknown(jid.toStdString(), err);
} else {
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
bool is;
@ -540,7 +721,6 @@ bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn)
{
MDB_cursor* cursor;
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
@ -550,8 +730,9 @@ std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* tx
if (rc == MDB_NOTFOUND) {
throw NotFound(id, jid.toStdString());
} else if (rc) {
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
throw 15; //TODO proper exception
std::string err(mdb_strerror(rc));
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
throw Unknown(jid.toStdString(), err);
} else {
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
return value;
@ -592,74 +773,38 @@ bool Core::Archive::setStatValue(const std::string& id, const std::string& value
return true;
}
bool Core::Archive::getHasAvatar() const
{
if (!opened) {
throw Closed("getHasAvatar", jid.toStdString());
}
return hasAvatar;
}
bool Core::Archive::getAutoAvatar() const
{
if (!opened) {
throw Closed("getAutoAvatar", jid.toStdString());
}
return avatarAutoGenerated;
}
QString Core::Archive::getAvatarHash() const
{
if (!opened) {
throw Closed("getAvatarHash", jid.toStdString());
}
return avatarHash;
}
QString Core::Archive::getAvatarType() const
{
if (!opened) {
throw Closed("getAvatarType", jid.toStdString());
}
return avatarType;
}
bool Core::Archive::dropAvatar()
bool Core::Archive::dropAvatar(const std::string& resource)
{
MDB_txn *txn;
MDB_val lmdbKey;
mdb_txn_begin(environment, NULL, 0, &txn);
bool success = setStatValue("hasAvatar", false, txn);
success = success && setStatValue("avatarAutoGenerated", false, txn);
success = success && setStatValue("avatarHash", "", txn);
success = success && setStatValue("avatarType", "", txn);
if (!success) {
lmdbKey.mv_size = resource.size();
lmdbKey.mv_data = (char*)resource.c_str();
int rc = mdb_del(txn, avatars, &lmdbKey, NULL);
if (rc != 0) {
mdb_txn_abort(txn);
return false;
} else {
hasAvatar = false;
avatarAutoGenerated = false;
avatarHash = "";
avatarType = "";
mdb_txn_commit(txn);
return true;
}
}
bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource)
{
if (!opened) {
throw Closed("setAvatar", jid.toStdString());
}
AvatarInfo oldInfo;
bool hasAvatar = readAvatarInfo(oldInfo, resource);
std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString();
if (data.size() == 0) {
if (!hasAvatar) {
return false;
} else {
return dropAvatar();
return dropAvatar(res);
}
} else {
const char* cep;
@ -668,14 +813,14 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
bool needToRemoveOld = false;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(data);
QString newHash(hash.result());
QByteArray newHash(hash.result());
if (hasAvatar) {
if (!generated && !avatarAutoGenerated && avatarHash == newHash) {
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) {
return false;
}
QFile oldAvatar(currentPath + "/avatar." + avatarType);
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type);
if (oldAvatar.exists()) {
if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) {
if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) {
needToRemoveOld = true;
} else {
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
@ -686,33 +831,38 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
QMimeDatabase db;
QMimeType type = db.mimeTypeForData(data);
QString ext = type.preferredSuffix();
QFile newAvatar(currentPath + "/avatar." + ext);
QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext);
if (newAvatar.open(QFile::WriteOnly)) {
newAvatar.write(data);
newAvatar.close();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
bool success = setStatValue("hasAvatar", true, txn);
success = success && setStatValue("avatarAutoGenerated", generated, txn);
success = success && setStatValue("avatarHash", newHash.toStdString(), txn);
success = success && setStatValue("avatarType", ext.toStdString(), txn);
if (!success) {
MDB_val lmdbKey, lmdbData;
QByteArray value;
newInfo.type = ext;
newInfo.hash = newHash;
newInfo.autogenerated = generated;
newInfo.serialize(&value);
lmdbKey.mv_size = res.size();
lmdbKey.mv_data = (char*)res.c_str();
lmdbData.mv_size = value.size();
lmdbData.mv_data = value.data();
int rc = mdb_put(txn, avatars, &lmdbKey, &lmdbData, 0);
if (rc != 0) {
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
oldAvatar.rename(currentPath + "/avatar." + avatarType);
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
}
mdb_txn_abort(txn);
return false;
} else {
hasAvatar = true;
avatarAutoGenerated = generated;
avatarHash = newHash;
avatarType = ext;
mdb_txn_commit(txn);
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");