diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90bb71e2d..4208e1479 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES chat/chat-message/chat-message-p.h chat/chat-message/chat-message.h chat/chat-message/imdn-message.h + chat/chat-message/imdn-message-p.h chat/chat-message/is-composing-message.h chat/chat-message/notification-message.h chat/chat-message/notification-message-p.h diff --git a/src/chat/chat-message/chat-message-p.h b/src/chat/chat-message/chat-message-p.h index 464ed8077..b013e9b96 100644 --- a/src/chat/chat-message/chat-message-p.h +++ b/src/chat/chat-message/chat-message-p.h @@ -62,7 +62,7 @@ public: std::list getParticipantsByImdnState (MainDb::ParticipantStateRetrievalFunc func) const; void setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState, time_t stateChangeTime); - void setState (ChatMessage::State newState, bool force = false); + virtual void setState (ChatMessage::State newState, bool force = false); void setTime (time_t time); diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index a8e0855cc..12bae8983 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -179,8 +179,9 @@ void ChatMessagePrivate::setState (ChatMessage::State newState, bool force) { if (state == ChatMessage::State::FileTransferDone && !hasFileTransferContent()) { // We wait until the file has been downloaded to send the displayed IMDN - static_cast(q->getChatRoom()->getPrivate())->sendDisplayNotification(q->getSharedFromThis()); - setState(ChatMessage::State::Displayed); + bool doNotStoreInDb = static_cast(q->getChatRoom()->getPrivate())->sendDisplayNotification(q->getSharedFromThis()); + // Force the state so it is stored directly in DB, but when the IMDN has successfully been delivered + setState(ChatMessage::State::Displayed, doNotStoreInDb); } else { updateInDb(); } diff --git a/src/chat/chat-message/chat-message.h b/src/chat/chat-message/chat-message.h index 2893fd6ed..7b5af855a 100644 --- a/src/chat/chat-message/chat-message.h +++ b/src/chat/chat-message/chat-message.h @@ -50,6 +50,7 @@ class LINPHONE_PUBLIC ChatMessage : public Object, public CoreAccessor { friend class CpimChatMessageModifier; friend class FileTransferChatMessageModifier; friend class Imdn; + friend class ImdnMessagePrivate; friend class MainDb; friend class MainDbPrivate; friend class RealTimeTextChatRoomPrivate; diff --git a/src/chat/chat-message/imdn-message-p.h b/src/chat/chat-message/imdn-message-p.h new file mode 100644 index 000000000..edd994c40 --- /dev/null +++ b/src/chat/chat-message/imdn-message-p.h @@ -0,0 +1,44 @@ +/* + * imdn-message-p.h + * Copyright (C) 2010-2018 Belledonne Communications SARL + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _L_IMDN_MESSAGE_P_H_ +#define _L_IMDN_MESSAGE_P_H_ + +#include "chat/chat-message/imdn-message.h" +#include "chat/chat-message/notification-message-p.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ImdnMessagePrivate : public NotificationMessagePrivate { +private: + ImdnMessagePrivate (const ImdnMessage::Context &context) + : NotificationMessagePrivate(context.chatRoom, ChatMessage::Direction::Outgoing), context(context) {} + + void setState (ChatMessage::State newState, bool force = false) override; + + ImdnMessage::Context context; + + L_DECLARE_PUBLIC(ImdnMessage); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_IMDN_MESSAGE_P_H_ diff --git a/src/chat/chat-message/imdn-message.cpp b/src/chat/chat-message/imdn-message.cpp index eb70de34d..76daa18a2 100644 --- a/src/chat/chat-message/imdn-message.cpp +++ b/src/chat/chat-message/imdn-message.cpp @@ -17,9 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "chat/chat-message/notification-message-p.h" -#include "chat/chat-message/imdn-message.h" +#include "chat/chat-message/imdn-message-p.h" +#include "chat/chat-room/chat-room-p.h" #include "content/content-disposition.h" +#include "logger/logger.h" #include "sip-tools/sip-headers.h" // ============================================================================= @@ -30,38 +31,49 @@ LINPHONE_BEGIN_NAMESPACE // ----------------------------------------------------------------------------- +void ImdnMessagePrivate::setState (ChatMessage::State newState, bool force) { + L_Q(); + + if (newState == ChatMessage::State::Delivered) { + for (const auto &message : context.deliveredMessages) + message->getPrivate()->updateInDb(); + for (const auto &message : context.displayedMessages) + message->getPrivate()->updateInDb(); + static_pointer_cast(context.chatRoom)->getPrivate()->getImdnHandler()->onImdnMessageDelivered(q->getSharedFromThis()); + } +} + +// ----------------------------------------------------------------------------- + ImdnMessage::ImdnMessage ( const shared_ptr &chatRoom, - const list> &deliveredMessages, - const list> &displayedMessages -) : NotificationMessage(*new NotificationMessagePrivate(chatRoom, ChatMessage::Direction::Outgoing)) { + const list> &deliveredMessages, + const list> &displayedMessages +) : ImdnMessage(Context(chatRoom, deliveredMessages, displayedMessages)) {} + +ImdnMessage::ImdnMessage ( + const shared_ptr &chatRoom, + const list &nonDeliveredMessages +) : ImdnMessage(Context(chatRoom, nonDeliveredMessages)) {} + +ImdnMessage::ImdnMessage (const Context &context) : NotificationMessage(*new ImdnMessagePrivate(context)) { L_D(); - for (const auto &message : deliveredMessages) { + for (const auto &message : d->context.deliveredMessages) { Content *content = new Content(); content->setContentDisposition(ContentDisposition::Notification); content->setContentType(ContentType::Imdn); content->setBody(Imdn::createXml(message->getImdnMessageId(), message->getTime(), Imdn::Type::Delivery, LinphoneReasonNone)); addContent(content); } - for (const auto &message : displayedMessages) { + for (const auto &message : d->context.displayedMessages) { Content *content = new Content(); content->setContentDisposition(ContentDisposition::Notification); content->setContentType(ContentType::Imdn); content->setBody(Imdn::createXml(message->getImdnMessageId(), message->getTime(), Imdn::Type::Display, LinphoneReasonNone)); addContent(content); } - - d->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); -} - -ImdnMessage::ImdnMessage ( - const shared_ptr &chatRoom, - const list &nonDeliveredMessages -) : NotificationMessage(*new NotificationMessagePrivate(chatRoom, ChatMessage::Direction::Outgoing)) { - L_D(); - - for (const auto &mr : nonDeliveredMessages) { + for (const auto &mr : d->context.nonDeliveredMessages) { Content *content = new Content(); content->setContentDisposition(ContentDisposition::Notification); content->setContentType(ContentType::Imdn); @@ -70,7 +82,8 @@ ImdnMessage::ImdnMessage ( } d->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); - d->setEncryptionPrevented(true); + if (!d->context.nonDeliveredMessages.empty()) + d->setEncryptionPrevented(true); } LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-message/imdn-message.h b/src/chat/chat-message/imdn-message.h index a7aa026ca..3cc4c0e23 100644 --- a/src/chat/chat-message/imdn-message.h +++ b/src/chat/chat-message/imdn-message.h @@ -21,11 +21,14 @@ #define _L_IMDN_MESSAGE_H_ #include "chat/chat-message/notification-message.h" +#include "chat/notification/imdn.h" // ============================================================================= LINPHONE_BEGIN_NAMESPACE +class ImdnMessagePrivate; + class LINPHONE_PUBLIC ImdnMessage : public NotificationMessage { public: friend class ChatRoomPrivate; @@ -35,17 +38,35 @@ public: virtual ~ImdnMessage () = default; private: + struct Context { + Context ( + const std::shared_ptr &chatRoom, + const std::list> &deliveredMessages, + const std::list> &displayedMessages + ) : chatRoom(chatRoom), deliveredMessages(deliveredMessages), displayedMessages(displayedMessages) {} + Context ( + const std::shared_ptr &chatRoom, + const std::list &nonDeliveredMessages + ) : chatRoom(chatRoom), nonDeliveredMessages(nonDeliveredMessages) {} + + std::shared_ptr chatRoom; + std::list> deliveredMessages; + std::list> displayedMessages; + std::list nonDeliveredMessages; + }; + ImdnMessage ( const std::shared_ptr &chatRoom, - const std::list> &deliveredMessages, - const std::list> &displayedMessages + const std::list> &deliveredMessages, + const std::list> &displayedMessages ); ImdnMessage ( const std::shared_ptr &chatRoom, const std::list &nonDeliveredMessages ); + ImdnMessage (const Context &context); - L_DECLARE_PRIVATE(NotificationMessage); + L_DECLARE_PRIVATE(ImdnMessage); L_DISABLE_COPY(ImdnMessage); }; diff --git a/src/chat/chat-message/notification-message-p.h b/src/chat/chat-message/notification-message-p.h index 259d7da02..28e2bbb52 100644 --- a/src/chat/chat-message/notification-message-p.h +++ b/src/chat/chat-message/notification-message-p.h @@ -31,10 +31,13 @@ class NotificationMessagePrivate : public ChatMessagePrivate { friend class ImdnMessage; friend class IsComposingMessage; -private: +protected: NotificationMessagePrivate(const std::shared_ptr &cr, ChatMessage::Direction dir) : ChatMessagePrivate(cr, dir) {} + void setState (ChatMessage::State newState, bool force = false) override {}; + +private: void setDisplayNotificationRequired (bool value) override {} void setNegativeDeliveryNotificationRequired (bool value) override {} void setPositiveDeliveryNotificationRequired (bool value) override {} diff --git a/src/chat/chat-room/chat-room-p.h b/src/chat/chat-room/chat-room-p.h index 438f08a5e..dd6604b07 100644 --- a/src/chat/chat-room/chat-room-p.h +++ b/src/chat/chat-room/chat-room-p.h @@ -32,6 +32,9 @@ LINPHONE_BEGIN_NAMESPACE +class ImdnMessage; +class IsComposingMessage; + class ChatRoomPrivate : public AbstractChatRoomPrivate, public IsComposingListener { public: inline void setProxyChatRoom (AbstractChatRoom *value) { proxyChatRoom = value; } @@ -55,17 +58,17 @@ public: void removeTransientEvent (const std::shared_ptr &eventLog) override; std::shared_ptr createChatMessage (ChatMessage::Direction direction); - std::shared_ptr createImdnMessage ( - const std::list> &deliveredMessages, - const std::list> &displayedMessages + std::shared_ptr createImdnMessage ( + const std::list> &deliveredMessages, + const std::list> &displayedMessages ); - std::shared_ptr createImdnMessage (const std::list &nonDeliveredMessages); - std::shared_ptr createIsComposingMessage (); + std::shared_ptr createImdnMessage (const std::list &nonDeliveredMessages); + std::shared_ptr createIsComposingMessage (); std::list> findChatMessages (const std::string &messageId) const; void sendDeliveryErrorNotification (const std::shared_ptr &message, LinphoneReason reason); void sendDeliveryNotification (const std::shared_ptr &message); - void sendDisplayNotification (const std::shared_ptr &message); + bool sendDisplayNotification (const std::shared_ptr &message); void notifyChatMessageReceived (const std::shared_ptr &chatMessage) override; void notifyIsComposingReceived (const Address &remoteAddress, bool isComposing); @@ -80,6 +83,8 @@ public: void onIsComposingStateChanged (bool isComposing) override; void onIsRemoteComposingStateChanged (const Address &remoteAddress, bool isComposing) override; + Imdn *getImdnHandler () const { return imdnHandler.get(); } + LinphoneChatRoom *getCChatRoom () const; std::list remoteIsComposing; diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index ed0762922..44af4294e 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -25,6 +25,7 @@ #include "chat/chat-message/chat-message-p.h" #include "chat/chat-message/imdn-message.h" #include "chat/chat-message/is-composing-message.h" +#include "chat/chat-message/notification-message-p.h" #include "chat/chat-room/chat-room-p.h" #include "core/core-p.h" #include "logger/logger.h" @@ -109,22 +110,22 @@ shared_ptr ChatRoomPrivate::createChatMessage (ChatMessage::Directi return shared_ptr(new ChatMessage(q->getSharedFromThis(), direction)); } -shared_ptr ChatRoomPrivate::createImdnMessage ( - const list> &deliveredMessages, - const list> &displayedMessages +shared_ptr ChatRoomPrivate::createImdnMessage ( + const list> &deliveredMessages, + const list> &displayedMessages ) { L_Q(); - return shared_ptr(new ImdnMessage(q->getSharedFromThis(), deliveredMessages, displayedMessages)); + return shared_ptr(new ImdnMessage(q->getSharedFromThis(), deliveredMessages, displayedMessages)); } -shared_ptr ChatRoomPrivate::createImdnMessage (const list &nonDeliveredMessages) { +shared_ptr ChatRoomPrivate::createImdnMessage (const list &nonDeliveredMessages) { L_Q(); - return shared_ptr(new ImdnMessage(q->getSharedFromThis(), nonDeliveredMessages)); + return shared_ptr(new ImdnMessage(q->getSharedFromThis(), nonDeliveredMessages)); } -shared_ptr ChatRoomPrivate::createIsComposingMessage () { +shared_ptr ChatRoomPrivate::createIsComposingMessage () { L_Q(); - return shared_ptr(new IsComposingMessage(q->getSharedFromThis(), *isComposingHandler.get(), isComposing)); + return shared_ptr(new IsComposingMessage(q->getSharedFromThis(), *isComposingHandler.get(), isComposing)); } list> ChatRoomPrivate::findChatMessages (const string &messageId) const { @@ -148,11 +149,14 @@ void ChatRoomPrivate::sendDeliveryNotification (const shared_ptr &m imdnHandler->notifyDelivery(message); } -void ChatRoomPrivate::sendDisplayNotification (const shared_ptr &message) { +bool ChatRoomPrivate::sendDisplayNotification (const shared_ptr &message) { L_Q(); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(q->getCore()->getCCore()); - if (linphone_im_notif_policy_get_send_imdn_displayed(policy)) + if (linphone_im_notif_policy_get_send_imdn_displayed(policy)) { imdnHandler->notifyDisplay(message); + return true; + } + return false; } // ----------------------------------------------------------------------------- @@ -468,16 +472,21 @@ shared_ptr ChatRoom::findChatMessage (const string &messageId, Chat void ChatRoom::markAsRead () { L_D(); + bool globallyMarkAsReadInDb = true; CorePrivate *dCore = getCore()->getPrivate(); for (auto &chatMessage : dCore->mainDb->getUnreadChatMessages(d->chatRoomId)) { // Do not send display notification for file transfer until it has been downloaded (it won't have a file transfer content anymore) if (!chatMessage->getPrivate()->hasFileTransferContent()) { - d->sendDisplayNotification(chatMessage); - chatMessage->getPrivate()->setState(ChatMessage::State::Displayed, true); // True will ensure the setState won't update the database so it will be done below by the markChatMessagesAsRead + bool doNotStoreInDb = d->sendDisplayNotification(chatMessage); + // Force the state so it is stored directly in DB, but when the IMDN has successfully been delivered + chatMessage->getPrivate()->setState(ChatMessage::State::Displayed, doNotStoreInDb); + if (doNotStoreInDb) + globallyMarkAsReadInDb = false; } } - dCore->mainDb->markChatMessagesAsRead(d->chatRoomId); + if (globallyMarkAsReadInDb) + dCore->mainDb->markChatMessagesAsRead(d->chatRoomId); } LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-room/chat-room.h b/src/chat/chat-room/chat-room.h index db261a90f..acde23615 100644 --- a/src/chat/chat-room/chat-room.h +++ b/src/chat/chat-room/chat-room.h @@ -32,6 +32,7 @@ class LINPHONE_PUBLIC ChatRoom : public AbstractChatRoom { public: friend class ChatMessagePrivate; friend class Imdn; + friend class ImdnMessagePrivate; friend class ProxyChatRoomPrivate; L_OVERRIDE_SHARED_FROM_THIS(ChatRoom); diff --git a/src/chat/notification/imdn.cpp b/src/chat/notification/imdn.cpp index 27b4776b0..23644f2b9 100644 --- a/src/chat/notification/imdn.cpp +++ b/src/chat/notification/imdn.cpp @@ -20,6 +20,7 @@ #include #include "chat/chat-message/chat-message-p.h" +#include "chat/chat-message/imdn-message.h" #include "chat/chat-room/chat-room-p.h" #include "core/core.h" #include "logger/logger.h" @@ -74,6 +75,12 @@ void Imdn::notifyDisplay (const shared_ptr &message) { // ----------------------------------------------------------------------------- +void Imdn::onImdnMessageDelivered (const std::shared_ptr &message) { + sentImdnMessages.remove(message); +} + +// ----------------------------------------------------------------------------- + string Imdn::createXml (const string &id, time_t timestamp, Imdn::Type imdnType, LinphoneReason reason) { char *datetime = linphone_timestamp_to_rfc3339_string(timestamp); Xsd::Imdn::Imdn imdn(id, datetime); @@ -153,10 +160,22 @@ int Imdn::timerExpired (void *data, unsigned int revents) { // ----------------------------------------------------------------------------- void Imdn::send () { - if (!deliveredMessages.empty() || !displayedMessages.empty()) - chatRoom->getPrivate()->createImdnMessage(deliveredMessages, displayedMessages)->send(); - if (!nonDeliveredMessages.empty()) - chatRoom->getPrivate()->createImdnMessage(nonDeliveredMessages)->send(); + bool networkReachable = linphone_core_is_network_reachable(chatRoom->getCore()->getCCore()); + if (!deliveredMessages.empty() || !displayedMessages.empty()) { + auto imdnMessage = chatRoom->getPrivate()->createImdnMessage(deliveredMessages, displayedMessages); + sentImdnMessages.push_back(imdnMessage); + if (networkReachable) + imdnMessage->send(); + deliveredMessages.clear(); + displayedMessages.clear(); + } + if (!nonDeliveredMessages.empty()) { + auto imdnMessage = chatRoom->getPrivate()->createImdnMessage(nonDeliveredMessages); + sentImdnMessages.push_back(imdnMessage); + if (networkReachable) + imdnMessage->send(); + nonDeliveredMessages.clear(); + } } void Imdn::startTimer () { diff --git a/src/chat/notification/imdn.h b/src/chat/notification/imdn.h index e9b8c633b..de467f859 100644 --- a/src/chat/notification/imdn.h +++ b/src/chat/notification/imdn.h @@ -32,6 +32,7 @@ LINPHONE_BEGIN_NAMESPACE class ChatMessage; class ChatRoom; +class ImdnMessage; class Imdn { public: @@ -55,6 +56,8 @@ public: void notifyDeliveryError (const std::shared_ptr &message, LinphoneReason reason); void notifyDisplay (const std::shared_ptr &message); + void onImdnMessageDelivered (const std::shared_ptr &message); + static std::string createXml (const std::string &id, time_t time, Imdn::Type imdnType, LinphoneReason reason); static void parse (const std::shared_ptr &chatMessage); @@ -67,9 +70,10 @@ private: private: ChatRoom *chatRoom = nullptr; - std::list> deliveredMessages; - std::list> displayedMessages; + std::list> deliveredMessages; + std::list> displayedMessages; std::list nonDeliveredMessages; + std::list> sentImdnMessages; belle_sip_source_t *timer = nullptr; BackgroundTask bgTask { "IMDN sending" }; };