diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 555a1aeb4..07de8fa2b 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -80,11 +80,30 @@ static void call_received(SalCallOp *h) { LinphoneAddress *toAddr = linphone_address_new(h->get_to()); if (_linphone_core_is_conference_creation(lc, toAddr)) { - if (sal_address_has_param(h->get_remote_contact_address(), "text")) - _linphone_core_create_server_group_chat_room(lc, h); - // TODO: handle media conference creation if the "text" feature tag is not present linphone_address_unref(toAddr); linphone_address_unref(fromAddr); + if (sal_address_has_param(h->get_remote_contact_address(), "text")) { + bool oneToOneChatRoom = false; + const char *oneToOneChatRoomStr = sal_custom_header_find(h->get_recv_custom_header(), "One-To-One-Chat-Room"); + if (oneToOneChatRoomStr && (strcmp(oneToOneChatRoomStr, "true") == 0)) + oneToOneChatRoom = true; + if (oneToOneChatRoom) { + IdentityAddress from(h->get_from()); + list identAddresses = ServerGroupChatRoom::parseResourceLists(h->get_remote_body()); + if (identAddresses.size() != 1) { + h->decline(SalReasonNotAcceptable, nullptr); + return; + } + IdentityAddress confAddr = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb->findOneToOneConferenceChatRoomAddress(from, identAddresses.front()); + if (confAddr.isValid()) { + shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ChatRoomId(confAddr, confAddr)); + L_GET_PRIVATE(static_pointer_cast(chatRoom))->confirmRecreation(h); + return; + } + } + _linphone_core_create_server_group_chat_room(lc, h); + } + // TODO: handle media conference creation if the "text" feature tag is not present return; } else if (sal_address_has_param(h->get_remote_contact_address(), "text")) { shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom( diff --git a/coreapi/chat.c b/coreapi/chat.c index d35f13a1a..19a3d040a 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -92,7 +92,7 @@ LinphoneChatRoom *_linphone_core_create_server_group_chat_room (LinphoneCore *lc } void linphone_core_delete_chat_room (LinphoneCore *, LinphoneChatRoom *cr) { - LinphonePrivate::Core::deleteChatRoom(L_GET_CPP_PTR_FROM_C_OBJECT(cr)); + L_GET_CPP_PTR_FROM_C_OBJECT(cr)->deleteFromDb(); } LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) { diff --git a/include/linphone/enums/chat-room-enums.h b/include/linphone/enums/chat-room-enums.h index 54c7fd735..0f20820d3 100644 --- a/include/linphone/enums/chat-room-enums.h +++ b/include/linphone/enums/chat-room-enums.h @@ -29,13 +29,15 @@ F(Created) \ F(TerminationPending) \ F(Terminated) \ - F(CreationFailed) + F(CreationFailed) \ + F(Deleted) #define L_ENUM_VALUES_CHAT_ROOM_CAPABILITIES(F) \ F(Basic, 1 << 0) \ F(RealTimeText, 1 << 1) \ F(Conference, 1 << 2) \ F(Proxy, 1 << 3) \ - F(Migratable, 1 << 4) + F(Migratable, 1 << 4) \ + F(OneToOne, 1 << 5) #endif // ifndef _L_CHAT_ROOM_ENUMS_H_ diff --git a/src/chat/chat-room/abstract-chat-room.h b/src/chat/chat-room/abstract-chat-room.h index 1213badb9..69fe7f0f5 100644 --- a/src/chat/chat-room/abstract-chat-room.h +++ b/src/chat/chat-room/abstract-chat-room.h @@ -38,6 +38,7 @@ class LINPHONE_PUBLIC AbstractChatRoom : public Object, public CoreAccessor, pub friend class ChatMessage; friend class ChatMessagePrivate; friend class ClientGroupToBasicChatRoomPrivate; + friend class Core; friend class CorePrivate; friend class MainDb; friend class ProxyChatRoomPrivate; @@ -66,6 +67,7 @@ public: virtual std::list> getHistoryRange (int begin, int end) const = 0; virtual int getHistorySize () const = 0; + virtual void deleteFromDb () = 0; virtual void deleteHistory () = 0; virtual std::shared_ptr getLastChatMessageInHistory () const = 0; diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index 8b835b4be..346e370dd 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -346,6 +346,12 @@ int ChatRoom::getHistorySize () const { return getCore()->getPrivate()->mainDb->getHistorySize(getChatRoomId()); } +void ChatRoom::deleteFromDb () { + L_D(); + Core::deleteChatRoom(this->getSharedFromThis()); + d->setState(ChatRoom::State::Deleted); +} + void ChatRoom::deleteHistory () { getCore()->getPrivate()->mainDb->cleanHistory(getChatRoomId()); } diff --git a/src/chat/chat-room/chat-room.h b/src/chat/chat-room/chat-room.h index e1dae2941..35d887e59 100644 --- a/src/chat/chat-room/chat-room.h +++ b/src/chat/chat-room/chat-room.h @@ -46,6 +46,7 @@ public: std::list> getHistoryRange (int begin, int end) const override; int getHistorySize () const override; + void deleteFromDb () override; void deleteHistory () override; std::shared_ptr getLastChatMessageInHistory () const override; diff --git a/src/chat/chat-room/client-group-chat-room-p.h b/src/chat/chat-room/client-group-chat-room-p.h index 34e16372a..7842d2071 100644 --- a/src/chat/chat-room/client-group-chat-room-p.h +++ b/src/chat/chat-room/client-group-chat-room-p.h @@ -42,6 +42,7 @@ public: // ChatRoomListener void onChatRoomInsertRequested (const std::shared_ptr &chatRoom) override; void onChatRoomInsertInDatabaseRequested (const std::shared_ptr &chatRoom) override; + void onChatRoomDeleteRequested (const std::shared_ptr &chatRoom) override; // CallSessionListener void onCallSessionSetReleased (const std::shared_ptr &session) override; @@ -50,6 +51,8 @@ public: private: CallSessionListener *callSessionListener = this; ChatRoomListener *chatRoomListener = this; + ClientGroupChatRoom::CapabilitiesMask capabilities = ClientGroupChatRoom::Capabilities::Conference; + bool deletionOnTerminationEnabled = false; L_DECLARE_PUBLIC(ClientGroupChatRoom); }; diff --git a/src/chat/chat-room/client-group-chat-room.cpp b/src/chat/chat-room/client-group-chat-room.cpp index 2704175d8..7bbcbf1b7 100644 --- a/src/chat/chat-room/client-group-chat-room.cpp +++ b/src/chat/chat-room/client-group-chat-room.cpp @@ -62,6 +62,8 @@ shared_ptr ClientGroupChatRoomPrivate::createSession () { CallSessionParams csp; csp.addCustomHeader("Require", "recipient-list-invite"); csp.addCustomContactParameter("text"); + if (capabilities & ClientGroupChatRoom::Capabilities::OneToOne) + csp.addCustomHeader("One-To-One-Chat-Room", "true"); shared_ptr focus = qConference->getPrivate()->focus; shared_ptr session = focus->getPrivate()->createSession(*q, &csp, false, callSessionListener); @@ -111,6 +113,12 @@ void ClientGroupChatRoomPrivate::onChatRoomInsertInDatabaseRequested (const shar q->getCore()->getPrivate()->insertChatRoomWithDb(chatRoom); } +void ClientGroupChatRoomPrivate::onChatRoomDeleteRequested (const shared_ptr &chatRoom) { + L_Q(); + q->getCore()->deleteChatRoom(q->getSharedFromThis()); + setState(ClientGroupChatRoom::State::Deleted); +} + // ----------------------------------------------------------------------------- void ClientGroupChatRoomPrivate::onCallSessionSetReleased (const shared_ptr &session) { @@ -164,6 +172,7 @@ ClientGroupChatRoom::ClientGroupChatRoom ( const shared_ptr &core, const ChatRoomId &chatRoomId, shared_ptr &me, + AbstractChatRoom::CapabilitiesMask capabilities, const string &subject, list> &&participants, unsigned int lastNotifyId @@ -189,7 +198,8 @@ shared_ptr ClientGroupChatRoom::getCore () const { } ClientGroupChatRoom::CapabilitiesMask ClientGroupChatRoom::getCapabilities () const { - return Capabilities::Conference; + L_D(); + return d->capabilities; } bool ClientGroupChatRoom::hasBeenLeft () const { @@ -208,6 +218,16 @@ const IdentityAddress &ClientGroupChatRoom::getConferenceAddress () const { return RemoteConference::getConferenceAddress(); } +void ClientGroupChatRoom::deleteFromDb () { + L_D(); + if (!hasBeenLeft()) { + d->deletionOnTerminationEnabled = true; + leave(); + return; + } + d->chatRoomListener->onChatRoomDeleteRequested(getSharedFromThis()); +} + void ClientGroupChatRoom::addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) { list addresses; addresses.push_back(addr); @@ -231,6 +251,19 @@ void ClientGroupChatRoom::addParticipants ( return; } + if ((getState() == ChatRoom::State::Created) && (d->capabilities & ClientGroupChatRoom::Capabilities::OneToOne)) { + lError() << "Cannot add more participants to a OneToOne ClientGroupChatRoom"; + return; + } + + if ((getState() == ChatRoom::State::Instantiated) + && (addressesList.size() == 1) + && (linphone_config_get_bool(linphone_core_get_config(L_GET_C_BACK_PTR(getCore())), + "misc", "one_to_one_chat_room_enabled", TRUE)) + ) { + d->capabilities |= ClientGroupChatRoom::Capabilities::OneToOne; + } + Content content; content.setBody(getResourceLists(addressesList)); content.setContentType("application/resource-lists+xml"); @@ -372,6 +405,11 @@ void ClientGroupChatRoom::onConferenceCreated (const IdentityAddress &addr) { d->chatRoomListener->onChatRoomInsertRequested(getSharedFromThis()); } +void ClientGroupChatRoom::onConferenceKeywordsChanged (const vector &keywords) { + L_D(); + d->capabilities |= ClientGroupChatRoom::Capabilities::OneToOne; +} + void ClientGroupChatRoom::onConferenceTerminated (const IdentityAddress &addr) { L_D(); L_D_T(RemoteConference, dConference); @@ -382,6 +420,10 @@ void ClientGroupChatRoom::onConferenceTerminated (const IdentityAddress &addr) { time(nullptr), d->chatRoomId )); + if (d->deletionOnTerminationEnabled) { + d->deletionOnTerminationEnabled = false; + d->chatRoomListener->onChatRoomDeleteRequested(getSharedFromThis()); + } } void ClientGroupChatRoom::onFirstNotifyReceived (const IdentityAddress &addr) { diff --git a/src/chat/chat-room/client-group-chat-room.h b/src/chat/chat-room/client-group-chat-room.h index 4579f6e0d..0bc562504 100644 --- a/src/chat/chat-room/client-group-chat-room.h +++ b/src/chat/chat-room/client-group-chat-room.h @@ -32,6 +32,7 @@ class ClientGroupChatRoomPrivate; class LINPHONE_PUBLIC ClientGroupChatRoom : public ChatRoom, public RemoteConference { friend class BasicToClientGroupChatRoomPrivate; friend class ClientGroupToBasicChatRoomPrivate; + friend class Core; public: L_OVERRIDE_SHARED_FROM_THIS(ClientGroupChatRoom); @@ -48,6 +49,7 @@ public: const std::shared_ptr &core, const ChatRoomId &chatRoomId, std::shared_ptr &me, + AbstractChatRoom::CapabilitiesMask capabilities, const std::string &subject, std::list> &&participants, unsigned int lastNotifyId @@ -63,6 +65,8 @@ public: bool canHandleParticipants () const override; bool canHandleCpim () const override; + void deleteFromDb () override; + void addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) override; void addParticipants (const std::list &addresses, const CallSessionParams *params, bool hasMedia) override; @@ -88,6 +92,7 @@ private: // ALL METHODS AFTER THIS POINT. void onConferenceCreated (const IdentityAddress &addr) override; + void onConferenceKeywordsChanged (const std::vector &keywords) override; void onConferenceTerminated (const IdentityAddress &addr) override; void onFirstNotifyReceived (const IdentityAddress &addr) override; void onParticipantAdded (const std::shared_ptr &event, bool isFullState) override; diff --git a/src/chat/chat-room/client-group-to-basic-chat-room.cpp b/src/chat/chat-room/client-group-to-basic-chat-room.cpp index b1b05ff07..85557f206 100644 --- a/src/chat/chat-room/client-group-to-basic-chat-room.cpp +++ b/src/chat/chat-room/client-group-to-basic-chat-room.cpp @@ -46,6 +46,12 @@ public: q->getCore()->getPrivate()->insertChatRoomWithDb(q->getSharedFromThis()); } + void onChatRoomDeleteRequested (const shared_ptr &chatRoom) override { + L_Q(); + q->getCore()->deleteChatRoom(q->getSharedFromThis()); + setState(AbstractChatRoom::State::Deleted); + } + void onCallSessionSetReleased (const std::shared_ptr &session) override { if (!(chatRoom->getCapabilities() & ChatRoom::Capabilities::Conference)) return; diff --git a/src/chat/chat-room/proxy-chat-room.cpp b/src/chat/chat-room/proxy-chat-room.cpp index 7b41f0dbe..dcec5d7cb 100644 --- a/src/chat/chat-room/proxy-chat-room.cpp +++ b/src/chat/chat-room/proxy-chat-room.cpp @@ -190,6 +190,11 @@ int ProxyChatRoom::getHistorySize () const { return d->chatRoom->getHistorySize(); } +void ProxyChatRoom::deleteFromDb () { + L_D(); + d->chatRoom->deleteFromDb(); +} + void ProxyChatRoom::deleteHistory () { L_D(); d->chatRoom->deleteHistory(); diff --git a/src/chat/chat-room/proxy-chat-room.h b/src/chat/chat-room/proxy-chat-room.h index 05df81db9..614da87ff 100644 --- a/src/chat/chat-room/proxy-chat-room.h +++ b/src/chat/chat-room/proxy-chat-room.h @@ -47,6 +47,7 @@ public: std::list> getHistoryRange (int begin, int end) const override; int getHistorySize () const override; + void deleteFromDb () override; void deleteHistory () override; std::shared_ptr getLastChatMessageInHistory () const override; diff --git a/src/chat/chat-room/server-group-chat-room-p.h b/src/chat/chat-room/server-group-chat-room-p.h index 6fda11f4f..23f9bdd9a 100644 --- a/src/chat/chat-room/server-group-chat-room-p.h +++ b/src/chat/chat-room/server-group-chat-room-p.h @@ -35,6 +35,7 @@ public: void confirmCreation (); void confirmJoining (SalCallOp *op); + void confirmRecreation (SalCallOp *op); IdentityAddress generateConferenceAddress (const std::shared_ptr &me) const; @@ -43,10 +44,9 @@ public: void update (SalCallOp *op); void dispatchMessage (const IdentityAddress &fromAddress, const Content &content); - void setConferenceAddress (const IdentityAddress &conferenceAddress); void setParticipantDevices (const IdentityAddress &addr, const std::list &devices); - void addCompatibleParticipants (const IdentityAddress &deviceAddr, const std::list &participantCompatible); + void addCompatibleParticipants (const IdentityAddress &deviceAddr, const std::list &compatibleParticipants); LinphoneReason onSipMessageReceived (SalOp *op, const SalMessage *message) override; @@ -63,12 +63,14 @@ private: // CallSessionListener void onCallSessionStateChanged ( const std::shared_ptr &session, - CallSession::State state, + CallSession::State newState, const std::string &message ) override; std::list> removedParticipants; ChatRoomListener *chatRoomListener = this; + ServerGroupChatRoom::CapabilitiesMask capabilities = ServerGroupChatRoom::Capabilities::Conference; + bool joiningPendingAfterCreation = false; L_DECLARE_PUBLIC(ServerGroupChatRoom); }; diff --git a/src/chat/chat-room/server-group-chat-room-stub.cpp b/src/chat/chat-room/server-group-chat-room-stub.cpp index ceacaabea..94fdd2429 100644 --- a/src/chat/chat-room/server-group-chat-room-stub.cpp +++ b/src/chat/chat-room/server-group-chat-room-stub.cpp @@ -46,6 +46,8 @@ void ServerGroupChatRoomPrivate::confirmCreation () {} void ServerGroupChatRoomPrivate::confirmJoining (SalCallOp *) {} +void ServerGroupChatRoomPrivate::confirmRecreation (SalCallOp *) {} + // ----------------------------------------------------------------------------- IdentityAddress ServerGroupChatRoomPrivate::generateConferenceAddress (const shared_ptr &me) const { @@ -110,6 +112,7 @@ LocalConference(core, IdentityAddress(op->get_to()), nullptr) { ServerGroupChatRoom::ServerGroupChatRoom ( const shared_ptr &core, const IdentityAddress &peerAddress, + AbstractChatRoom::CapabilitiesMask capabilities, const string &subject, list> &&participants, unsigned int lastNotifyId diff --git a/src/chat/chat-room/server-group-chat-room.h b/src/chat/chat-room/server-group-chat-room.h index c0caec3bb..8dac199a4 100644 --- a/src/chat/chat-room/server-group-chat-room.h +++ b/src/chat/chat-room/server-group-chat-room.h @@ -39,11 +39,16 @@ public: ServerGroupChatRoom ( const std::shared_ptr &core, const IdentityAddress &peerAddress, + AbstractChatRoom::CapabilitiesMask capabilities, const std::string &subject, std::list> &&participants, unsigned int lastNotifyId ); + std::shared_ptr getCore () const; + + std::shared_ptr findParticipant (const std::shared_ptr &session) const; + CapabilitiesMask getCapabilities () const override; bool hasBeenLeft () const override; diff --git a/src/conference/conference-listener.h b/src/conference/conference-listener.h index f782092b3..e90c00760 100644 --- a/src/conference/conference-listener.h +++ b/src/conference/conference-listener.h @@ -22,6 +22,7 @@ #include #include +#include #include "linphone/utils/general.h" @@ -36,6 +37,7 @@ class IdentityAddress; class ConferenceListener { public: virtual void onConferenceCreated (const IdentityAddress &addr) = 0; + virtual void onConferenceKeywordsChanged (const std::vector &keywords) {} virtual void onConferenceTerminated (const IdentityAddress &addr) = 0; virtual void onFirstNotifyReceived (const IdentityAddress &addr) = 0; virtual void onParticipantAdded (const std::shared_ptr &event, bool isFullState) = 0; diff --git a/src/conference/handlers/local-conference-event-handler-p.h b/src/conference/handlers/local-conference-event-handler-p.h index e87d97343..6fce0ddb3 100644 --- a/src/conference/handlers/local-conference-event-handler-p.h +++ b/src/conference/handlers/local-conference-event-handler-p.h @@ -37,7 +37,7 @@ public: void notifyFullState (const std::string ¬ify, const std::shared_ptr &device); void notifyAllExcept (const std::string ¬ify, const std::shared_ptr &exceptParticipant); void notifyAll (const std::string ¬ify); - std::string createNotifyFullState (int notifyId = -1); + std::string createNotifyFullState (int notifyId = -1, bool oneToOne = false); std::string createNotifyMultipart (int notifyId); std::string createNotifyParticipantAdded (const Address &addr, int notifyId = -1); std::string createNotifyParticipantRemoved (const Address &addr, int notifyId = -1); diff --git a/src/conference/handlers/local-conference-event-handler.cpp b/src/conference/handlers/local-conference-event-handler.cpp index c0669eb68..6d0bf73b2 100644 --- a/src/conference/handlers/local-conference-event-handler.cpp +++ b/src/conference/handlers/local-conference-event-handler.cpp @@ -107,13 +107,17 @@ string LocalConferenceEventHandlerPrivate::createNotify (ConferenceType confInfo return notify.str(); } -string LocalConferenceEventHandlerPrivate::createNotifyFullState (int notifyId) { +string LocalConferenceEventHandlerPrivate::createNotifyFullState (int notifyId, bool oneToOne) { string entity = conf->getConferenceAddress().asString(); string subject = conf->getSubject(); ConferenceType confInfo = ConferenceType(entity); UsersType users; ConferenceDescriptionType confDescr = ConferenceDescriptionType(); confDescr.setSubject(subject); + if (oneToOne) { + KeywordsType keywords(sizeof(char), "one-to-one"); + confDescr.setKeywords(keywords); + } confInfo.setUsers(users); confInfo.setConferenceDescription((const ConferenceDescriptionType) confDescr); @@ -357,18 +361,13 @@ string LocalConferenceEventHandlerPrivate::createNotifyParticipantDeviceRemoved LocalConferenceEventHandler::LocalConferenceEventHandler (LocalConference *localConference, unsigned int notify) : Object(*new LocalConferenceEventHandlerPrivate) { L_D(); - xercesc::XMLPlatformUtils::Initialize(); d->conf = localConference; d->lastNotify = notify; } -LocalConferenceEventHandler::~LocalConferenceEventHandler () { - xercesc::XMLPlatformUtils::Terminate(); -} - // ----------------------------------------------------------------------------- -void LocalConferenceEventHandler::subscribeReceived (LinphoneEvent *lev) { +void LocalConferenceEventHandler::subscribeReceived (LinphoneEvent *lev, bool oneToOne) { L_D(); const LinphoneAddress *lAddr = linphone_event_get_from(lev); char *addrStr = linphone_address_as_string(lAddr); @@ -396,7 +395,7 @@ void LocalConferenceEventHandler::subscribeReceived (LinphoneEvent *lev) { device->setConferenceSubscribeEvent(lev); if (lastNotify == 0) { lInfo() << "Sending initial notify of conference:" << d->conf->getConferenceAddress().asString() << " to: " << device->getAddress().asString(); - d->notifyFullState(d->createNotifyFullState(static_cast(d->lastNotify)), device); + d->notifyFullState(d->createNotifyFullState(static_cast(d->lastNotify), oneToOne), device); } else if (lastNotify < d->lastNotify) { lInfo() << "Sending all missed notify [" << lastNotify << "-" << d->lastNotify << "] for conference:" << d->conf->getConferenceAddress().asString() << diff --git a/src/conference/handlers/local-conference-event-handler.h b/src/conference/handlers/local-conference-event-handler.h index 06abcb4ff..60794767c 100644 --- a/src/conference/handlers/local-conference-event-handler.h +++ b/src/conference/handlers/local-conference-event-handler.h @@ -42,9 +42,8 @@ class LocalConferenceEventHandlerPrivate; class LocalConferenceEventHandler : public Object { public: LocalConferenceEventHandler (LocalConference *localConference, unsigned int notify = 0); - ~LocalConferenceEventHandler (); - void subscribeReceived (LinphoneEvent *lev); + void subscribeReceived (LinphoneEvent *lev, bool oneToOne = false); std::shared_ptr notifyParticipantAdded (const Address &addr); std::shared_ptr notifyParticipantRemoved (const Address &addr); std::shared_ptr notifyParticipantSetAdmin (const Address &addr, bool isAdmin); diff --git a/src/conference/handlers/remote-conference-event-handler.cpp b/src/conference/handlers/remote-conference-event-handler.cpp index 2daebb426..2d7219531 100644 --- a/src/conference/handlers/remote-conference-event-handler.cpp +++ b/src/conference/handlers/remote-conference-event-handler.cpp @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "linphone/utils/utils.h" #include "conference/remote-conference.h" @@ -53,20 +55,30 @@ void RemoteConferenceEventHandlerPrivate::simpleNotifyReceived (const string &xm IdentityAddress entityAddress(confInfo->getEntity().c_str()); if (entityAddress == chatRoomId.getPeerAddress()) { - if ( - confInfo->getConferenceDescription().present() && - confInfo->getConferenceDescription().get().getSubject().present() && - !confInfo->getConferenceDescription().get().getSubject().get().empty() - ) - confListener->onSubjectChanged( - make_shared( - tm, - chatRoomId, - lastNotify, - confInfo->getConferenceDescription().get().getSubject().get() - ), - isFullState - ); + if (confInfo->getConferenceDescription().present()) { + if (confInfo->getConferenceDescription().get().getSubject().present() && + !confInfo->getConferenceDescription().get().getSubject().get().empty() + ) { + confListener->onSubjectChanged( + make_shared( + tm, + chatRoomId, + lastNotify, + confInfo->getConferenceDescription().get().getSubject().get() + ), + isFullState + ); + } + if (confInfo->getConferenceDescription().get().getKeywords().present() + && !confInfo->getConferenceDescription().get().getKeywords().get().empty() + ) { + KeywordsType xmlKeywords = confInfo->getConferenceDescription().get().getKeywords().get(); + vector keywords; + for (const auto &k : xmlKeywords) + keywords.push_back(k); + confListener->onConferenceKeywordsChanged(keywords); + } + } if (confInfo->getVersion().present()) lastNotify = confInfo->getVersion().get(); @@ -215,7 +227,6 @@ void RemoteConferenceEventHandlerPrivate::onRegistrationStateChanged (LinphonePr RemoteConferenceEventHandler::RemoteConferenceEventHandler (RemoteConference *remoteConference) : Object(*new RemoteConferenceEventHandlerPrivate) { L_D(); - xercesc::XMLPlatformUtils::Initialize(); d->conf = remoteConference; d->conf->getCore()->getPrivate()->registerListener(d); } @@ -223,7 +234,6 @@ Object(*new RemoteConferenceEventHandlerPrivate) { RemoteConferenceEventHandler::~RemoteConferenceEventHandler () { L_D(); d->conf->getCore()->getPrivate()->unregisterListener(d); - xercesc::XMLPlatformUtils::Terminate(); } // ----------------------------------------------------------------------------- diff --git a/src/conference/local-conference.cpp b/src/conference/local-conference.cpp index 9aa13a0fa..b07f17287 100644 --- a/src/conference/local-conference.cpp +++ b/src/conference/local-conference.cpp @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "content/content.h" +#include "content/content-type.h" #include "handlers/local-conference-event-handler.h" #include "local-conference-p.h" #include "participant-p.h" @@ -63,20 +65,25 @@ void LocalConference::removeParticipant (const shared_ptr &pa } } -list LocalConference::parseResourceLists (const string &xmlBody) { - istringstream data(xmlBody); - unique_ptr rl = LinphonePrivate::Xsd::ResourceLists::parseResourceLists( - data, - Xsd::XmlSchema::Flags::dont_validate - ); - list addresses = list(); - for (const auto &l : rl->getList()) { - for (const auto &entry : l.getEntry()) { - IdentityAddress addr(entry.getUri()); - addresses.push_back(addr); +list LocalConference::parseResourceLists (const Content &content) { + if ((content.getContentType() == ContentType::ResourceLists) + && (content.getContentDisposition() == "recipient-list") + ) { + istringstream data(content.getBodyAsString()); + unique_ptr rl(Xsd::ResourceLists::parseResourceLists( + data, + Xsd::XmlSchema::Flags::dont_validate + )); + list addresses; + for (const auto &l : rl->getList()) { + for (const auto &entry : l.getEntry()) { + IdentityAddress addr(entry.getUri()); + addresses.push_back(move(addr)); + } } + return addresses; } - return addresses; + return list(); } LINPHONE_END_NAMESPACE diff --git a/src/conference/local-conference.h b/src/conference/local-conference.h index 5ffa65761..7dee773e4 100644 --- a/src/conference/local-conference.h +++ b/src/conference/local-conference.h @@ -26,6 +26,7 @@ LINPHONE_BEGIN_NAMESPACE +class Content; class LocalConferencePrivate; class LocalConference : public Conference { @@ -39,7 +40,7 @@ public: void addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) override; void removeParticipant (const std::shared_ptr &participant) override; - std::list parseResourceLists (const std::string &xmlBody); + static std::list parseResourceLists (const Content &content); private: L_DECLARE_PRIVATE(LocalConference); diff --git a/src/core/core-chat-room.cpp b/src/core/core-chat-room.cpp index 9c9eb07f3..511963e3b 100644 --- a/src/core/core-chat-room.cpp +++ b/src/core/core-chat-room.cpp @@ -252,21 +252,12 @@ void Core::deleteChatRoom (const shared_ptr &chatRoom) { CorePrivate *d = chatRoom->getCore()->getPrivate(); const ChatRoomId &chatRoomId = chatRoom->getChatRoomId(); - auto it = d->chatRoomsById.find(chatRoomId); - if (it != d->chatRoomsById.end()) { - - // TODO: Remove me later. - auto it = find_if( - d->chatRooms.begin(), d->chatRooms.end(), - [&chatRoomId](const shared_ptr &chatRoom) { - return chatRoom->getChatRoomId() == chatRoomId; - } - ); - - // FIXME: Use this code in the future. (Wait for signals.) - // auto it = find(d->chatRooms.begin(), d->chatRooms.end(), chatRoom); - // L_ASSERT(it != d->chatRooms.end()); - d->chatRooms.erase(it); + auto chatRoomsByIdIt = d->chatRoomsById.find(chatRoomId); + if (chatRoomsByIdIt != d->chatRoomsById.end()) { + auto chatRoomsIt = find(d->chatRooms.begin(), d->chatRooms.end(), chatRoom); + L_ASSERT(chatRoomsIt != d->chatRooms.end()); + d->chatRooms.erase(chatRoomsIt); + d->chatRoomsById.erase(chatRoomsByIdIt); d->mainDb->deleteChatRoom(chatRoomId); } } diff --git a/src/core/core.cpp b/src/core/core.cpp index a7f83274e..c09784f5c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "call/call.h" #include "core/core-listener.h" @@ -52,7 +53,7 @@ void CorePrivate::init () { uri = q->getDataPath() + LINPHONE_DB; } - lInfo() << "Opening " LINPHONE_DB "..."; + lInfo() << "Opening linphone database: " << uri; if (!mainDb->connect(backend, uri)) lFatal() << "Unable to open linphone database."; @@ -91,10 +92,13 @@ void CorePrivate::notifyRegistrationStateChanged (LinphoneProxyConfig *cfg, Linp // ============================================================================= -Core::Core () : Object(*new CorePrivate) {} +Core::Core () : Object(*new CorePrivate) { + xercesc::XMLPlatformUtils::Initialize(); +} Core::~Core () { lInfo() << "Destroying core: " << this; + xercesc::XMLPlatformUtils::Terminate(); } shared_ptr Core::create (LinphoneCore *cCore) { diff --git a/src/db/main-db-p.h b/src/db/main-db-p.h index e0bf1f4f0..c1a4cff1e 100644 --- a/src/db/main-db-p.h +++ b/src/db/main-db-p.h @@ -67,6 +67,7 @@ private: long long selectChatRoomId (long long peerSipAddressId, long long localSipAddressId) const; long long selectChatRoomId (const ChatRoomId &chatRoomId) const; long long selectChatRoomParticipantId (long long chatRoomId, long long participantSipAddressId) const; + long long selectOneToOneChatRoomId (long long sipAddressIdA, long long sipAddressIdB) const; void deleteContents (long long messageEventId); void deleteChatRoomParticipant (long long chatRoomId, long long participantSipAddressId); diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index 0e6c65d4f..ba132c59b 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -266,10 +266,7 @@ long long MainDbPrivate::insertChatRoom (const shared_ptr &cha const tm &creationTime = Utils::getTimeTAsTm(chatRoom->getCreationTime()); // Remove capabilities like `Proxy`. - const int &capabilities = chatRoom->getCapabilities() & ChatRoom::CapabilitiesMask({ - ChatRoom::Capabilities::Basic, ChatRoom::Capabilities::RealTimeText, - ChatRoom::Capabilities::Conference, ChatRoom::Capabilities::Migratable - }); + const int &capabilities = chatRoom->getCapabilities() & ~ChatRoom::CapabilitiesMask(ChatRoom::Capabilities::Proxy); const string &subject = chatRoom->getSubject(); const int &flags = chatRoom->hasBeenLeft(); @@ -409,6 +406,18 @@ long long MainDbPrivate::selectChatRoomParticipantId (long long chatRoomId, long return session->got_data() ? id : -1; } +long long MainDbPrivate::selectOneToOneChatRoomId (long long sipAddressIdA, long long sipAddressIdB) const { + long long id; + soci::session *session = dbSession.getBackendSession(); + *session << "SELECT chat_room_id" + " FROM one_to_one_chat_room" + " WHERE participant_a_sip_address_id IN (:sipAddressIdA, :sipAddressIdB)" + " AND participant_b_sip_address_id IN (:sipAddressIdABis, :sipAddressIdBBis)", + soci::into(id), + soci::use(sipAddressIdA), soci::use(sipAddressIdB), soci::use(sipAddressIdA), soci::use(sipAddressIdB); + return session->got_data() ? id : -1; +} + // ----------------------------------------------------------------------------- void MainDbPrivate::deleteContents (long long messageEventId) { @@ -1372,6 +1381,26 @@ void MainDb::init () { " ON DELETE CASCADE" ") " + charset; + if (linphone_core_conference_server_enabled(getCore()->getCCore())) { + *session << + "CREATE TABLE IF NOT EXISTS one_to_one_chat_room (" + " chat_room_id" + primaryKeyStr("BIGINT UNSIGNED") + "," + + " participant_a_sip_address_id" + primaryKeyRefStr("BIGINT UNSIGNED") + " NOT NULL," + " participant_b_sip_address_id" + primaryKeyRefStr("BIGINT UNSIGNED") + " NOT NULL," + + " FOREIGN KEY (chat_room_id)" + " REFERENCES chat_room(id)" + " ON DELETE CASCADE," + " FOREIGN KEY (participant_a_sip_address_id)" + " REFERENCES sip_address(id)" + " ON DELETE CASCADE," + " FOREIGN KEY (participant_b_sip_address_id)" + " REFERENCES sip_address(id)" + " ON DELETE CASCADE" + ") " + charset; + } + *session << "CREATE TABLE IF NOT EXISTS chat_room_participant (" " id" + primaryKeyStr("BIGINT UNSIGNED") + "," @@ -2433,6 +2462,7 @@ list> MainDb::getChatRooms () const { core, chatRoomId, me, + capabilities, subject, move(participants), lastNotifyId @@ -2447,6 +2477,7 @@ list> MainDb::getChatRooms () const { chatRoom = make_shared( core, chatRoomId.getPeerAddress(), + capabilities, subject, move(participants), lastNotifyId @@ -2600,17 +2631,6 @@ IdentityAddress MainDb::findOneToOneConferenceChatRoomAddress ( const IdentityAddress &participantA, const IdentityAddress &participantB ) const { - static const string query = "SELECT sip_address.value" - " FROM chat_room, sip_address" - " WHERE capabilities = " + Utils::toString(static_cast(ChatRoom::Capabilities::Conference)) + - " AND (SELECT COUNT(*) FROM chat_room_participant WHERE chat_room_id = chat_room.id) = 2" - " AND (SELECT COUNT(*) FROM chat_room_participant WHERE chat_room_id = chat_room.id AND participant_sip_address_id IN (" - " (SELECT id FROM sip_address WHERE value = :participantSipAddressA)," - " (SELECT id FROM sip_address WHERE value = :participantSipAddressB)" - " )) = 2" - " AND sip_address.id = peer_sip_address_id" - " LIMIT 1"; - L_D(); if (!isConnected()) { @@ -2618,24 +2638,63 @@ IdentityAddress MainDb::findOneToOneConferenceChatRoomAddress ( return IdentityAddress(); } + string chatRoomAddress; + L_BEGIN_LOG_EXCEPTION soci::session *session = d->dbSession.getBackendSession(); soci::transaction tr(*session); - const string &participantSipAddressA = participantA.asString(); - const string &participantSipAddressB = participantB.asString(); + const long long &participantASipAddressId = d->selectSipAddressId(participantA.asString()); + const long long &participantBSipAddressId = d->selectSipAddressId(participantB.asString()); + if ((participantASipAddressId == -1) || (participantBSipAddressId == -1)) + return IdentityAddress(); - string chatRoomAddress; + const long long &chatRoomId = d->selectOneToOneChatRoomId(participantASipAddressId, participantBSipAddressId); - *session << query, soci::use(participantSipAddressA), soci::use(participantSipAddressB), soci::into(chatRoomAddress); - - return IdentityAddress(chatRoomAddress); + *session << "SELECT sip_address.value" + " FROM chat_room, sip_address" + " WHERE chat_room.id = :chatRoomId AND peer_sip_address_id = sip_address.id", + soci::use(chatRoomId), soci::into(chatRoomAddress); L_END_LOG_EXCEPTION - // Soci error. - return IdentityAddress(); + return IdentityAddress(chatRoomAddress); +} + +void MainDb::insertOneToOneConferenceChatRoom (const shared_ptr &chatRoom) { + L_D(); + L_ASSERT(linphone_core_conference_server_enabled(chatRoom->getCore()->getCCore())); + L_ASSERT(chatRoom->getCapabilities() & ChatRoom::Capabilities::OneToOne); + + if (!isConnected()) { + lWarning() << "Unable to insert one to one conference chat room. Not connected."; + return; + } + + L_BEGIN_LOG_EXCEPTION + + soci::session *session = d->dbSession.getBackendSession(); + soci::transaction tr(*session); + + const list> &participants = chatRoom->getParticipants(); + const long long &participantASipAddressId = d->selectSipAddressId(participants.front()->getAddress().asString()); + const long long &participantBSipAddressId = d->selectSipAddressId(participants.back()->getAddress().asString()); + L_ASSERT(participantASipAddressId != -1); + L_ASSERT(participantBSipAddressId != -1); + + long long chatRoomId = d->selectOneToOneChatRoomId(participantASipAddressId, participantBSipAddressId); + if (chatRoomId == -1) { + chatRoomId = d->selectChatRoomId(chatRoom->getChatRoomId()); + *session << "INSERT INTO one_to_one_chat_room (" + " chat_room_id, participant_a_sip_address_id, participant_b_sip_address_id" + ") VALUES (:chatRoomId, :participantASipAddressId, :participantBSipAddressId)", + soci::use(chatRoomId), soci::use(participantASipAddressId), soci::use(participantBSipAddressId); + } + + tr.commit(); + + L_END_LOG_EXCEPTION } void MainDb::enableChatRoomMigration (const ChatRoomId &chatRoomId, bool enable) { diff --git a/src/db/main-db.h b/src/db/main-db.h index 188cc655e..693fda14f 100644 --- a/src/db/main-db.h +++ b/src/db/main-db.h @@ -130,6 +130,8 @@ public: const IdentityAddress &participantB ) const; + void insertOneToOneConferenceChatRoom (const std::shared_ptr &chatRoom); + // --------------------------------------------------------------------------- // Other. // --------------------------------------------------------------------------- diff --git a/tester/conference-event-tester.cpp b/tester/conference-event-tester.cpp index 1dc2f8460..db089e34b 100644 --- a/tester/conference-event-tester.cpp +++ b/tester/conference-event-tester.cpp @@ -449,6 +449,7 @@ public: private: void onConferenceCreated (const IdentityAddress &addr) override; + void onConferenceKeywordsChanged (const vector &keywords) override; void onConferenceTerminated (const IdentityAddress &addr) override; void onFirstNotifyReceived (const IdentityAddress &addr) override; void onParticipantAdded (const shared_ptr &event, bool isFullState) override; @@ -463,6 +464,7 @@ public: map participants; map participantDevices; string confSubject; + bool oneToOne = false; }; ConferenceEventTester::ConferenceEventTester (const shared_ptr &core, const Address &confAddr) @@ -476,6 +478,13 @@ ConferenceEventTester::~ConferenceEventTester () { void ConferenceEventTester::onConferenceCreated (const IdentityAddress &addr) {} +void ConferenceEventTester::onConferenceKeywordsChanged (const vector &keywords) { + for (const auto &k : keywords) { + if (k == "one-to-one") + oneToOne = true; + } +} + void ConferenceEventTester::onConferenceTerminated (const IdentityAddress &addr) {} void ConferenceEventTester::onFirstNotifyReceived (const IdentityAddress &addr) {} @@ -1277,6 +1286,42 @@ void send_device_removed_notify() { linphone_core_manager_destroy(pauline); } +void one_to_one_keyword () { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + char *identityStr = linphone_address_as_string(pauline->identity); + Address addr(identityStr); + bctbx_free(identityStr); + shared_ptr tester = make_shared(marie->lc->cppPtr, addr); + shared_ptr localConf = make_shared(pauline->lc->cppPtr, addr, nullptr); + LinphoneAddress *cBobAddr = linphone_core_interpret_url(marie->lc, bobUri); + char *bobAddrStr = linphone_address_as_string(cBobAddr); + Address bobAddr(bobAddrStr); + bctbx_free(bobAddrStr); + linphone_address_unref(cBobAddr); + + CallSessionParams params; + localConf->addParticipant(bobAddr, ¶ms, false); + LocalConferenceEventHandlerPrivate *localHandlerPrivate = L_GET_PRIVATE( + L_ATTR_GET(L_GET_PRIVATE(localConf), eventHandler) + ); + const_cast(localConf->getConferenceAddress()) = addr; + string notify = localHandlerPrivate->createNotifyFullState(-1, true); + + const_cast(tester->handler->getChatRoomId().getPeerAddress()) = addr; + tester->handler->notifyReceived(notify); + + BC_ASSERT_EQUAL(tester->participantDevices.size(), 1, int, "%d"); + BC_ASSERT_TRUE(tester->participantDevices.find(bobAddr.asString()) != tester->participantDevices.end()); + BC_ASSERT_EQUAL(tester->participantDevices.find(bobAddr.asString())->second, 0, int, "%d"); + BC_ASSERT_TRUE(tester->oneToOne); + + tester = nullptr; + localConf = nullptr; + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + test_t conference_event_tests[] = { TEST_NO_TAG("First notify parsing", first_notify_parsing), TEST_NO_TAG("First notify parsing wrong conf", first_notify_parsing_wrong_conf), @@ -1292,7 +1337,8 @@ test_t conference_event_tests[] = { TEST_NO_TAG("Send participant unadmined notify", send_unadmined_notify), TEST_NO_TAG("Send subject changed notify", send_subject_changed_notify), TEST_NO_TAG("Send device added notify", send_device_added_notify), - TEST_NO_TAG("Send device removed notify", send_device_removed_notify) + TEST_NO_TAG("Send device removed notify", send_device_removed_notify), + TEST_NO_TAG("one-to-one keyword", one_to_one_keyword) }; test_suite_t conference_event_test_suite = { diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index 8dee2b75b..1b7b1d98f 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -93,6 +93,9 @@ static void chat_room_state_changed (LinphoneChatRoom *cr, LinphoneChatRoomState case LinphoneChatRoomStateCreationFailed: manager->stat.number_of_LinphoneChatRoomStateCreationFailed++; break; + case LinphoneChatRoomStateDeleted: + manager->stat.number_of_LinphoneChatRoomStateDeleted++; + break; } } @@ -2033,10 +2036,72 @@ static void group_chat_room_send_file (void) { group_chat_room_send_file_with_or_without_text(FALSE); } -static void group_chat_room_send_file_plus_text(void) { +static void group_chat_room_send_file_plus_text (void) { group_chat_room_send_file_with_or_without_text(TRUE); } +static void group_chat_room_unique_one_to_one_chat_room (void) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + bctbx_list_t *coresManagerList = NULL; + bctbx_list_t *participantsAddresses = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(pauline->lc))); + stats initialMarieStats = marie->stat; + stats initialPaulineStats = pauline->stat; + + // Marie creates a new group chat room + const char *initialSubject = "Pauline"; + LinphoneChatRoom *marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1); + BC_ASSERT_TRUE(linphone_chat_room_get_capabilities(marieCr) & LinphoneChatRoomCapabilitiesOneToOne); + + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline, &initialPaulineStats, confAddr, initialSubject, 1, 0); + BC_ASSERT_TRUE(linphone_chat_room_get_capabilities(paulineCr) & LinphoneChatRoomCapabilitiesOneToOne); + + // Marie sends a message + const char *message = "Hello"; + _send_message(marieCr, message); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDelivered, initialMarieStats.number_of_LinphoneMessageDelivered + 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 10000)); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(pauline->stat.last_received_chat_message), message); + + // Marie deletes the chat room + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + + // Marie creates the chat room again + initialMarieStats = marie->stat; + initialPaulineStats = pauline->stat; + participantsAddresses = bctbx_list_append(NULL, linphone_address_new(linphone_core_get_identity(pauline->lc))); + marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1); + + // Marie sends a new message + message = "Hey again"; + _send_message(marieCr, message); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDelivered, initialMarieStats.number_of_LinphoneMessageDelivered + 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 10000)); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(pauline->stat.last_received_chat_message), message); + + // Check that the created address is the same as before + const LinphoneAddress *newConfAddr = linphone_chat_room_get_conference_address(marieCr); + BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, newConfAddr)); + + // Clean db from chat room + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + test_t group_chat_tests[] = { TEST_TWO_TAGS("Group chat room creation server", group_chat_room_creation_server, "Server", "LeaksMemory"), TEST_TWO_TAGS("Send message", group_chat_room_send_message, "Server", "LeaksMemory"), @@ -2062,7 +2127,8 @@ test_t group_chat_tests[] = { TEST_TWO_TAGS("Migrate basic chat room to client group chat room failure", group_chat_room_migrate_from_basic_to_client_fail, "Server", "LeaksMemory"), TEST_TWO_TAGS("Migrate basic chat room to client group chat room not needed", group_chat_donot_room_migrate_from_basic_chat_room, "Server", "LeaksMemory"), TEST_TWO_TAGS("Send file", group_chat_room_send_file, "Server", "LeaksMemory"), - TEST_TWO_TAGS("Send file + text", group_chat_room_send_file_plus_text, "Server", "LeaksMemory") + TEST_TWO_TAGS("Send file + text", group_chat_room_send_file_plus_text, "Server", "LeaksMemory"), + TEST_TWO_TAGS("Unique one-to-one chatroom", group_chat_room_unique_one_to_one_chat_room, "Server", "LeaksMemory") }; test_suite_t group_chat_test_suite = { diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index f3c7c0fe9..798d07d63 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -189,6 +189,7 @@ typedef struct _stats { int number_of_LinphoneChatRoomStateTerminationPending; int number_of_LinphoneChatRoomStateTerminated; int number_of_LinphoneChatRoomStateCreationFailed; + int number_of_LinphoneChatRoomStateDeleted; int number_of_IframeDecoded; @@ -331,6 +332,7 @@ void linphone_core_manager_restart(LinphoneCoreManager *mgr, bool_t check_for_pr void linphone_core_manager_uninit(LinphoneCoreManager *mgr); void linphone_core_manager_wait_for_stun_resolution(LinphoneCoreManager *mgr); void linphone_core_manager_destroy(LinphoneCoreManager* mgr); +void linphone_core_manager_delete_chat_room (LinphoneCoreManager *mgr, LinphoneChatRoom *cr, bctbx_list_t *coresList); void reset_counters( stats* counters); diff --git a/tester/tester.c b/tester/tester.c index 41f7fcdaa..472c0df46 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -534,6 +534,12 @@ void linphone_core_manager_destroy(LinphoneCoreManager* mgr) { ms_free(mgr); } +void linphone_core_manager_delete_chat_room (LinphoneCoreManager *mgr, LinphoneChatRoom *cr, bctbx_list_t *coresList) { + stats mgrStats = mgr->stat; + linphone_core_delete_chat_room(mgr->lc, cr); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneChatRoomStateDeleted, mgrStats.number_of_LinphoneChatRoomStateDeleted + 1, 10000)); +} + int liblinphone_tester_ipv6_available(void){ if (liblinphonetester_ipv6) { struct addrinfo *ai=bctbx_ip_address_to_addrinfo(AF_INET6,SOCK_STREAM,"2a01:e00::2",53);