From d1dc2238982ee2c97e882c77090723da4fe5fe3e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 13 Apr 2018 17:11:41 +0200 Subject: [PATCH] Handle imdn namespace and Disposition-Notification header in CPIM. --- src/CMakeLists.txt | 2 + src/chat/chat-message/chat-message-p.h | 26 ++++++-- src/chat/chat-message/chat-message.cpp | 12 ++-- src/chat/chat-message/chat-message.h | 7 +- .../chat-message/notification-message.cpp | 55 ++++++++++++++++ src/chat/chat-message/notification-message.h | 50 ++++++++++++++ src/chat/chat-room/chat-room-p.h | 1 + src/chat/chat-room/chat-room.cpp | 26 ++++---- .../modifier/cpim-chat-message-modifier.cpp | 65 +++++++++++++++++-- 9 files changed, 210 insertions(+), 34 deletions(-) create mode 100644 src/chat/chat-message/notification-message.cpp create mode 100644 src/chat/chat-message/notification-message.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b94bf46a5..adf89688f 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/notification-message.h chat/chat-room/abstract-chat-room-p.h chat/chat-room/abstract-chat-room.h chat/chat-room/basic-chat-room-p.h @@ -190,6 +191,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/notification-message.cpp chat/chat-room/abstract-chat-room.cpp chat/chat-room/basic-chat-room.cpp chat/chat-room/basic-to-client-group-chat-room.cpp diff --git a/src/chat/chat-message/chat-message-p.h b/src/chat/chat-message/chat-message-p.h index 7ffe61832..2e620bb4a 100644 --- a/src/chat/chat-message/chat-message-p.h +++ b/src/chat/chat-message/chat-message-p.h @@ -44,6 +44,7 @@ class ChatMessagePrivate : public ObjectPrivate { friend class CpimChatMessageModifier; friend class EncryptionChatMessageModifier; friend class MultipartChatMessageModifier; + friend class NotificationMessagePrivate; public: enum Step { @@ -99,6 +100,13 @@ public: SalOp *getSalOp () const; void setSalOp (SalOp *op); + bool getDisplayNotificationRequired () const { return displayNotificationRequired; } + bool getNegativeDeliveryNotificationRequired () const { return negativeDeliveryNotificationRequired; } + bool getPositiveDeliveryNotificationRequired () const { return positiveDeliveryNotificationRequired; } + virtual void setDisplayNotificationRequired (bool value) { displayNotificationRequired = value; } + virtual void setNegativeDeliveryNotificationRequired (bool value) { negativeDeliveryNotificationRequired = value; } + virtual void setPositiveDeliveryNotificationRequired (bool value) { positiveDeliveryNotificationRequired = value; } + SalCustomHeader *getSalCustomHeaders () const; void setSalCustomHeaders (SalCustomHeader *headers); @@ -156,11 +164,20 @@ public: void updateInDb (); private: - ChatMessagePrivate(const std::shared_ptr &cr, ChatMessage::Direction dir); - + static bool validStateTransition (ChatMessage::State currentState, ChatMessage::State newState); +public: + mutable MainDbChatMessageKey dbKey; + +protected: + bool displayNotificationRequired = true; + bool negativeDeliveryNotificationRequired = true; + bool positiveDeliveryNotificationRequired = true; + bool toBeStored = true; + +private: // TODO: Clean attributes. time_t time = ::ms_time(0); // TODO: Change me in all files. std::string imdnId; @@ -189,10 +206,6 @@ private: // TODO: Remove my comment. VARIABLES OK. // Do not expose. -public: - mutable MainDbChatMessageKey dbKey; - -private: std::weak_ptr chatRoom; ChatRoomId chatRoomId; IdentityAddress fromAddress; @@ -204,7 +217,6 @@ private: std::list contents; bool encryptionPrevented = false; - bool toBeStored = true; mutable bool contentsNotLoadedFromDatabase = false; L_DECLARE_PUBLIC(ChatMessage); }; diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index 6010dadbb..cb76692e6 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -457,7 +457,8 @@ void ChatMessagePrivate::setChatRoom (const shared_ptr &cr) { void ChatMessagePrivate::sendImdn (Imdn::Type imdnType, LinphoneReason reason) { L_Q(); - shared_ptr msg = q->getChatRoom()->createChatMessage(); + auto chatRoomPrivate = static_cast(q->getChatRoom()->getPrivate()); + shared_ptr msg = chatRoomPrivate->createNotificationMessage(ChatMessage::Direction::Outgoing); Content *content = new Content(); content->setContentDisposition(ContentDisposition::Notification); @@ -468,7 +469,6 @@ void ChatMessagePrivate::sendImdn (Imdn::Type imdnType, LinphoneReason reason) { if (reason != LinphoneReasonNone) msg->getPrivate()->setEncryptionPrevented(true); - msg->setToBeStored(false); msg->getPrivate()->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); msg->getPrivate()->send(); @@ -523,9 +523,8 @@ void ChatMessagePrivate::notifyReceiving () { // Legacy q->getChatRoom()->getPrivate()->notifyChatMessageReceived(q->getSharedFromThis()); - if ((getContentType() != ContentType::Imdn) && (getContentType() != ContentType::ImIsComposing)) { + if (getPositiveDeliveryNotificationRequired()) q->sendDeliveryNotification(LinphoneReasonNone); - } } LinphoneReason ChatMessagePrivate::receive () { @@ -894,7 +893,10 @@ bool ChatMessagePrivate::validStateTransition (ChatMessage::State currentState, // ----------------------------------------------------------------------------- ChatMessage::ChatMessage (const shared_ptr &chatRoom, ChatMessage::Direction direction) : - Object(*new ChatMessagePrivate(chatRoom,direction)), CoreAccessor(chatRoom->getCore()) { + Object(*new ChatMessagePrivate(chatRoom, direction)), CoreAccessor(chatRoom->getCore()) { +} + +ChatMessage::ChatMessage (ChatMessagePrivate &p) : Object(p), CoreAccessor(p.getPublic()->getChatRoom()->getCore()) { } ChatMessage::~ChatMessage () { diff --git a/src/chat/chat-message/chat-message.h b/src/chat/chat-message/chat-message.h index 633b379b3..d6dfc3296 100644 --- a/src/chat/chat-message/chat-message.h +++ b/src/chat/chat-message/chat-message.h @@ -61,7 +61,7 @@ public: L_DECLARE_ENUM(State, L_ENUM_VALUES_CHAT_MESSAGE_STATE); L_DECLARE_ENUM(Direction, L_ENUM_VALUES_CHAT_MESSAGE_DIRECTION); - ~ChatMessage (); + virtual ~ChatMessage (); // ----- TODO: Remove me. void cancelFileTransfer (); @@ -93,7 +93,7 @@ public: bool isReadOnly () const; bool getToBeStored () const; - void setToBeStored (bool value); + virtual void setToBeStored (bool value); std::list getParticipantsThatHaveDisplayed () const; std::list getParticipantsThatHaveReceived () const; @@ -114,6 +114,9 @@ public: bool downloadFile (FileTransferContent *content); bool isFileTransferInProgress(); +protected: + explicit ChatMessage (ChatMessagePrivate &p); + private: ChatMessage (const std::shared_ptr &chatRoom, ChatMessage::Direction direction); diff --git a/src/chat/chat-message/notification-message.cpp b/src/chat/chat-message/notification-message.cpp new file mode 100644 index 000000000..8471c1484 --- /dev/null +++ b/src/chat/chat-message/notification-message.cpp @@ -0,0 +1,55 @@ +/* + * notification-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/chat-message-p.h" +#include "chat/chat-message/notification-message.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +class NotificationMessagePrivate : public ChatMessagePrivate { +private: + NotificationMessagePrivate(const std::shared_ptr &cr, ChatMessage::Direction dir) + : ChatMessagePrivate(cr, dir) {} + + void setDisplayNotificationRequired (bool value) override {} + void setNegativeDeliveryNotificationRequired (bool value) override {} + void setPositiveDeliveryNotificationRequired (bool value) override {} + + L_DECLARE_PUBLIC(NotificationMessage); +}; + +// ----------------------------------------------------------------------------- + +NotificationMessage::NotificationMessage (const shared_ptr &chatRoom, ChatMessage::Direction direction) : + ChatMessage(*new NotificationMessagePrivate(chatRoom, direction)) { + L_D(); + d->displayNotificationRequired = false; + d->negativeDeliveryNotificationRequired = false; + d->positiveDeliveryNotificationRequired = false; + d->toBeStored = false; +} + +void NotificationMessage::setToBeStored (bool value) { +} + +LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-message/notification-message.h b/src/chat/chat-message/notification-message.h new file mode 100644 index 000000000..4870788c1 --- /dev/null +++ b/src/chat/chat-message/notification-message.h @@ -0,0 +1,50 @@ +/* + * notification-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_NOTIFICATION_MESSAGE_H_ +#define _L_NOTIFICATION_MESSAGE_H_ + +#include "chat/chat-message/chat-message.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class NotificationMessagePrivate; + +class LINPHONE_PUBLIC NotificationMessage : public ChatMessage { +public: + friend class ChatRoomPrivate; + + L_OVERRIDE_SHARED_FROM_THIS(NotificationMessage); + + virtual ~NotificationMessage () = default; + + void setToBeStored (bool value) override; + +private: + NotificationMessage (const std::shared_ptr &chatRoom, ChatMessage::Direction direction); + + L_DECLARE_PRIVATE(NotificationMessage); + L_DISABLE_COPY(NotificationMessage); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_NOTIFICATION_MESSAGE_H_ diff --git a/src/chat/chat-room/chat-room-p.h b/src/chat/chat-room/chat-room-p.h index 4d177d845..0da5cc137 100644 --- a/src/chat/chat-room/chat-room-p.h +++ b/src/chat/chat-room/chat-room-p.h @@ -54,6 +54,7 @@ public: void removeTransientEvent (const std::shared_ptr &eventLog) override; std::shared_ptr createChatMessage (ChatMessage::Direction direction); + std::shared_ptr createNotificationMessage (ChatMessage::Direction direction); std::list> findChatMessages (const std::string &messageId) const; void notifyChatMessageReceived (const std::shared_ptr &chatMessage) override; diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index 72705d157..e57bef564 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -23,6 +23,7 @@ #include "c-wrapper/c-wrapper.h" #include "chat/chat-message/chat-message-p.h" +#include "chat/chat-message/notification-message.h" #include "chat/chat-room/chat-room-p.h" #include "core/core-p.h" #include "sip-tools/sip-headers.h" @@ -84,8 +85,7 @@ void ChatRoomPrivate::sendIsComposingNotification () { content->setContentType(ContentType::ImIsComposing); content->setBody(payload); - shared_ptr chatMessage = createChatMessage(ChatMessage::Direction::Outgoing); - chatMessage->setToBeStored(false); + shared_ptr chatMessage = createNotificationMessage(ChatMessage::Direction::Outgoing); chatMessage->addContent(content); chatMessage->getPrivate()->addSalCustomHeader(PriorityHeader::HeaderName, PriorityHeader::NonUrgent); chatMessage->getPrivate()->addSalCustomHeader("Expires", "0"); @@ -121,6 +121,11 @@ shared_ptr ChatRoomPrivate::createChatMessage (ChatMessage::Directi return shared_ptr(new ChatMessage(q->getSharedFromThis(), direction)); } +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); @@ -214,26 +219,21 @@ LinphoneReason ChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessag reason = msg->getPrivate()->receive(); if (reason == LinphoneReasonNotAcceptable || reason == LinphoneReasonUnknown) { - /* Return LinphoneReasonNone to avoid flexisip resending us a message we can't decrypt */ - reason = LinphoneReasonNone; - goto end; + // Return LinphoneReasonNone to avoid flexisip resending us a message we can't decrypt + return LinphoneReasonNone; } if (msg->getPrivate()->getContentType() == ContentType::ImIsComposing) { onIsComposingReceived(msg->getFromAddress(), msg->getPrivate()->getText()); - if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) { - goto end; - } + if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) + return reason; } else if (msg->getPrivate()->getContentType() == ContentType::Imdn) { onImdnReceived(msg); - if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) { - goto end; - } + if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) + return reason; } onChatMessageReceived(msg); - -end: return reason; } diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index 1cc8f8b5b..5f2865ee9 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "linphone/utils/utils.h" + #include "address/address.h" #include "chat/chat-message/chat-message-p.h" #include "chat/cpim/cpim.h" @@ -42,12 +44,33 @@ ChatMessageModifier::Result CpimChatMessageModifier::encode (const shared_ptrgetToAddress())); cpimMessage.addMessageHeader(cpimToHeader); - char token[13]; - belle_sip_random_token(token, sizeof(token)); - Cpim::MessageIdHeader cpimMessageIdHeader; - cpimMessageIdHeader.setValue(token); - cpimMessage.addMessageHeader(cpimMessageIdHeader); - message->getPrivate()->setImdnMessageId(token); + + if (message->getPrivate()->getPositiveDeliveryNotificationRequired() + || message->getPrivate()->getDisplayNotificationRequired() + ) { + const string imdnNamespace = "imdn"; + Cpim::NsHeader cpimNsHeader; + cpimNsHeader.setValue(imdnNamespace + " "); + cpimMessage.addMessageHeader(cpimNsHeader); + + char token[13]; + belle_sip_random_token(token, sizeof(token)); + Cpim::GenericHeader cpimMessageIdHeader; + cpimMessageIdHeader.setName("Message-ID"); // TODO: Replace by imdnNamespace + ".Message-ID"); + cpimMessageIdHeader.setValue(token); + cpimMessage.addMessageHeader(cpimMessageIdHeader); + message->getPrivate()->setImdnMessageId(token); + + Cpim::GenericHeader dispositionNotificationHeader; + dispositionNotificationHeader.setName(imdnNamespace + ".Disposition-Notification"); + vector dispositionNotificationValues; + if (message->getPrivate()->getPositiveDeliveryNotificationRequired()) + dispositionNotificationValues.emplace_back("positive-delivery"); + if (message->getPrivate()->getDisplayNotificationRequired()) + dispositionNotificationValues.emplace_back("display"); + dispositionNotificationHeader.setValue(Utils::join(dispositionNotificationValues, ", ")); + cpimMessage.addMessageHeader(dispositionNotificationHeader); + } const Content *content; if (!message->getInternalContent().isEmpty()) { @@ -125,17 +148,45 @@ ChatMessageModifier::Result CpimChatMessageModifier::decode (const shared_ptrgetContent()); + message->getPrivate()->setPositiveDeliveryNotificationRequired(false); + message->getPrivate()->setNegativeDeliveryNotificationRequired(false); + message->getPrivate()->setDisplayNotificationRequired(false); + Address cpimFromAddress; Address cpimToAddress; l = cpimMessage->getMessageHeaders(); if (l) { + string imdnNamespace = ""; + for (const auto &header : *l.get()) { + if (header->getName() == "NS") { + string val = header->getValue(); + size_t startPos = 0; + startPos = val.find("<", startPos); + if (startPos == string::npos) + break; + size_t endPos = 0; + endPos = val.find(">", startPos); + if (endPos == string::npos) + break; + if (val.substr(startPos, endPos) == "") + imdnNamespace = Utils::trim(val.substr(0, startPos)); + } + } for (const auto &header : *l.get()) { if (header->getName() == "From") cpimFromAddress = Address(header->getValue()); else if (header->getName() == "To") cpimToAddress = Address(header->getValue()); - else if (header->getName() == "Message-ID") + else if ((header->getName() == "Message-ID") || (header->getName() == (imdnNamespace + ".Message-ID"))) message->getPrivate()->setImdnMessageId(header->getValue()); + else if ((header->getName() == (imdnNamespace + ".Disposition-Notification"))) { + vector values = Utils::split(header->getValue(), ", "); + for (const auto &value : values) + if (value == "positive-delivery") + message->getPrivate()->setPositiveDeliveryNotificationRequired(true); + else if (value == "display") + message->getPrivate()->setDisplayNotificationRequired(true); + } } }