From 2888e84392afe90dfbb2ed8a85404c9a2df5fde6 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 20 Apr 2018 17:44:39 +0200 Subject: [PATCH] Add ImdnMessage class. --- src/CMakeLists.txt | 2 + src/chat/chat-message/chat-message-p.h | 2 - src/chat/chat-message/chat-message.cpp | 50 +++--------- src/chat/chat-message/chat-message.h | 2 - src/chat/chat-message/imdn-message.cpp | 76 ++++++++++++++++++ src/chat/chat-message/imdn-message.h | 54 +++++++++++++ .../chat-message/notification-message-p.h | 1 + src/chat/chat-room/chat-room-p.h | 12 ++- src/chat/chat-room/chat-room.cpp | 46 +++++++++-- src/chat/chat-room/chat-room.h | 1 + src/chat/notification/imdn.cpp | 80 ++++++++++++++++++- src/chat/notification/imdn.h | 27 +++++++ 12 files changed, 300 insertions(+), 53 deletions(-) create mode 100644 src/chat/chat-message/imdn-message.cpp create mode 100644 src/chat/chat-message/imdn-message.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4141b33f..a6f4568cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES call/remote-conference-call.h chat/chat-message/chat-message-p.h chat/chat-message/chat-message.h + chat/chat-message/imdn-message.h chat/chat-message/is-composing-message.h chat/chat-message/notification-message.h chat/chat-message/notification-message-p.h @@ -193,6 +194,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES call/local-conference-call.cpp call/remote-conference-call.cpp chat/chat-message/chat-message.cpp + chat/chat-message/imdn-message.cpp chat/chat-message/is-composing-message.cpp chat/chat-message/notification-message.cpp chat/chat-room/abstract-chat-room.cpp diff --git a/src/chat/chat-message/chat-message-p.h b/src/chat/chat-message/chat-message-p.h index 2e620bb4a..a8e9ae60f 100644 --- a/src/chat/chat-message/chat-message-p.h +++ b/src/chat/chat-message/chat-message-p.h @@ -154,8 +154,6 @@ public: bool downloadFile (); - void sendImdn (Imdn::Type imdnType, LinphoneReason reason); - void notifyReceiving (); LinphoneReason receive (); void send (); diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 9d6ec11a2..22321eda6 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -179,7 +179,7 @@ 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 - q->sendDisplayNotification(); + static_cast(q->getChatRoom()->getPrivate())->sendDisplayNotification(q->getSharedFromThis()); setState(ChatMessage::State::Displayed); } else { updateInDb(); @@ -454,26 +454,6 @@ void ChatMessagePrivate::setChatRoom (const shared_ptr &cr) { // ----------------------------------------------------------------------------- -void ChatMessagePrivate::sendImdn (Imdn::Type imdnType, LinphoneReason reason) { - L_Q(); - - auto chatRoomPrivate = static_cast(q->getChatRoom()->getPrivate()); - shared_ptr msg = chatRoomPrivate->createNotificationMessage(ChatMessage::Direction::Outgoing); - - Content *content = new Content(); - content->setContentDisposition(ContentDisposition::Notification); - content->setContentType(ContentType::Imdn); - content->setBody(Imdn::createXml(imdnId, time, imdnType, reason)); - msg->addContent(content); - - if (reason != LinphoneReasonNone) - msg->getPrivate()->setEncryptionPrevented(true); - - msg->getPrivate()->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); - - msg->getPrivate()->send(); -} - static void forceUtf8Content (Content &content) { // TODO: Deal with other content type in the future. ContentType contentType = content.getContentType(); @@ -524,7 +504,7 @@ void ChatMessagePrivate::notifyReceiving () { q->getChatRoom()->getPrivate()->notifyChatMessageReceived(q->getSharedFromThis()); if (getPositiveDeliveryNotificationRequired()) - q->sendDeliveryNotification(LinphoneReasonNone); + static_cast(q->getChatRoom()->getPrivate())->sendDeliveryNotification(q->getSharedFromThis()); } LinphoneReason ChatMessagePrivate::receive () { @@ -549,7 +529,10 @@ LinphoneReason ChatMessagePrivate::receive () { chatRoom->getPrivate()->notifyUndecryptableChatMessageReceived(q->getSharedFromThis()); reason = linphone_error_code_to_reason(errorCode); if (getNegativeDeliveryNotificationRequired()) { - q->sendDeliveryNotification(reason); + static_cast(q->getChatRoom()->getPrivate())->sendDeliveryErrorNotification( + q->getSharedFromThis(), + reason + ); } return reason; } else if (result == ChatMessageModifier::Result::Suspended) { @@ -635,7 +618,10 @@ LinphoneReason ChatMessagePrivate::receive () { if (errorCode > 0) { reason = linphone_error_code_to_reason(errorCode); if (getNegativeDeliveryNotificationRequired()) { - q->sendDeliveryNotification(reason); + static_cast(q->getChatRoom()->getPrivate())->sendDeliveryErrorNotification( + q->getSharedFromThis(), + reason + ); } return reason; } @@ -1123,22 +1109,6 @@ void ChatMessage::send () { getChatRoom()->getPrivate()->sendChatMessage(getSharedFromThis()); } -void ChatMessage::sendDeliveryNotification (LinphoneReason reason) { - L_D(); - - LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(getCore()->getCCore()); - if (linphone_im_notif_policy_get_send_imdn_delivered(policy)) - d->sendImdn(Imdn::Type::Delivery, reason); -} - -void ChatMessage::sendDisplayNotification () { - L_D(); - - LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(getCore()->getCCore()); - if (linphone_im_notif_policy_get_send_imdn_displayed(policy)) - d->sendImdn(Imdn::Type::Display, LinphoneReasonNone); -} - bool ChatMessage::downloadFile(FileTransferContent *fileTransferContent) { L_D(); return d->fileTransferChatMessageModifier.downloadFile(getSharedFromThis(), fileTransferContent); diff --git a/src/chat/chat-message/chat-message.h b/src/chat/chat-message/chat-message.h index d6dfc3296..2893fd6ed 100644 --- a/src/chat/chat-message/chat-message.h +++ b/src/chat/chat-message/chat-message.h @@ -66,8 +66,6 @@ public: // ----- TODO: Remove me. void cancelFileTransfer (); int putCharacter (uint32_t character); - void sendDeliveryNotification (LinphoneReason reason); - void sendDisplayNotification (); void setIsSecured (bool isSecured); // ----- TODO: Remove me. diff --git a/src/chat/chat-message/imdn-message.cpp b/src/chat/chat-message/imdn-message.cpp new file mode 100644 index 000000000..eb70de34d --- /dev/null +++ b/src/chat/chat-message/imdn-message.cpp @@ -0,0 +1,76 @@ +/* + * imdn-message.cpp + * 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. + */ + +#include "chat/chat-message/notification-message-p.h" +#include "chat/chat-message/imdn-message.h" +#include "content/content-disposition.h" +#include "sip-tools/sip-headers.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +// ----------------------------------------------------------------------------- + +ImdnMessage::ImdnMessage ( + const shared_ptr &chatRoom, + const list> &deliveredMessages, + const list> &displayedMessages +) : NotificationMessage(*new NotificationMessagePrivate(chatRoom, ChatMessage::Direction::Outgoing)) { + L_D(); + + for (const auto &message : 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) { + 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) { + Content *content = new Content(); + content->setContentDisposition(ContentDisposition::Notification); + content->setContentType(ContentType::Imdn); + content->setBody(Imdn::createXml(mr.message->getImdnMessageId(), mr.message->getTime(), Imdn::Type::Delivery, mr.reason)); + addContent(content); + } + + d->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); + d->setEncryptionPrevented(true); +} + +LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-message/imdn-message.h b/src/chat/chat-message/imdn-message.h new file mode 100644 index 000000000..a7aa026ca --- /dev/null +++ b/src/chat/chat-message/imdn-message.h @@ -0,0 +1,54 @@ +/* + * imdn-message.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_H_ +#define _L_IMDN_MESSAGE_H_ + +#include "chat/chat-message/notification-message.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class LINPHONE_PUBLIC ImdnMessage : public NotificationMessage { +public: + friend class ChatRoomPrivate; + + L_OVERRIDE_SHARED_FROM_THIS(ImdnMessage); + + virtual ~ImdnMessage () = default; + +private: + ImdnMessage ( + const std::shared_ptr &chatRoom, + const std::list> &deliveredMessages, + const std::list> &displayedMessages + ); + ImdnMessage ( + const std::shared_ptr &chatRoom, + const std::list &nonDeliveredMessages + ); + + L_DECLARE_PRIVATE(NotificationMessage); + L_DISABLE_COPY(ImdnMessage); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_IMDN_MESSAGE_H_ diff --git a/src/chat/chat-message/notification-message-p.h b/src/chat/chat-message/notification-message-p.h index 122086529..259d7da02 100644 --- a/src/chat/chat-message/notification-message-p.h +++ b/src/chat/chat-message/notification-message-p.h @@ -28,6 +28,7 @@ LINPHONE_BEGIN_NAMESPACE class NotificationMessagePrivate : public ChatMessagePrivate { + friend class ImdnMessage; friend class IsComposingMessage; private: diff --git a/src/chat/chat-room/chat-room-p.h b/src/chat/chat-room/chat-room-p.h index c884ffda6..438f08a5e 100644 --- a/src/chat/chat-room/chat-room-p.h +++ b/src/chat/chat-room/chat-room-p.h @@ -25,6 +25,7 @@ #include "abstract-chat-room-p.h" #include "chat-room-id.h" #include "chat-room.h" +#include "chat/notification/imdn.h" #include "chat/notification/is-composing.h" // ============================================================================= @@ -54,10 +55,18 @@ 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 &nonDeliveredMessages); std::shared_ptr createIsComposingMessage (); - std::shared_ptr createNotificationMessage (ChatMessage::Direction direction); 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); + void notifyChatMessageReceived (const std::shared_ptr &chatMessage) override; void notifyIsComposingReceived (const Address &remoteAddress, bool isComposing); void notifyStateChanged (); @@ -86,6 +95,7 @@ private: time_t creationTime = std::time(nullptr); time_t lastUpdateTime = std::time(nullptr); + std::unique_ptr imdnHandler; std::unique_ptr isComposingHandler; bool isComposing = false; diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index dcacd666e..ed0762922 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -23,8 +23,8 @@ #include "c-wrapper/c-wrapper.h" #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.h" #include "chat/chat-room/chat-room-p.h" #include "core/core-p.h" #include "logger/logger.h" @@ -109,16 +109,24 @@ shared_ptr ChatRoomPrivate::createChatMessage (ChatMessage::Directi return shared_ptr(new ChatMessage(q->getSharedFromThis(), direction)); } +shared_ptr ChatRoomPrivate::createImdnMessage ( + const list> &deliveredMessages, + const list> &displayedMessages +) { + L_Q(); + return shared_ptr(new ImdnMessage(q->getSharedFromThis(), deliveredMessages, displayedMessages)); +} + +shared_ptr ChatRoomPrivate::createImdnMessage (const list &nonDeliveredMessages) { + L_Q(); + return shared_ptr(new ImdnMessage(q->getSharedFromThis(), nonDeliveredMessages)); +} + shared_ptr ChatRoomPrivate::createIsComposingMessage () { L_Q(); return shared_ptr(new IsComposingMessage(q->getSharedFromThis(), *isComposingHandler.get(), isComposing)); } -shared_ptr ChatRoomPrivate::createNotificationMessage (ChatMessage::Direction direction) { - L_Q(); - return shared_ptr(new NotificationMessage(q->getSharedFromThis(), direction)); -} - list> ChatRoomPrivate::findChatMessages (const string &messageId) const { L_Q(); return q->getCore()->getPrivate()->mainDb->findChatMessages(q->getChatRoomId(), messageId); @@ -126,6 +134,29 @@ list> ChatRoomPrivate::findChatMessages (const string &m // ----------------------------------------------------------------------------- +void ChatRoomPrivate::sendDeliveryErrorNotification (const shared_ptr &message, LinphoneReason reason) { + L_Q(); + LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(q->getCore()->getCCore()); + if (linphone_im_notif_policy_get_send_imdn_delivered(policy)) + imdnHandler->notifyDeliveryError(message, reason); +} + +void ChatRoomPrivate::sendDeliveryNotification (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_delivered(policy)) + imdnHandler->notifyDelivery(message); +} + +void 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)) + imdnHandler->notifyDisplay(message); +} + +// ----------------------------------------------------------------------------- + void ChatRoomPrivate::notifyChatMessageReceived (const shared_ptr &chatMessage) { L_Q(); LinphoneChatRoom *cr = getCChatRoom(); @@ -279,6 +310,7 @@ ChatRoom::ChatRoom (ChatRoomPrivate &p, const shared_ptr &core, const Chat L_D(); d->chatRoomId = chatRoomId; + d->imdnHandler.reset(new Imdn(this)); d->isComposingHandler.reset(new IsComposing(core->getCCore(), d)); } @@ -440,7 +472,7 @@ void ChatRoom::markAsRead () { 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()) { - chatMessage->sendDisplayNotification(); + 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 } } diff --git a/src/chat/chat-room/chat-room.h b/src/chat/chat-room/chat-room.h index bbdb34e2d..db261a90f 100644 --- a/src/chat/chat-room/chat-room.h +++ b/src/chat/chat-room/chat-room.h @@ -31,6 +31,7 @@ class ChatRoomPrivate; class LINPHONE_PUBLIC ChatRoom : public AbstractChatRoom { public: friend class ChatMessagePrivate; + friend class Imdn; friend class ProxyChatRoomPrivate; L_OVERRIDE_SHARED_FROM_THIS(ChatRoom); diff --git a/src/chat/notification/imdn.cpp b/src/chat/notification/imdn.cpp index f74afa848..da89e9810 100644 --- a/src/chat/notification/imdn.cpp +++ b/src/chat/notification/imdn.cpp @@ -17,8 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "chat/chat-message/chat-message-p.h" -#include "chat/chat-room/chat-room.h" +#include "chat/chat-room/chat-room-p.h" #include "core/core.h" #include "logger/logger.h" @@ -32,6 +34,46 @@ LINPHONE_BEGIN_NAMESPACE const string Imdn::imdnPrefix = "/imdn:imdn"; +// ----------------------------------------------------------------------------- + +Imdn::Imdn (ChatRoom *chatRoom) : chatRoom(chatRoom) {} + +Imdn::~Imdn () { + stopTimer(); +} + +// ----------------------------------------------------------------------------- + +void Imdn::notifyDelivery (const shared_ptr &message) { + if (find(deliveredMessages.begin(), deliveredMessages.end(), message) == deliveredMessages.end()) { + deliveredMessages.push_back(message); + startTimer(); + } +} + +void Imdn::notifyDeliveryError (const shared_ptr &message, LinphoneReason reason) { + auto it = find_if(nonDeliveredMessages.begin(), nonDeliveredMessages.end(), [message](const MessageReason mr) { + return message == mr.message; + }); + if (it == nonDeliveredMessages.end()) { + nonDeliveredMessages.emplace_back(message, reason); + startTimer(); + } +} + +void Imdn::notifyDisplay (const shared_ptr &message) { + auto it = find(deliveredMessages.begin(), deliveredMessages.end(), message); + if (it != deliveredMessages.end()) + deliveredMessages.erase(it); + + if (find(displayedMessages.begin(), displayedMessages.end(), message) == displayedMessages.end()) { + displayedMessages.push_back(message); + startTimer(); + } +} + +// ----------------------------------------------------------------------------- + string Imdn::createXml (const string &id, time_t time, Imdn::Type imdnType, LinphoneReason reason) { xmlBufferPtr buf; xmlTextWriterPtr writer; @@ -144,6 +186,8 @@ void Imdn::parse (const shared_ptr &chatMessage) { linphone_xmlparsing_context_destroy(xmlCtx); } +// ----------------------------------------------------------------------------- + void Imdn::parse (const shared_ptr &imdnMessage, xmlparsing_context_t *xmlCtx) { char xpathStr[MAX_XPATH_LENGTH]; char *messageIdStr = nullptr; @@ -208,4 +252,38 @@ void Imdn::parse (const shared_ptr &imdnMessage, xmlparsing_context linphone_free_xml_text_content(datetimeStr); } +int Imdn::timerExpired (void *data, unsigned int revents) { + Imdn *d = reinterpret_cast(data); + d->stopTimer(); + d->send(); + return BELLE_SIP_STOP; +} + +// ----------------------------------------------------------------------------- + +void Imdn::send () { + if (!deliveredMessages.empty() || !displayedMessages.empty()) + chatRoom->getPrivate()->createImdnMessage(deliveredMessages, displayedMessages)->send(); + if (!nonDeliveredMessages.empty()) + chatRoom->getPrivate()->createImdnMessage(nonDeliveredMessages)->send(); +} + +void Imdn::startTimer () { + unsigned int duration = 500; + if (!timer) + timer = chatRoom->getCore()->getCCore()->sal->create_timer(timerExpired, this, duration, "imdn timeout"); + else + belle_sip_source_set_timeout(timer, duration); +} + +void Imdn::stopTimer () { + if (timer) { + auto core = chatRoom->getCore()->getCCore(); + if (core && core->sal) + core->sal->cancel_timer(timer); + belle_sip_object_unref(timer); + timer = nullptr; + } +} + LINPHONE_END_NAMESPACE diff --git a/src/chat/notification/imdn.h b/src/chat/notification/imdn.h index e6eea4766..63927ab52 100644 --- a/src/chat/notification/imdn.h +++ b/src/chat/notification/imdn.h @@ -28,6 +28,7 @@ LINPHONE_BEGIN_NAMESPACE +class ChatMessage; class ChatRoom; class Imdn { @@ -37,14 +38,40 @@ public: Display }; + struct MessageReason { + MessageReason (const std::shared_ptr &message, LinphoneReason reason) + : message(message), reason(reason) {} + + const std::shared_ptr message; + LinphoneReason reason; + }; + + Imdn (ChatRoom *chatRoom); + ~Imdn (); + + void notifyDelivery (const std::shared_ptr &message); + void notifyDeliveryError (const std::shared_ptr &message, LinphoneReason reason); + void notifyDisplay (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); private: static void parse (const std::shared_ptr &chatMessage, xmlparsing_context_t *xmlCtx); + static int timerExpired (void *data, unsigned int revents); + + void send (); + void startTimer (); + void stopTimer (); private: static const std::string imdnPrefix; + + ChatRoom *chatRoom = nullptr; + std::list> deliveredMessages; + std::list> displayedMessages; + std::list nonDeliveredMessages; + belle_sip_source_t *timer = nullptr; }; LINPHONE_END_NAMESPACE