diff --git a/coreapi/call_log.c b/coreapi/call_log.c index 176ad4960..abdcf98b5 100644 --- a/coreapi/call_log.c +++ b/coreapi/call_log.c @@ -198,7 +198,11 @@ const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){ return cl->refkey; } -LinphoneAddress *linphone_call_log_get_remote_address(const LinphoneCallLog *cl){ +const LinphoneAddress *linphone_call_log_get_local_address(const LinphoneCallLog *cl) { + return (cl->dir == LinphoneCallIncoming) ? cl->to : cl->from; +} + +const LinphoneAddress *linphone_call_log_get_remote_address(const LinphoneCallLog *cl){ return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to; } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index f3554763d..535883882 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "c-wrapper/c-wrapper.h" #include "call/call-p.h" +#include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/chat-room.h" #include "chat/chat-room/server-group-chat-room-p.h" #include "conference/participant.h" @@ -565,7 +566,7 @@ static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status) // Check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks if (msg->getChatRoom()) - msg->updateState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status)); + L_GET_PRIVATE(msg)->setState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status)); } static void info_received(SalOp *op, SalBodyHandler *body_handler) { diff --git a/coreapi/chat.c b/coreapi/chat.c index 19a3d040a..ac9c8dcf0 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -36,6 +36,7 @@ #include "linphone/wrapper_utils.h" #include "c-wrapper/c-wrapper.h" +#include "call/call.h" #include "chat/chat-room/basic-chat-room.h" #include "chat/chat-room/client-group-chat-room.h" #include "chat/chat-room/client-group-to-basic-chat-room.h" @@ -149,12 +150,9 @@ int linphone_core_message_received(LinphoneCore *lc, LinphonePrivate::SalOp *op, } void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call) { - if (linphone_core_realtime_text_enabled(lc)) { - shared_ptr rttcr = - static_pointer_cast(L_GET_CPP_PTR_FROM_C_OBJECT(cr)); - L_GET_PRIVATE(rttcr)->realtimeTextReceived(character, call); - //L_GET_PRIVATE(static_pointer_cast(L_GET_CPP_PTR_FROM_C_OBJECT(cr)))->realtimeTextReceived(character, call); - } + if (!(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText)) + return; + L_GET_PRIVATE_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->realtimeTextReceived(character, L_GET_CPP_PTR_FROM_C_OBJECT(call)); } unsigned int linphone_chat_message_store(LinphoneChatMessage *msg) { diff --git a/coreapi/friendlist.c b/coreapi/friendlist.c index 159409449..b92c13a02 100644 --- a/coreapi/friendlist.c +++ b/coreapi/friendlist.c @@ -766,7 +766,7 @@ void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *li LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) { LinphoneAddress *clean_addr = linphone_address_clone(address); LinphoneFriend *lf; - if (linphone_address_has_param(clean_addr, "gr")) { + if (linphone_address_has_uri_param(clean_addr, "gr")) { linphone_address_remove_uri_param(clean_addr, "gr"); } char *uri = linphone_address_as_string_uri_only(clean_addr); diff --git a/coreapi/misc.c b/coreapi/misc.c index c95d2fa54..b82e749bf 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -917,7 +917,7 @@ void linphone_core_report_call_log(LinphoneCore *lc, LinphoneCallLog *call_log){ linphone_address_unref(conference_factory_addr); } const char *username = linphone_address_get_username(call_log->to); - if (strstr(username, "chatroom-") == username) + if (username && (strstr(username, "chatroom-") == username)) return; // End of workaround diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index e0e48308f..0908f70ea 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -355,7 +355,6 @@ LinphoneChatMessageStateChangedCb linphone_chat_message_get_message_state_change void linphone_chat_message_set_message_state_changed_cb(LinphoneChatMessage* msg, LinphoneChatMessageStateChangedCb cb); void linphone_chat_message_set_message_state_changed_cb_user_data(LinphoneChatMessage* msg, void *user_data); void * linphone_chat_message_get_message_state_changed_cb_user_data(LinphoneChatMessage* msg); -void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state); LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call); void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id); diff --git a/coreapi/private_structs.h b/coreapi/private_structs.h index 073598723..993e5f983 100644 --- a/coreapi/private_structs.h +++ b/coreapi/private_structs.h @@ -151,11 +151,6 @@ struct _LinphoneAuthInfo char *algorithm; }; -struct _LinphoneChatMessageCharacter { - uint32_t value; - bool_t has_been_read; -}; - struct _LinphoneFriendPresence { char *uri_or_tel; LinphonePresenceModel *presence; diff --git a/coreapi/private_types.h b/coreapi/private_types.h index f646ce3ce..5e7ca8029 100644 --- a/coreapi/private_types.h +++ b/coreapi/private_types.h @@ -27,8 +27,6 @@ typedef struct StunCandidate StunCandidate; typedef struct _PortConfig PortConfig; -typedef struct _LinphoneChatMessageCharacter LinphoneChatMessageCharacter; - typedef struct _LinphoneFriendPresence LinphoneFriendPresence; typedef struct _LinphoneFriendPhoneNumberSipUri LinphoneFriendPhoneNumberSipUri; diff --git a/include/linphone/api/c-callbacks.h b/include/linphone/api/c-callbacks.h index a053e78a8..ea533f2c8 100644 --- a/include/linphone/api/c-callbacks.h +++ b/include/linphone/api/c-callbacks.h @@ -249,6 +249,13 @@ typedef void (*LinphoneChatRoomCbsParticipantDeviceFetchedCb) (LinphoneChatRoom */ typedef void (*LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb) (LinphoneChatRoom *cr, const LinphoneAddress *deviceAddr, const bctbx_list_t *participantsAddr); +/** + * Callback used to tell the core whether or not to store the incoming message in db or not using linphone_chat_message_set_to_be_stored(). + * @param[in] cr #LinphoneChatRoom object + * @param[in] msg The #LinphoneChatMessage that is being received + */ +typedef void (*LinphoneChatRoomCbsShouldChatMessageBeStoredCb) (LinphoneChatRoom *cr, LinphoneChatMessage *msg); + /** * @} **/ diff --git a/include/linphone/api/c-chat-room-cbs.h b/include/linphone/api/c-chat-room-cbs.h index 9dc396fac..0e02b615c 100644 --- a/include/linphone/api/c-chat-room-cbs.h +++ b/include/linphone/api/c-chat-room-cbs.h @@ -271,6 +271,19 @@ LINPHONE_PUBLIC LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb linphone_ch */ LINPHONE_PUBLIC void linphone_chat_room_cbs_set_participants_capabilities_checked (LinphoneChatRoomCbs *cbs, LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb cb); +/** + * Get the message should be stored callback. + * @param[in] cbs LinphoneChatRoomCbs object + * @return The message should be stored getting callback + */ +LINPHONE_PUBLIC LinphoneChatRoomCbsShouldChatMessageBeStoredCb linphone_chat_room_cbs_get_chat_message_should_be_stored( LinphoneChatRoomCbs *cbs); +/** + * Set the message should be stored callback. + * @param[in] cbs LinphoneChatRoomCbs object + * @param[in] cb The message should be stored callback to be used + */ +LINPHONE_PUBLIC void linphone_chat_room_cbs_set_chat_message_should_be_stored( LinphoneChatRoomCbs *cbs, LinphoneChatRoomCbsShouldChatMessageBeStoredCb cb); + /** * @} */ diff --git a/include/linphone/api/c-chat-room.h b/include/linphone/api/c-chat-room.h index 392bce10f..8fc03d6c6 100644 --- a/include/linphone/api/c-chat-room.h +++ b/include/linphone/api/c-chat-room.h @@ -179,6 +179,23 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history (LinphoneChatRoom * */ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range(LinphoneChatRoom *cr, int begin, int end); +/** + * Gets nb_events most recent chat message events from cr chat room, sorted from oldest to most recent. + * @param[in] cr The #LinphoneChatRoom object corresponding to the conversation for which events should be retrieved + * @param[in] nb_events Number of events to retrieve. 0 means everything. + * @return \bctbx_list{LinphoneEventLog} + */ +LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_message_events (LinphoneChatRoom *cr, int nb_events); + +/** + * Gets the partial list of chat message events in the given range, sorted from oldest to most recent. + * @param[in] cr The #LinphoneChatRoom object corresponding to the conversation for which events should be retrieved + * @param[in] begin The first event of the range to be retrieved. History most recent event has index 0. + * @param[in] end The last event of the range to be retrieved. History oldest event has index of history size - 1 + * @return \bctbx_list{LinphoneEventLog} + */ +LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range_message_events (LinphoneChatRoom *cr, int begin, int end); + /** * Gets nb_events most recent events from cr chat room, sorted from oldest to most recent. * @param[in] cr The #LinphoneChatRoom object corresponding to the conversation for which events should be retrieved @@ -281,6 +298,13 @@ LINPHONE_PUBLIC LinphoneChatRoomState linphone_chat_room_get_state (const Linpho */ LINPHONE_PUBLIC bool_t linphone_chat_room_has_been_left (const LinphoneChatRoom *cr); +/** + * Return the last updated time for the chat room + * @param[in] cr LinphoneChatRoom object + * @return the last updated time + */ +LINPHONE_PUBLIC time_t linphone_chat_room_get_last_update_time(const LinphoneChatRoom *cr); + /** * Add a participant to a chat room. This may fail if this type of chat room does not handle participants. * Use linphone_chat_room_can_handle_participants() to know if this chat room handles participants. diff --git a/include/linphone/call_log.h b/include/linphone/call_log.h index 205865fad..004377e55 100644 --- a/include/linphone/call_log.h +++ b/include/linphone/call_log.h @@ -90,12 +90,19 @@ LINPHONE_PUBLIC float linphone_call_log_get_quality(const LinphoneCallLog *cl); **/ LINPHONE_PUBLIC const char * linphone_call_log_get_ref_key(const LinphoneCallLog *cl); +/** + * Get the local address (that is from or to depending on call direction) + * @param[in] cl LinphoneCallLog object + * @return The local address of the call + */ +LINPHONE_PUBLIC const LinphoneAddress *linphone_call_log_get_local_address(const LinphoneCallLog *cl); + /** * Get the remote address (that is from or to depending on call direction). * @param[in] cl LinphoneCallLog object * @return The remote address of the call. **/ -LINPHONE_PUBLIC LinphoneAddress * linphone_call_log_get_remote_address(const LinphoneCallLog *cl); +LINPHONE_PUBLIC const LinphoneAddress * linphone_call_log_get_remote_address(const LinphoneCallLog *cl); /** * Get the RTP statistics computed by the remote end and sent back via RTCP. diff --git a/include/linphone/utils/general.h b/include/linphone/utils/general.h index c1970637d..5a4c3b15f 100644 --- a/include/linphone/utils/general.h +++ b/include/linphone/utils/general.h @@ -27,6 +27,10 @@ // ============================================================================= +// ----------------------------------------------------------------------------- +// Namespace. +// ----------------------------------------------------------------------------- + #ifdef __cplusplus #define LINPHONE_BEGIN_NAMESPACE namespace LinphonePrivate { #define LINPHONE_END_NAMESPACE } @@ -75,10 +79,10 @@ LINPHONE_BEGIN_NAMESPACE // Debug. // ----------------------------------------------------------------------------- -void l_assert (const char *condition, const char *file, int line); +void lAssert (const char *condition, const char *file, int line); #ifdef DEBUG - #define L_ASSERT(CONDITION) ((CONDITION) ? static_cast(0) : LinphonePrivate::l_assert(#CONDITION, __FILE__, __LINE__)) + #define L_ASSERT(CONDITION) ((CONDITION) ? static_cast(0) : LinphonePrivate::lAssert(#CONDITION, __FILE__, __LINE__)) #else #define L_ASSERT(CONDITION) static_cast(false && (CONDITION)) #endif @@ -100,16 +104,26 @@ void l_assert (const char *condition, const char *file, int line); // ----------------------------------------------------------------------------- // Define an integer version like: 0xXXYYZZ, XX=MAJOR, YY=MINOR, and ZZ=PATCH. -#define L_VERSION(MAJOR, MINOR, PATCH) (((MAJOR) << 16) | ((MINOR) << 8) | (PATCH)) +constexpr unsigned int makeVersion (unsigned int major, unsigned int minor, unsigned int patch) { + return ((major << 16) | (minor << 8) | patch); +} // Not available in C++11... template -std::unique_ptr make_unique(Args && ...args) { +std::unique_ptr makeUnique(Args && ...args) { return std::unique_ptr(new T(std::forward(args)...)); } // ----------------------------------------------------------------------------- -// Data access. +// Class tools. +// ----------------------------------------------------------------------------- + +#define L_DISABLE_COPY(CLASS) \ + CLASS (const CLASS &) = delete; \ + CLASS &operator= (const CLASS &) = delete; + +// ----------------------------------------------------------------------------- +// PImpl tools. // ----------------------------------------------------------------------------- class BaseObject; @@ -119,34 +133,68 @@ class ClonableObjectPrivate; class Object; class ObjectPrivate; +namespace Private { + template + using BetterPrivateAncestor = typename std::conditional< + std::is_base_of::value, + BaseObject, + typename std::conditional< + std::is_base_of::value, + ClonableObject, + T + >::type + >::type; + + // Generic public helper. + template< + typename R, + typename P, + typename C + > + constexpr R *getPublicHelper (P *object, const C *) { + return static_cast(object); + } + + // Generic public helper. Deal with shared data. + template< + typename R, + typename P, + typename C + > + inline R *getPublicHelper (const P &objectSet, const C *) { + auto it = objectSet.cbegin(); + L_ASSERT(it != objectSet.cend()); + return static_cast(*it); + } + + template + struct AddConstMirror { + typedef U type; + }; + + template + struct AddConstMirror { + typedef typename std::add_const::type type; + }; +} + #define L_INTERNAL_CHECK_OBJECT_INHERITANCE(CLASS) \ static_assert( \ !(std::is_base_of::value && std::is_base_of::value), \ "Multiple inheritance between BaseObject and ClonableObject is not allowed." \ ); -#define L_INTERNAL_GET_BETTER_PRIVATE_ANCESTOR(CLASS) \ - std::conditional< \ - std::is_base_of::value, \ - BaseObject, \ - std::conditional< \ - std::is_base_of::value, \ - ClonableObject, \ - CLASS \ - >::type \ - >::type - #define L_INTERNAL_DECLARE_PRIVATE(CLASS) \ inline CLASS ## Private *getPrivate () { \ L_INTERNAL_CHECK_OBJECT_INHERITANCE(CLASS); \ return reinterpret_cast( \ - L_INTERNAL_GET_BETTER_PRIVATE_ANCESTOR(CLASS)::mPrivate \ + LinphonePrivate::Private::BetterPrivateAncestor::mPrivate \ ); \ } \ inline const CLASS ## Private *getPrivate () const { \ L_INTERNAL_CHECK_OBJECT_INHERITANCE(CLASS); \ return reinterpret_cast( \ - L_INTERNAL_GET_BETTER_PRIVATE_ANCESTOR(CLASS)::mPrivate \ + LinphonePrivate::Private::BetterPrivateAncestor::mPrivate \ ); \ } \ friend class CLASS ## Private; \ @@ -162,61 +210,25 @@ class ObjectPrivate; friend class Tester; #endif -// Generic public helper. -template< - typename R, - typename P, - typename C -> -constexpr R *getPublicHelper (P *object, const C *) { - return static_cast(object); -} - -// Generic public helper. Deal with shared data. -template< - typename R, - typename P, - typename C -> -inline R *getPublicHelper (const P &objectSet, const C *) { - auto it = objectSet.cbegin(); - L_ASSERT(it != objectSet.cend()); - return static_cast(*it); -} - #define L_DECLARE_PUBLIC(CLASS) \ - inline CLASS *getPublic () { \ - return getPublicHelper(mPublic, this); \ + CLASS *getPublic () { \ + return LinphonePrivate::Private::getPublicHelper(mPublic, this); \ } \ - inline const CLASS *getPublic () const { \ - return getPublicHelper(mPublic, this); \ + const CLASS *getPublic () const { \ + return LinphonePrivate::Private::getPublicHelper(mPublic, this); \ } \ friend class CLASS; -#define L_DISABLE_COPY(CLASS) \ - CLASS (const CLASS &) = delete; \ - CLASS &operator= (const CLASS &) = delete; - // Get Private data. #define L_D() decltype(getPrivate()) const d = getPrivate(); // Get Public data. #define L_Q() decltype(getPublic()) const q = getPublic(); -template -struct AddConstMirror { - typedef U type; -}; - -template -struct AddConstMirror { - typedef typename std::add_const::type type; -}; - // Get Private data of class in a multiple inheritance case. #define L_D_T(CLASS, NAME) \ auto const NAME = static_cast< \ - AddConstMirror< \ + LinphonePrivate::Private::AddConstMirror< \ std::remove_reference::type, \ CLASS ## Private \ >::type * \ @@ -225,12 +237,16 @@ struct AddConstMirror { // Get Private data of class in a multiple inheritance case. #define L_Q_T(CLASS, NAME) \ auto const NAME = static_cast< \ - AddConstMirror< \ + LinphonePrivate::Private::AddConstMirror< \ std::remove_reference::type, \ CLASS \ >::type * \ >(getPublic()); +// ----------------------------------------------------------------------------- +// Overload. +// ----------------------------------------------------------------------------- + #define L_OVERRIDE_SHARED_FROM_THIS(CLASS) \ inline std::shared_ptr getSharedFromThis () { \ return std::static_pointer_cast(Object::getSharedFromThis()); \ @@ -239,10 +255,6 @@ struct AddConstMirror { return std::static_pointer_cast(Object::getSharedFromThis()); \ } -// ----------------------------------------------------------------------------- -// Overload. -// ----------------------------------------------------------------------------- - namespace Private { template struct ResolveMemberFunctionOverload { @@ -273,7 +285,8 @@ namespace Private { } // Useful to select a specific overloaded function. (Avoid usage of static_cast.) -#define L_RESOLVE_OVERLOAD(ARGS) LinphonePrivate::Private::ResolveOverload +template +using resolveOverload = Private::ResolveOverload; // ----------------------------------------------------------------------------- // Wrapper public. diff --git a/src/c-wrapper/api/c-call.cpp b/src/c-wrapper/api/c-call.cpp index 3f0b4f376..07159bfc4 100644 --- a/src/c-wrapper/api/c-call.cpp +++ b/src/c-wrapper/api/c-call.cpp @@ -27,6 +27,7 @@ #include "call/call.h" #include "call/local-conference-call.h" #include "call/remote-conference-call.h" +#include "chat/chat-room/real-time-text-chat-room.h" #include "conference/params/media-session-params-p.h" #include "core/core-p.h" @@ -246,7 +247,7 @@ const char *linphone_call_get_to_header (const LinphoneCall *call, const char *n } char *linphone_call_get_remote_address_as_string (const LinphoneCall *call) { - return ms_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getRemoteAddressAsString().c_str()); + return ms_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getRemoteAddress().asString().c_str()); } const LinphoneAddress *linphone_call_get_diversion_address (const LinphoneCall *call) { @@ -538,16 +539,10 @@ bool_t linphone_call_echo_limiter_enabled (const LinphoneCall *call) { } LinphoneChatRoom *linphone_call_get_chat_room (LinphoneCall *call) { -#if 0 - if (!call->chat_room){ - if (call->state != LinphoneCallReleased && call->state != LinphoneCallEnd){ - call->chat_room = _linphone_core_create_chat_room_from_call(call); - } - } - return call->chat_room; -#else + shared_ptr acr = L_GET_PRIVATE_FROM_C_OBJECT(call)->getChatRoom(); + if (acr) + return L_GET_C_BACK_PTR(acr); return nullptr; -#endif } float linphone_call_get_play_volume (const LinphoneCall *call) { diff --git a/src/c-wrapper/api/c-chat-message.cpp b/src/c-wrapper/api/c-chat-message.cpp index da453b39c..04218bfeb 100644 --- a/src/c-wrapper/api/c-chat-message.cpp +++ b/src/c-wrapper/api/c-chat-message.cpp @@ -209,10 +209,6 @@ void linphone_chat_message_resend_2(LinphoneChatMessage *msg) { L_GET_CPP_PTR_FROM_C_OBJECT(msg)->send(); } -void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state) { - L_GET_CPP_PTR_FROM_C_OBJECT(msg)->updateState((LinphonePrivate::ChatMessage::State) new_state); -} - LinphoneStatus linphone_chat_message_put_char(LinphoneChatMessage *msg, uint32_t character) { return ((LinphoneStatus)L_GET_CPP_PTR_FROM_C_OBJECT(msg)->putCharacter(character)); } diff --git a/src/c-wrapper/api/c-chat-room-cbs.cpp b/src/c-wrapper/api/c-chat-room-cbs.cpp index c8c8ca89d..cce0b165a 100644 --- a/src/c-wrapper/api/c-chat-room-cbs.cpp +++ b/src/c-wrapper/api/c-chat-room-cbs.cpp @@ -41,6 +41,7 @@ struct _LinphoneChatRoomCbs { LinphoneChatRoomCbsConferenceAddressGenerationCb conferenceAddressGenerationCb; LinphoneChatRoomCbsParticipantDeviceFetchedCb participantDeviceFetchedCb; LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb participantsCapabilitiesChecked; + LinphoneChatRoomCbsShouldChatMessageBeStoredCb shouldMessageBeStoredCb; }; BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneChatRoomCbs); @@ -196,3 +197,11 @@ LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb linphone_chat_room_cbs_get_ void linphone_chat_room_cbs_set_participants_capabilities_checked (LinphoneChatRoomCbs *cbs, LinphoneChatRoomCbsParticipantsCapabilitiesCheckedCb cb) { cbs->participantsCapabilitiesChecked = cb; } + +LinphoneChatRoomCbsShouldChatMessageBeStoredCb linphone_chat_room_cbs_get_chat_message_should_be_stored( LinphoneChatRoomCbs *cbs) { + return cbs->shouldMessageBeStoredCb; +} + +void linphone_chat_room_cbs_set_chat_message_should_be_stored( LinphoneChatRoomCbs *cbs, LinphoneChatRoomCbsShouldChatMessageBeStoredCb cb) { + cbs->shouldMessageBeStoredCb = cb; +} diff --git a/src/c-wrapper/api/c-chat-room.cpp b/src/c-wrapper/api/c-chat-room.cpp index 3288f1073..d0cd1c61f 100644 --- a/src/c-wrapper/api/c-chat-room.cpp +++ b/src/c-wrapper/api/c-chat-room.cpp @@ -27,6 +27,7 @@ #include "address/identity-address.h" #include "c-wrapper/c-wrapper.h" +#include "call/call.h" #include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/basic-chat-room.h" #include "chat/chat-room/client-group-chat-room-p.h" @@ -156,9 +157,9 @@ void linphone_chat_room_receive_chat_message (LinphoneChatRoom *cr, LinphoneChat } uint32_t linphone_chat_room_get_char (const LinphoneChatRoom *cr) { - if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(cr))) - return L_GET_CPP_PTR_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->getChar(); - return 0; + if (!(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText)) + return 0; + return L_GET_CPP_PTR_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->getChar(); } void linphone_chat_room_compose (LinphoneChatRoom *cr) { @@ -166,14 +167,15 @@ void linphone_chat_room_compose (LinphoneChatRoom *cr) { } LinphoneCall *linphone_chat_room_get_call (const LinphoneChatRoom *cr) { - if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(cr))) - return L_GET_CPP_PTR_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->getCall(); - return nullptr; + if (!(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText)) + return nullptr; + return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->getCall()); } void linphone_chat_room_set_call (LinphoneChatRoom *cr, LinphoneCall *call) { - if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(cr))) - L_GET_PRIVATE_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->call = call; + if (!(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText)) + return; + L_GET_PRIVATE_FROM_C_OBJECT(cr, RealTimeTextChatRoom)->call = L_GET_CPP_PTR_FROM_C_OBJECT(call); } void linphone_chat_room_mark_as_read (LinphoneChatRoom *cr) { @@ -202,9 +204,8 @@ void linphone_chat_room_delete_history (LinphoneChatRoom *cr) { bctbx_list_t *linphone_chat_room_get_history_range (LinphoneChatRoom *cr, int startm, int endm) { list> chatMessages; - for (auto &event : L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getHistoryRange(startm, endm)) - if (event->getType() == LinphonePrivate::EventLog::Type::ConferenceChatMessage) - chatMessages.push_back(static_pointer_cast(event)->getChatMessage()); + for (auto &event : L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getMessageHistoryRange(startm, endm)) + chatMessages.push_back(static_pointer_cast(event)->getChatMessage()); return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(chatMessages); } @@ -213,6 +214,14 @@ bctbx_list_t *linphone_chat_room_get_history (LinphoneChatRoom *cr, int nb_messa return linphone_chat_room_get_history_range(cr, 0, nb_message); } +bctbx_list_t *linphone_chat_room_get_history_range_message_events (LinphoneChatRoom *cr, int startm, int endm) { + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getMessageHistoryRange(startm, endm)); +} + +bctbx_list_t *linphone_chat_room_get_history_message_events (LinphoneChatRoom *cr, int nb_events) { + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getMessageHistory(nb_events)); +} + bctbx_list_t *linphone_chat_room_get_history_events (LinphoneChatRoom *cr, int nb_events) { return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getHistory(nb_events)); } @@ -251,6 +260,10 @@ bool_t linphone_chat_room_has_been_left (const LinphoneChatRoom *cr) { return (bool_t)L_GET_CPP_PTR_FROM_C_OBJECT(cr)->hasBeenLeft(); } +time_t linphone_chat_room_get_last_update_time(const LinphoneChatRoom *cr) { + return L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getLastUpdateTime(); +} + void linphone_chat_room_add_participant (LinphoneChatRoom *cr, const LinphoneAddress *addr) { L_GET_CPP_PTR_FROM_C_OBJECT(cr)->addParticipant( LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(addr)), nullptr, false diff --git a/src/c-wrapper/internal/c-tools.h b/src/c-wrapper/internal/c-tools.h index 55d12a33b..a54e98c3b 100644 --- a/src/c-wrapper/internal/c-tools.h +++ b/src/c-wrapper/internal/c-tools.h @@ -338,7 +338,7 @@ public: typename = typename std::enable_if::value, CppType>::type > static inline std::shared_ptr getCppPtrFromC (const CType *cObject) { - return getCppPtrFromC(const_cast(cObject)); + return getCppPtrFromC::type, CppType>(const_cast(cObject)); } template< diff --git a/src/call/call-p.h b/src/call/call-p.h index 71eb8fa41..0c6f9ae65 100644 --- a/src/call/call-p.h +++ b/src/call/call-p.h @@ -34,6 +34,7 @@ LINPHONE_BEGIN_NAMESPACE class CallSession; +class RealTimeTextChatRoom; class CallPrivate : public ObjectPrivate, public CallSessionListener { public: @@ -50,6 +51,7 @@ public: virtual std::shared_ptr getActiveSession () const { return nullptr; } bool getAudioMuted () const; + std::shared_ptr getChatRoom (); LinphoneProxyConfig *getDestProxy () const; IceSession *getIceSession () const; @@ -72,43 +74,44 @@ private: void terminateBecauseOfLostMedia (); /* CallSessionListener */ - void onAckBeingSent (const std::shared_ptr &session, LinphoneHeaders *headers) override; - void onAckReceived (const std::shared_ptr &session, LinphoneHeaders *headers) override; - void onBackgroundTaskToBeStarted (const std::shared_ptr &session) override; - void onBackgroundTaskToBeStopped (const std::shared_ptr &session) override; - bool onCallSessionAccepted (const std::shared_ptr &session) override; - void onCallSessionConferenceStreamStarting (const std::shared_ptr &session, bool mute) override; - void onCallSessionConferenceStreamStopping (const std::shared_ptr &session) override; - void onCallSessionEarlyFailed (const std::shared_ptr &session, LinphoneErrorInfo *ei) override; - void onCallSessionSetReleased (const std::shared_ptr &session) override; - void onCallSessionSetTerminated (const std::shared_ptr &session) override; - void onCallSessionStartReferred (const std::shared_ptr &session) override; - void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) override; - void onCallSessionTransferStateChanged (const std::shared_ptr &session, CallSession::State state) override; - void onCheckForAcceptation (const std::shared_ptr &session) override; - void onDtmfReceived (const std::shared_ptr &session, char dtmf) override; - void onIncomingCallSessionNotified (const std::shared_ptr &session) override; - void onIncomingCallSessionStarted (const std::shared_ptr &session) override; - void onIncomingCallSessionTimeoutCheck (const std::shared_ptr &session, int elapsed, bool oneSecondElapsed) override; - void onInfoReceived (const std::shared_ptr &session, const LinphoneInfoMessage *im) override; - void onNoMediaTimeoutCheck (const std::shared_ptr &session, bool oneSecondElapsed) override; - void onEncryptionChanged (const std::shared_ptr &session, bool activated, const std::string &authToken) override; - void onCallSessionStateChangedForReporting (const std::shared_ptr &session) override; - void onRtcpUpdateForReporting (const std::shared_ptr &session, SalStreamType type) override; - void onStatsUpdated (const std::shared_ptr &session, const LinphoneCallStats *stats) override; - void onUpdateMediaInfoForReporting (const std::shared_ptr &session, int statsType) override; - void onResetCurrentSession (const std::shared_ptr &session) override; - void onSetCurrentSession (const std::shared_ptr &session) override; - void onFirstVideoFrameDecoded (const std::shared_ptr &session) override; - void onResetFirstVideoFrameDecoded (const std::shared_ptr &session) override; - void onPlayErrorTone (const std::shared_ptr &session, LinphoneReason reason) override; - void onRingbackToneRequested (const std::shared_ptr &session, bool requested) override; - void onStartRinging (const std::shared_ptr &session) override; - void onStopRinging (const std::shared_ptr &session) override; - void onStopRingingIfInCall (const std::shared_ptr &session) override; - void onStopRingingIfNeeded (const std::shared_ptr &session) override; - bool areSoundResourcesAvailable (const std::shared_ptr &session) override; - bool isPlayingRingbackTone (const std::shared_ptr &session) override; + void onAckBeingSent (const std::shared_ptr &session, LinphoneHeaders *headers) override; + void onAckReceived (const std::shared_ptr &session, LinphoneHeaders *headers) override; + void onBackgroundTaskToBeStarted (const std::shared_ptr &session) override; + void onBackgroundTaskToBeStopped (const std::shared_ptr &session) override; + bool onCallSessionAccepted (const std::shared_ptr &session) override; + void onCallSessionConferenceStreamStarting (const std::shared_ptr &session, bool mute) override; + void onCallSessionConferenceStreamStopping (const std::shared_ptr &session) override; + void onCallSessionEarlyFailed (const std::shared_ptr &session, LinphoneErrorInfo *ei) override; + void onCallSessionSetReleased (const std::shared_ptr &session) override; + void onCallSessionSetTerminated (const std::shared_ptr &session) override; + void onCallSessionStartReferred (const std::shared_ptr &session) override; + void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) override; + void onCallSessionTransferStateChanged (const std::shared_ptr &session, CallSession::State state) override; + void onCheckForAcceptation (const std::shared_ptr &session) override; + void onDtmfReceived (const std::shared_ptr &session, char dtmf) override; + void onIncomingCallSessionNotified (const std::shared_ptr &session) override; + void onIncomingCallSessionStarted (const std::shared_ptr &session) override; + void onIncomingCallSessionTimeoutCheck (const std::shared_ptr &session, int elapsed, bool oneSecondElapsed) override; + void onInfoReceived (const std::shared_ptr &session, const LinphoneInfoMessage *im) override; + void onNoMediaTimeoutCheck (const std::shared_ptr &session, bool oneSecondElapsed) override; + void onEncryptionChanged (const std::shared_ptr &session, bool activated, const std::string &authToken) override; + void onCallSessionStateChangedForReporting (const std::shared_ptr &session) override; + void onRtcpUpdateForReporting (const std::shared_ptr &session, SalStreamType type) override; + void onStatsUpdated (const std::shared_ptr &session, const LinphoneCallStats *stats) override; + void onUpdateMediaInfoForReporting (const std::shared_ptr &session, int statsType) override; + void onResetCurrentSession (const std::shared_ptr &session) override; + void onSetCurrentSession (const std::shared_ptr &session) override; + void onFirstVideoFrameDecoded (const std::shared_ptr &session) override; + void onResetFirstVideoFrameDecoded (const std::shared_ptr &session) override; + void onPlayErrorTone (const std::shared_ptr &session, LinphoneReason reason) override; + void onRingbackToneRequested (const std::shared_ptr &session, bool requested) override; + void onStartRinging (const std::shared_ptr &session) override; + void onStopRinging (const std::shared_ptr &session) override; + void onStopRingingIfInCall (const std::shared_ptr &session) override; + void onStopRingingIfNeeded (const std::shared_ptr &session) override; + bool areSoundResourcesAvailable (const std::shared_ptr &session) override; + bool isPlayingRingbackTone (const std::shared_ptr &session) override; + void onRealTimeTextCharacterReceived (const std::shared_ptr &session, RealtimeTextReceivedCharacter *character) override; mutable LinphonePlayer *player = nullptr; @@ -119,6 +122,8 @@ private: BackgroundTask bgTask; + mutable std::shared_ptr chatRoom; + L_DECLARE_PUBLIC(Call); }; diff --git a/src/call/call.cpp b/src/call/call.cpp index 973e352e3..9cd36d9ed 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -19,6 +19,7 @@ #include "c-wrapper/c-wrapper.h" #include "call-p.h" +#include "chat/chat-room/real-time-text-chat-room-p.h" #include "conference/params/media-session-params-p.h" #include "conference/session/call-session-p.h" #include "conference/session/media-session-p.h" @@ -37,6 +38,17 @@ bool CallPrivate::getAudioMuted () const { return static_pointer_cast(getActiveSession())->getPrivate()->getAudioMuted(); } +shared_ptr CallPrivate::getChatRoom () { + L_Q(); + if (!chatRoom && (q->getState() != CallSession::State::End) && (q->getState() != CallSession::State::Released)) { + ChatRoomId chatRoomId(q->getRemoteAddress(), q->getLocalAddress()); + RealTimeTextChatRoom *rttcr = new RealTimeTextChatRoom(q->getCore(), chatRoomId); + chatRoom.reset(rttcr); + rttcr->getPrivate()->setCall(q->getSharedFromThis()); + } + return chatRoom; +} + LinphoneProxyConfig *CallPrivate::getDestProxy () const { return getActiveSession()->getPrivate()->getDestProxy(); } @@ -167,7 +179,7 @@ void CallPrivate::startRemoteRing () { void CallPrivate::terminateBecauseOfLostMedia () { L_Q(); - lInfo() << "Call [" << q << "]: Media connectivity with " << q->getRemoteAddressAsString() + lInfo() << "Call [" << q << "]: Media connectivity with " << q->getRemoteAddress().asString() << " is lost, call is going to be terminated"; static_pointer_cast(getActiveSession())->terminateBecauseOfLostMedia(); linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallLost); @@ -175,25 +187,25 @@ void CallPrivate::terminateBecauseOfLostMedia () { // ----------------------------------------------------------------------------- -void CallPrivate::onAckBeingSent (const shared_ptr &session, LinphoneHeaders *headers) { +void CallPrivate::onAckBeingSent (const shared_ptr &session, LinphoneHeaders *headers) { L_Q(); linphone_call_notify_ack_processing(L_GET_C_BACK_PTR(q), headers, false); } -void CallPrivate::onAckReceived (const shared_ptr &session, LinphoneHeaders *headers) { +void CallPrivate::onAckReceived (const shared_ptr &session, LinphoneHeaders *headers) { L_Q(); linphone_call_notify_ack_processing(L_GET_C_BACK_PTR(q), headers, true); } -void CallPrivate::onBackgroundTaskToBeStarted (const shared_ptr &session) { +void CallPrivate::onBackgroundTaskToBeStarted (const shared_ptr &session) { bgTask.start(); } -void CallPrivate::onBackgroundTaskToBeStopped (const shared_ptr &session) { +void CallPrivate::onBackgroundTaskToBeStopped (const shared_ptr &session) { bgTask.stop(); } -bool CallPrivate::onCallSessionAccepted (const shared_ptr &session) { +bool CallPrivate::onCallSessionAccepted (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); bool wasRinging = false; @@ -214,21 +226,21 @@ bool CallPrivate::onCallSessionAccepted (const shared_ptr &se return wasRinging; } -void CallPrivate::onCallSessionConferenceStreamStarting (const shared_ptr &session, bool mute) { +void CallPrivate::onCallSessionConferenceStreamStarting (const shared_ptr &session, bool mute) { L_Q(); if (q->getCore()->getCCore()->conf_ctx) { linphone_conference_on_call_stream_starting(q->getCore()->getCCore()->conf_ctx, L_GET_C_BACK_PTR(q), mute); } } -void CallPrivate::onCallSessionConferenceStreamStopping (const shared_ptr &session) { +void CallPrivate::onCallSessionConferenceStreamStopping (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); if (lc->conf_ctx && _linphone_call_get_endpoint(L_GET_C_BACK_PTR(q))) linphone_conference_on_call_stream_stopping(lc->conf_ctx, L_GET_C_BACK_PTR(q)); } -void CallPrivate::onCallSessionEarlyFailed (const shared_ptr &session, LinphoneErrorInfo *ei) { +void CallPrivate::onCallSessionEarlyFailed (const shared_ptr &session, LinphoneErrorInfo *ei) { L_Q(); LinphoneCallLog *log = session->getLog(); linphone_core_report_early_failed_call(q->getCore()->getCCore(), @@ -239,12 +251,12 @@ void CallPrivate::onCallSessionEarlyFailed (const shared_ptr linphone_call_unref(L_GET_C_BACK_PTR(q)); } -void CallPrivate::onCallSessionSetReleased (const shared_ptr &session) { +void CallPrivate::onCallSessionSetReleased (const shared_ptr &session) { L_Q(); linphone_call_unref(L_GET_C_BACK_PTR(q)); } -void CallPrivate::onCallSessionSetTerminated (const shared_ptr &session) { +void CallPrivate::onCallSessionSetTerminated (const shared_ptr &session) { L_Q(); LinphoneCore *core = q->getCore()->getCCore(); if (q->getSharedFromThis() == q->getCore()->getCurrentCall()) { @@ -267,21 +279,21 @@ void CallPrivate::onCallSessionSetTerminated (const shared_ptrbw_controller); } -void CallPrivate::onCallSessionStartReferred (const shared_ptr &session) { +void CallPrivate::onCallSessionStartReferred (const shared_ptr &session) { startReferredCall(nullptr); } -void CallPrivate::onCallSessionStateChanged (const shared_ptr &session, CallSession::State state, const string &message) { +void CallPrivate::onCallSessionStateChanged (const shared_ptr &session, CallSession::State state, const string &message) { L_Q(); linphone_call_notify_state_changed(L_GET_C_BACK_PTR(q), static_cast(state), message.c_str()); } -void CallPrivate::onCallSessionTransferStateChanged (const shared_ptr &session, CallSession::State state) { +void CallPrivate::onCallSessionTransferStateChanged (const shared_ptr &session, CallSession::State state) { L_Q(); linphone_call_notify_transfer_state_changed(L_GET_C_BACK_PTR(q), static_cast(state)); } -void CallPrivate::onCheckForAcceptation (const shared_ptr &session) { +void CallPrivate::onCheckForAcceptation (const shared_ptr &session) { L_Q(); LinphoneCall *lcall = L_GET_C_BACK_PTR(q); bctbx_list_t *copy = bctbx_list_copy(linphone_core_get_calls(q->getCore()->getCCore())); @@ -304,23 +316,23 @@ void CallPrivate::onCheckForAcceptation (const shared_ptr &se bctbx_list_free(copy); } -void CallPrivate::onDtmfReceived (const shared_ptr &session, char dtmf) { +void CallPrivate::onDtmfReceived (const shared_ptr &session, char dtmf) { L_Q(); linphone_call_notify_dtmf_received(L_GET_C_BACK_PTR(q), dtmf); } -void CallPrivate::onIncomingCallSessionNotified (const shared_ptr &session) { +void CallPrivate::onIncomingCallSessionNotified (const shared_ptr &session) { L_Q(); /* The call is acceptable so we can now add it to our list */ q->getCore()->getPrivate()->addCall(q->getSharedFromThis()); } -void CallPrivate::onIncomingCallSessionStarted (const shared_ptr &session) { +void CallPrivate::onIncomingCallSessionStarted (const shared_ptr &session) { L_Q(); linphone_core_notify_incoming_call(q->getCore()->getCCore(), L_GET_C_BACK_PTR(q)); } -void CallPrivate::onIncomingCallSessionTimeoutCheck (const shared_ptr &session, int elapsed, bool oneSecondElapsed) { +void CallPrivate::onIncomingCallSessionTimeoutCheck (const shared_ptr &session, int elapsed, bool oneSecondElapsed) { L_Q(); if (oneSecondElapsed) lInfo() << "Incoming call ringing for " << elapsed << " seconds"; @@ -332,12 +344,12 @@ void CallPrivate::onIncomingCallSessionTimeoutCheck (const shared_ptr &session, const LinphoneInfoMessage *im) { +void CallPrivate::onInfoReceived (const shared_ptr &session, const LinphoneInfoMessage *im) { L_Q(); linphone_call_notify_info_message_received(L_GET_C_BACK_PTR(q), im); } -void CallPrivate::onNoMediaTimeoutCheck (const shared_ptr &session, bool oneSecondElapsed) { +void CallPrivate::onNoMediaTimeoutCheck (const shared_ptr &session, bool oneSecondElapsed) { L_Q(); int disconnectTimeout = linphone_core_get_nortp_timeout(q->getCore()->getCCore()); bool disconnected = false; @@ -349,42 +361,42 @@ void CallPrivate::onNoMediaTimeoutCheck (const shared_ptr &se terminateBecauseOfLostMedia(); } -void CallPrivate::onEncryptionChanged (const shared_ptr &session, bool activated, const string &authToken) { +void CallPrivate::onEncryptionChanged (const shared_ptr &session, bool activated, const string &authToken) { L_Q(); linphone_call_notify_encryption_changed(L_GET_C_BACK_PTR(q), activated, authToken.empty() ? nullptr : authToken.c_str()); } -void CallPrivate::onCallSessionStateChangedForReporting (const shared_ptr &session) { +void CallPrivate::onCallSessionStateChangedForReporting (const shared_ptr &session) { L_Q(); linphone_reporting_call_state_updated(L_GET_C_BACK_PTR(q)); } -void CallPrivate::onRtcpUpdateForReporting (const shared_ptr &session, SalStreamType type) { +void CallPrivate::onRtcpUpdateForReporting (const shared_ptr &session, SalStreamType type) { L_Q(); linphone_reporting_on_rtcp_update(L_GET_C_BACK_PTR(q), type); } -void CallPrivate::onStatsUpdated (const shared_ptr &session, const LinphoneCallStats *stats) { +void CallPrivate::onStatsUpdated (const shared_ptr &session, const LinphoneCallStats *stats) { L_Q(); linphone_call_notify_stats_updated(L_GET_C_BACK_PTR(q), stats); } -void CallPrivate::onUpdateMediaInfoForReporting (const std::shared_ptr &session, int statsType) { +void CallPrivate::onUpdateMediaInfoForReporting (const shared_ptr &session, int statsType) { L_Q(); linphone_reporting_update_media_info(L_GET_C_BACK_PTR(q), statsType); } -void CallPrivate::onResetCurrentSession (const shared_ptr &session) { +void CallPrivate::onResetCurrentSession (const shared_ptr &session) { L_Q(); q->getCore()->getPrivate()->setCurrentCall(nullptr); } -void CallPrivate::onSetCurrentSession (const shared_ptr &session) { +void CallPrivate::onSetCurrentSession (const shared_ptr &session) { L_Q(); q->getCore()->getPrivate()->setCurrentCall(q->getSharedFromThis()); } -void CallPrivate::onFirstVideoFrameDecoded (const shared_ptr &session) { +void CallPrivate::onFirstVideoFrameDecoded (const shared_ptr &session) { L_Q(); if (nextVideoFrameDecoded._func) { nextVideoFrameDecoded._func(L_GET_C_BACK_PTR(q), nextVideoFrameDecoded._user_data); @@ -393,16 +405,16 @@ void CallPrivate::onFirstVideoFrameDecoded (const shared_ptr } } -void CallPrivate::onResetFirstVideoFrameDecoded (const shared_ptr &session) { +void CallPrivate::onResetFirstVideoFrameDecoded (const shared_ptr &session) { resetFirstVideoFrameDecoded(); } -void CallPrivate::onPlayErrorTone (const shared_ptr &session, LinphoneReason reason) { +void CallPrivate::onPlayErrorTone (const shared_ptr &session, LinphoneReason reason) { L_Q(); linphone_core_play_call_error_tone(q->getCore()->getCCore(), reason); } -void CallPrivate::onRingbackToneRequested (const shared_ptr &session, bool requested) { +void CallPrivate::onRingbackToneRequested (const shared_ptr &session, bool requested) { L_Q(); if (requested && linphone_core_get_remote_ringback_tone(q->getCore()->getCCore())) playingRingbackTone = true; @@ -410,7 +422,7 @@ void CallPrivate::onRingbackToneRequested (const shared_ptr & playingRingbackTone = false; } -void CallPrivate::onStartRinging (const shared_ptr &session) { +void CallPrivate::onStartRinging (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); if (lc->ringstream) @@ -418,12 +430,12 @@ void CallPrivate::onStartRinging (const shared_ptr &session) startRemoteRing(); } -void CallPrivate::onStopRinging (const shared_ptr &session) { +void CallPrivate::onStopRinging (const shared_ptr &session) { L_Q(); linphone_core_stop_ringing(q->getCore()->getCCore()); } -void CallPrivate::onStopRingingIfInCall (const shared_ptr &session) { +void CallPrivate::onStopRingingIfInCall (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); // We stop the ring only if we have this current call or if we are in call @@ -432,7 +444,7 @@ void CallPrivate::onStopRingingIfInCall (const shared_ptr &se } } -void CallPrivate::onStopRingingIfNeeded (const shared_ptr &session) { +void CallPrivate::onStopRingingIfNeeded (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); bool stopRinging = true; @@ -448,17 +460,22 @@ void CallPrivate::onStopRingingIfNeeded (const shared_ptr &se linphone_core_stop_ringing(lc); } -bool CallPrivate::areSoundResourcesAvailable (const shared_ptr &session) { +bool CallPrivate::areSoundResourcesAvailable (const shared_ptr &session) { L_Q(); LinphoneCore *lc = q->getCore()->getCCore(); shared_ptr currentCall = q->getCore()->getCurrentCall(); return !linphone_core_is_in_conference(lc) && (!currentCall || (currentCall == q->getSharedFromThis())); } -bool CallPrivate::isPlayingRingbackTone (const shared_ptr &session) { +bool CallPrivate::isPlayingRingbackTone (const shared_ptr &session) { return playingRingbackTone; } +void CallPrivate::onRealTimeTextCharacterReceived (const shared_ptr &session, RealtimeTextReceivedCharacter *data) { + L_Q(); + getChatRoom()->getPrivate()->realtimeTextReceived(data->character, q->getSharedFromThis()); +} + // ============================================================================= Call::Call (CallPrivate &p, shared_ptr core) : Object(p), CoreAccessor(core) { @@ -521,7 +538,7 @@ LinphoneStatus Call::pause () { return static_pointer_cast(d->getActiveSession())->pause(); } -LinphoneStatus Call::redirect (const std::string &redirectUri) { +LinphoneStatus Call::redirect (const string &redirectUri) { L_D(); return d->getActiveSession()->redirect(redirectUri); } @@ -536,7 +553,7 @@ LinphoneStatus Call::sendDtmf (char dtmf) { return static_pointer_cast(d->getActiveSession())->sendDtmf(dtmf); } -LinphoneStatus Call::sendDtmfs (const std::string &dtmfs) { +LinphoneStatus Call::sendDtmfs (const string &dtmfs) { L_D(); return static_pointer_cast(d->getActiveSession())->sendDtmfs(dtmfs); } @@ -682,6 +699,11 @@ const LinphoneErrorInfo *Call::getErrorInfo () const { return d->getActiveSession()->getErrorInfo(); } +const Address &Call::getLocalAddress () const { + L_D(); + return d->getActiveSession()->getLocalAddress(); +} + LinphoneCallLog *Call::getLog () const { L_D(); return d->getActiveSession()->getLog(); @@ -756,11 +778,6 @@ const Address &Call::getRemoteAddress () const { return d->getActiveSession()->getRemoteAddress(); } -string Call::getRemoteAddressAsString () const { - L_D(); - return d->getActiveSession()->getRemoteAddressAsString(); -} - string Call::getRemoteContact () const { L_D(); return d->getActiveSession()->getRemoteContact(); @@ -823,7 +840,7 @@ const Address &Call::getToAddress () const { return d->getActiveSession()->getToAddress(); } -string Call::getToHeader (const std::string &name) const { +string Call::getToHeader (const string &name) const { L_D(); return d->getActiveSession()->getToHeader(name); } diff --git a/src/call/call.h b/src/call/call.h index 6d1059127..15204a797 100644 --- a/src/call/call.h +++ b/src/call/call.h @@ -36,6 +36,7 @@ class MediaSessionPrivate; class Call : public Object, public CoreAccessor { friend class CallSessionPrivate; + friend class ChatMessage; friend class CorePrivate; friend class MediaSessionPrivate; @@ -85,6 +86,7 @@ public: const Address &getDiversionAddress () const; int getDuration () const; const LinphoneErrorInfo *getErrorInfo () const; + const Address &getLocalAddress () const; LinphoneCallLog *getLog () const; RtpTransport *getMetaRtcpTransport (int streamIndex) const; RtpTransport *getMetaRtpTransport (int streamIndex) const; @@ -98,7 +100,6 @@ public: std::shared_ptr getReferer () const; std::string getReferTo () const; const Address &getRemoteAddress () const; - std::string getRemoteAddressAsString () const; std::string getRemoteContact () const; const MediaSessionParams *getRemoteParams () const; std::string getRemoteUserAgent () const; diff --git a/src/chat/chat-message/chat-message-p.h b/src/chat/chat-message/chat-message-p.h index 0eae6c24e..fd374a39e 100644 --- a/src/chat/chat-message/chat-message-p.h +++ b/src/chat/chat-message/chat-message-p.h @@ -60,7 +60,8 @@ public: void setDirection (ChatMessage::Direction dir); - void setState (ChatMessage::State state, bool force = false); + void setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState); + void setState (ChatMessage::State newState, bool force = false); void setTime (time_t time); @@ -134,6 +135,8 @@ public: void updateInDb (); private: + static bool validStateTransition (ChatMessage::State currentState, ChatMessage::State newState); + // TODO: Clean attributes. time_t time = ::ms_time(0); // TODO: Change me in all files. std::string imdnId; diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index d6f7d3f95..a254ff42d 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -25,7 +25,7 @@ #include "address/address.h" #include "c-wrapper/c-wrapper.h" -#include "call/call.h" +#include "call/call-p.h" #include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/chat-room-p.h" #include "chat/chat-room/client-group-to-basic-chat-room.h" @@ -63,28 +63,69 @@ void ChatMessagePrivate::setIsReadOnly (bool readOnly) { isReadOnly = readOnly; } -void ChatMessagePrivate::setState (ChatMessage::State s, bool force) { +void ChatMessagePrivate::setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState) { + L_Q(); + + if (!(q->getChatRoom()->getCapabilities() & AbstractChatRoom::Capabilities::Conference) + || (linphone_config_get_bool(linphone_core_get_config(q->getChatRoom()->getCore()->getCCore()), + "misc", "enable_simple_group_chat_message_state", TRUE + )) + ) { + setState(newState); + return; + } + + if (!dbKey.isValid()) + return; + + unique_ptr &mainDb = q->getChatRoom()->getCore()->getPrivate()->mainDb; + shared_ptr eventLog = mainDb->getEventFromKey(dbKey); + ChatMessage::State currentState = mainDb->getChatMessageParticipantState(eventLog, participantAddress); + if (!validStateTransition(currentState, newState)) + return; + + mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState); + + list states = mainDb->getChatMessageParticipantStates(eventLog); + size_t nbDisplayedStates = 0; + size_t nbDeliveredToUserStates = 0; + size_t nbNotDeliveredStates = 0; + for (const auto &state : states) { + switch (state) { + case ChatMessage::State::Displayed: + nbDisplayedStates++; + break; + case ChatMessage::State::DeliveredToUser: + nbDeliveredToUserStates++; + break; + case ChatMessage::State::NotDelivered: + nbNotDeliveredStates++; + break; + default: + break; + } + } + + if (nbNotDeliveredStates > 0) + setState(ChatMessage::State::NotDelivered); + else if (nbDisplayedStates == states.size()) + setState(ChatMessage::State::Displayed); + else if ((nbDisplayedStates + nbDeliveredToUserStates) == states.size()) + setState(ChatMessage::State::DeliveredToUser); +} + +void ChatMessagePrivate::setState (ChatMessage::State newState, bool force) { L_Q(); if (force) - state = s; + state = newState; - if (s == state) - return; - - if ( - (state == ChatMessage::State::Displayed || state == ChatMessage::State::DeliveredToUser) && - ( - s == ChatMessage::State::DeliveredToUser || - s == ChatMessage::State::Delivered || - s == ChatMessage::State::NotDelivered - ) - ) + if (!validStateTransition(state, newState)) return; lInfo() << "Chat message " << this << ": moving from " << Utils::toString(state) << - " to " << Utils::toString(s); - state = s; + " to " << Utils::toString(newState); + state = newState; LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q); if (linphone_chat_message_get_message_state_changed_cb(msg)) @@ -401,18 +442,23 @@ void ChatMessagePrivate::notifyReceiving () { LinphoneChatRoom *chatRoom = L_GET_C_BACK_PTR(q->getChatRoom()); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(chatRoom); - LinphoneChatRoomCbsParticipantAddedCb cb = linphone_chat_room_cbs_get_chat_message_received(cbs); - shared_ptr event = make_shared( - ::time(nullptr), q->getSharedFromThis() - ); - if (cb) - cb(chatRoom, L_GET_C_BACK_PTR(event)); - // Legacy - q->getChatRoom()->getPrivate()->notifyChatMessageReceived(q->getSharedFromThis()); + + LinphoneChatRoomCbsShouldChatMessageBeStoredCb shouldMessageBeStoredCb = linphone_chat_room_cbs_get_chat_message_should_be_stored(cbs); + if (shouldMessageBeStoredCb) + shouldMessageBeStoredCb(chatRoom, L_GET_C_BACK_PTR(q->getSharedFromThis())); if (toBeStored) storeInDb(); + shared_ptr event = make_shared( + ::time(nullptr), q->getSharedFromThis() + ); + LinphoneChatRoomCbsChatMessageReceivedCb messageReceivedCb = linphone_chat_room_cbs_get_chat_message_received(cbs); + if (messageReceivedCb) + messageReceivedCb(chatRoom, L_GET_C_BACK_PTR(event)); + // Legacy + q->getChatRoom()->getPrivate()->notifyChatMessageReceived(q->getSharedFromThis()); + q->sendDeliveryNotification(LinphoneReasonNone); } @@ -665,7 +711,7 @@ void ChatMessagePrivate::send () { } else { msgOp->send_message(ContentType::PlainText.asString().c_str(), internalContent.getBodyAsUtf8String().c_str()); } - + // Restore FileContents and remove FileTransferContents list::iterator it = contents.begin(); while (it != contents.end()) { @@ -712,7 +758,7 @@ void ChatMessagePrivate::storeInDb () { updateInDb(); } else { shared_ptr eventLog = make_shared(time, q->getSharedFromThis()); - q->getChatRoom()->getCore()->getPrivate()->mainDb->addEvent(eventLog); + q->getChatRoom()->getPrivate()->addEvent(eventLog); if (direction == ChatMessage::Direction::Incoming) { if (hasFileTransferContent()) { @@ -751,6 +797,24 @@ void ChatMessagePrivate::updateInDb () { // ----------------------------------------------------------------------------- +bool ChatMessagePrivate::validStateTransition (ChatMessage::State currentState, ChatMessage::State newState) { + if (newState == currentState) + return false; + + if ( + (currentState == ChatMessage::State::Displayed || currentState == ChatMessage::State::DeliveredToUser) && + ( + newState == ChatMessage::State::DeliveredToUser || + newState == ChatMessage::State::Delivered || + newState == ChatMessage::State::NotDelivered + ) + ) + return false; + return true; +} + +// ----------------------------------------------------------------------------- + ChatMessage::ChatMessage (const shared_ptr &chatRoom, ChatMessage::Direction direction) : Object(*new ChatMessagePrivate), CoreAccessor(chatRoom->getCore()) { L_D(); @@ -926,12 +990,6 @@ void ChatMessage::removeCustomHeader (const string &headerName) { d->customHeaders.erase(headerName); } -void ChatMessage::updateState (State state) { - L_D(); - - d->setState(state); -} - void ChatMessage::send () { L_D(); @@ -981,48 +1039,50 @@ void ChatMessage::cancelFileTransfer () { int ChatMessage::putCharacter (uint32_t character) { L_D(); - shared_ptr core = getCore(); - if (linphone_core_realtime_text_enabled(core->getCCore())) { - static const uint32_t new_line = 0x2028; - static const uint32_t crlf = 0x0D0A; - static const uint32_t lf = 0x0A; + static const uint32_t new_line = 0x2028; + static const uint32_t crlf = 0x0D0A; + static const uint32_t lf = 0x0A; - shared_ptr chatRoom = getChatRoom(); + shared_ptr chatRoom = getChatRoom(); + if (!(chatRoom->getCapabilities() & LinphonePrivate::ChatRoom::Capabilities::RealTimeText)) + return -1; - shared_ptr rttcr = - static_pointer_cast(chatRoom); - LinphoneCall *call = rttcr->getCall(); + shared_ptr rttcr = + static_pointer_cast(chatRoom); + if (!rttcr) + return -1; - if (!call || !linphone_call_get_stream(call, LinphoneStreamTypeText)) - return -1; + shared_ptr call = rttcr->getCall(); + if (!call || !call->getPrivate()->getMediaStream(LinphoneStreamTypeText)) + return -1; - if (character == new_line || character == crlf || character == lf) { - if (lp_config_get_int(core->getCCore()->config, "misc", "store_rtt_messages", 1) == 1) { - // TODO: History. - lDebug() << "New line sent, forge a message with content " << d->rttMessage.c_str(); - d->setTime(ms_time(0)); - d->state = State::Displayed; - // d->direction = Direction::Outgoing; - // setFromAddress(Address( - // linphone_address_as_string(linphone_address_new(linphone_core_get_identity(core->getCCore()))) - // )); - // linphone_chat_message_store(L_GET_C_BACK_PTR(this)); - d->rttMessage = ""; - } - } else { - char *value = LinphonePrivate::Utils::utf8ToChar(character); - d->rttMessage = d->rttMessage + string(value); - lDebug() << "Sent RTT character: " << value << "(" << (unsigned long)character << - "), pending text is " << d->rttMessage.c_str(); - delete value; + if (character == new_line || character == crlf || character == lf) { + shared_ptr core = getCore(); + if (lp_config_get_int(core->getCCore()->config, "misc", "store_rtt_messages", 1) == 1) { + // TODO: History. + lDebug() << "New line sent, forge a message with content " << d->rttMessage.c_str(); + d->setTime(ms_time(0)); + d->state = State::Displayed; + // d->direction = Direction::Outgoing; + // setFromAddress(Address( + // linphone_address_as_string(linphone_address_new(linphone_core_get_identity(core->getCCore()))) + // )); + // linphone_chat_message_store(L_GET_C_BACK_PTR(this)); + d->rttMessage = ""; } - - text_stream_putchar32(reinterpret_cast( - linphone_call_get_stream(call, LinphoneStreamTypeText)), character - ); - return 0; + } else { + char *value = LinphonePrivate::Utils::utf8ToChar(character); + d->rttMessage = d->rttMessage + string(value); + lDebug() << "Sent RTT character: " << value << "(" << (unsigned long)character << + "), pending text is " << d->rttMessage.c_str(); + delete[] value; } - return -1; + + text_stream_putchar32( + reinterpret_cast(call->getPrivate()->getMediaStream(LinphoneStreamTypeText)), + character + ); + return 0; } LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-message/chat-message.h b/src/chat/chat-message/chat-message.h index af7623b77..1108bcb70 100644 --- a/src/chat/chat-message/chat-message.h +++ b/src/chat/chat-message/chat-message.h @@ -47,6 +47,7 @@ class LINPHONE_PUBLIC ChatMessage : public Object, public CoreAccessor { friend class ChatRoomPrivate; friend class CpimChatMessageModifier; friend class FileTransferChatMessageModifier; + friend class Imdn; friend class MainDb; friend class MainDbPrivate; friend class RealTimeTextChatRoomPrivate; @@ -63,7 +64,6 @@ public: // ----- TODO: Remove me. void cancelFileTransfer (); int putCharacter (uint32_t character); - void updateState (State state); void sendDeliveryNotification (LinphoneReason reason); void sendDisplayNotification (); void setIsSecured (bool isSecured); diff --git a/src/chat/chat-room/abstract-chat-room-p.h b/src/chat/chat-room/abstract-chat-room-p.h index 5577122ec..bd2ea703e 100644 --- a/src/chat/chat-room/abstract-chat-room-p.h +++ b/src/chat/chat-room/abstract-chat-room-p.h @@ -42,6 +42,8 @@ public: virtual void sendChatMessage (const std::shared_ptr &chatMessage) = 0; + virtual void addEvent (const std::shared_ptr &eventLog) = 0; + virtual void addTransientEvent (const std::shared_ptr &eventLog) = 0; virtual void removeTransientEvent (const std::shared_ptr &eventLog) = 0; diff --git a/src/chat/chat-room/abstract-chat-room.h b/src/chat/chat-room/abstract-chat-room.h index 46f5aec29..9662f89b9 100644 --- a/src/chat/chat-room/abstract-chat-room.h +++ b/src/chat/chat-room/abstract-chat-room.h @@ -68,6 +68,8 @@ public: virtual State getState () const = 0; virtual bool hasBeenLeft () const = 0; + virtual std::list> getMessageHistory (int nLast) const = 0; + virtual std::list> getMessageHistoryRange (int begin, int end) const = 0; virtual std::list> getHistory (int nLast) const = 0; virtual std::list> getHistoryRange (int begin, int end) const = 0; virtual int getHistorySize () const = 0; diff --git a/src/chat/chat-room/basic-to-client-group-chat-room.cpp b/src/chat/chat-room/basic-to-client-group-chat-room.cpp index 827d9b3d9..e75c56337 100644 --- a/src/chat/chat-room/basic-to-client-group-chat-room.cpp +++ b/src/chat/chat-room/basic-to-client-group-chat-room.cpp @@ -77,7 +77,7 @@ public: } void onCallSessionStateChanged ( - const shared_ptr &session, + const shared_ptr &session, CallSession::State newState, const string &message ) override { diff --git a/src/chat/chat-room/chat-room-id.cpp b/src/chat/chat-room/chat-room-id.cpp index 5ffe44697..8ff0b8d35 100644 --- a/src/chat/chat-room/chat-room-id.cpp +++ b/src/chat/chat-room/chat-room-id.cpp @@ -61,7 +61,8 @@ bool ChatRoomId::operator!= (const ChatRoomId &chatRoomId) const { bool ChatRoomId::operator< (const ChatRoomId &chatRoomId) const { L_D(); const ChatRoomIdPrivate *dChatRoomId = chatRoomId.getPrivate(); - return d->peerAddress < dChatRoomId->peerAddress || d->localAddress < dChatRoomId->localAddress; + return d->peerAddress < dChatRoomId->peerAddress + || (d->peerAddress == dChatRoomId->peerAddress && d->localAddress < dChatRoomId->localAddress); } const IdentityAddress &ChatRoomId::getPeerAddress () const { diff --git a/src/chat/chat-room/chat-room-p.h b/src/chat/chat-room/chat-room-p.h index dcb22bb92..2deeec842 100644 --- a/src/chat/chat-room/chat-room-p.h +++ b/src/chat/chat-room/chat-room-p.h @@ -46,6 +46,8 @@ public: void sendChatMessage (const std::shared_ptr &chatMessage) override; void sendIsComposingNotification (); + void addEvent (const std::shared_ptr &eventLog) override; + void addTransientEvent (const std::shared_ptr &eventLog) override; void removeTransientEvent (const std::shared_ptr &eventLog) override; @@ -59,7 +61,7 @@ public: LinphoneReason onSipMessageReceived (SalOp *op, const SalMessage *message) override; void onChatMessageReceived (const std::shared_ptr &chatMessage) override; - void onImdnReceived (const std::string &text); + void onImdnReceived (const std::shared_ptr &chatMessage); void onIsComposingReceived (const Address &remoteAddress, const std::string &text); void onIsComposingRefreshNeeded () override; void onIsComposingStateChanged (bool isComposing) override; @@ -76,7 +78,6 @@ private: time_t creationTime = std::time(nullptr); time_t lastUpdateTime = std::time(nullptr); - std::shared_ptr pendingMessage; 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 14e92cf85..3d2ea95a4 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -90,6 +90,13 @@ void ChatRoomPrivate::sendIsComposingNotification () { // ----------------------------------------------------------------------------- +void ChatRoomPrivate::addEvent (const shared_ptr &eventLog) { + L_Q(); + + q->getCore()->getPrivate()->mainDb->addEvent(eventLog); + setLastUpdateTime(eventLog->getCreationTime()); +} + void ChatRoomPrivate::addTransientEvent (const shared_ptr &eventLog) { auto it = find(transientEvents.begin(), transientEvents.end(), eventLog); if (it == transientEvents.end()) @@ -182,7 +189,6 @@ void ChatRoomPrivate::notifyUndecryptableChatMessageReceived (const shared_ptr msg; @@ -217,29 +223,18 @@ LinphoneReason ChatRoomPrivate::onSipMessageReceived (SalOp *op, const SalMessag if (msg->getPrivate()->getContentType() == ContentType::ImIsComposing) { onIsComposingReceived(msg->getFromAddress(), msg->getPrivate()->getText()); - increaseMsgCount = FALSE; if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) { goto end; } } else if (msg->getPrivate()->getContentType() == ContentType::Imdn) { - onImdnReceived(msg->getPrivate()->getText()); - increaseMsgCount = FALSE; + onImdnReceived(msg); if (lp_config_get_int(linphone_core_get_config(cCore), "sip", "deliver_imdn", 0) != 1) { goto end; } } - if (increaseMsgCount) { - /* Mark the message as pending so that if ChatRoom::markAsRead() is called in the - * ChatRoomPrivate::chatMessageReceived() callback, it will effectively be marked as - * being read before being stored. */ - pendingMessage = msg; - } - onChatMessageReceived(msg); - pendingMessage = nullptr; - end: return reason; } @@ -251,9 +246,8 @@ void ChatRoomPrivate::onChatMessageReceived (const shared_ptr &chat chatMessage->getPrivate()->notifyReceiving(); } -void ChatRoomPrivate::onImdnReceived (const string &text) { - L_Q(); - Imdn::parse(*q, text); +void ChatRoomPrivate::onImdnReceived (const shared_ptr &chatMessage) { + Imdn::parse(chatMessage); } void ChatRoomPrivate::onIsComposingReceived (const Address &remoteAddress, const string &text) { @@ -321,12 +315,20 @@ ChatRoom::State ChatRoom::getState () const { // ----------------------------------------------------------------------------- +list> ChatRoom::getMessageHistory (int nLast) const { + return getCore()->getPrivate()->mainDb->getHistory(getChatRoomId(), nLast, MainDb::Filter::ConferenceChatMessageFilter); +} + +list> ChatRoom::getMessageHistoryRange (int begin, int end) const { + return getCore()->getPrivate()->mainDb->getHistoryRange(getChatRoomId(), begin, end, MainDb::Filter::ConferenceChatMessageFilter); +} + list> ChatRoom::getHistory (int nLast) const { - return getCore()->getPrivate()->mainDb->getHistory(getChatRoomId(), nLast); + return getCore()->getPrivate()->mainDb->getHistory(getChatRoomId(), nLast, MainDb::Filter::ConferenceInfoNoDeviceFilter); } list> ChatRoom::getHistoryRange (int begin, int end) const { - return getCore()->getPrivate()->mainDb->getHistoryRange(getChatRoomId(), begin, end); + return getCore()->getPrivate()->mainDb->getHistoryRange(getChatRoomId(), begin, end, MainDb::Filter::ConferenceInfoNoDeviceFilter); } int ChatRoom::getHistorySize () const { @@ -423,11 +425,6 @@ void ChatRoom::markAsRead () { chatMessage->sendDisplayNotification(); dCore->mainDb->markChatMessagesAsRead(d->chatRoomId); - - if (d->pendingMessage) { - d->pendingMessage->updateState(ChatMessage::State::Displayed); - d->pendingMessage->sendDisplayNotification(); - } } LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-room/chat-room.h b/src/chat/chat-room/chat-room.h index 35d887e59..e5a270a97 100644 --- a/src/chat/chat-room/chat-room.h +++ b/src/chat/chat-room/chat-room.h @@ -42,6 +42,8 @@ public: State getState () const override; + std::list> getMessageHistory (int nLast) const override; + std::list> getMessageHistoryRange (int begin, int end) const override; std::list> getHistory (int nLast) const override; std::list> getHistoryRange (int begin, int end) const override; int getHistorySize () 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 fa51cf8a3..f75a5abac 100644 --- a/src/chat/chat-room/client-group-chat-room-p.h +++ b/src/chat/chat-room/client-group-chat-room-p.h @@ -46,8 +46,8 @@ public: void onChatRoomDeleteRequested (const std::shared_ptr &chatRoom) override; // CallSessionListener - void onCallSessionSetReleased (const std::shared_ptr &session) override; - void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) override; + void onCallSessionSetReleased (const std::shared_ptr &session) override; + void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) override; private: CallSessionListener *callSessionListener = this; diff --git a/src/chat/chat-room/client-group-chat-room.cpp b/src/chat/chat-room/client-group-chat-room.cpp index a3e2847b4..89240f441 100644 --- a/src/chat/chat-room/client-group-chat-room.cpp +++ b/src/chat/chat-room/client-group-chat-room.cpp @@ -20,16 +20,14 @@ #include "linphone/utils/utils.h" #include "address/address-p.h" -#include "c-wrapper/c-wrapper.h" -#include "basic-chat-room.h" #include "basic-to-client-group-chat-room.h" +#include "c-wrapper/c-wrapper.h" #include "client-group-chat-room-p.h" #include "conference/handlers/remote-conference-event-handler.h" #include "conference/participant-p.h" #include "conference/remote-conference-p.h" #include "conference/session/call-session-p.h" #include "core/core-p.h" -#include "event-log/events.h" #include "logger/logger.h" #include "sal/refer-op.h" @@ -65,12 +63,11 @@ shared_ptr ClientGroupChatRoomPrivate::createSession () { 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); - const Address &myAddress = q->getMe()->getAddress(); - Address myCleanedAddress(myAddress); - myCleanedAddress.removeUriParam("gr"); // Remove gr parameter for INVITE - session->configure(LinphoneCallOutgoing, nullptr, nullptr, myCleanedAddress, focus->getPrivate()->getDevices().front()->getAddress()); + ParticipantPrivate *dFocus = qConference->getPrivate()->focus->getPrivate(); + shared_ptr session = dFocus->createSession(*q, &csp, false, callSessionListener); + Address myCleanedAddress(q->getMe()->getAddress()); + myCleanedAddress.removeUriParam("gr"); // Remove gr parameter for INVITE. + session->configure(LinphoneCallOutgoing, nullptr, nullptr, myCleanedAddress, dFocus->getDevices().front()->getAddress()); session->initiateOutgoing(); session->getPrivate()->createOp(); return session; @@ -122,7 +119,7 @@ void ClientGroupChatRoomPrivate::onChatRoomDeleteRequested (const shared_ptr &session) { +void ClientGroupChatRoomPrivate::onCallSessionSetReleased (const shared_ptr &session) { L_Q_T(RemoteConference, qConference); ParticipantPrivate *participantPrivate = qConference->getPrivate()->focus->getPrivate(); @@ -131,7 +128,7 @@ void ClientGroupChatRoomPrivate::onCallSessionSetReleased (const shared_ptr &session, + const shared_ptr &session, CallSession::State newState, const string &message ) { @@ -152,9 +149,14 @@ void ClientGroupChatRoomPrivate::onCallSessionStateChanged ( if (q->getState() == ChatRoom::State::CreationPending) setState(ChatRoom::State::CreationFailed); else if (q->getState() == ChatRoom::State::TerminationPending) { - // Go to state TerminationFailed and then back to Created since it has not been terminated - setState(ChatRoom::State::TerminationFailed); - setState(ChatRoom::State::Created); + if (session->getReason() == LinphoneReasonNotFound) { + // Somehow the chat room is no longer know on the server, so terminate it + q->onConferenceTerminated(q->getConferenceAddress()); + } else { + // Go to state TerminationFailed and then back to Created since it has not been terminated + setState(ChatRoom::State::TerminationFailed); + setState(ChatRoom::State::Created); + } } } } @@ -206,6 +208,11 @@ RemoteConference(core, me->getAddress(), nullptr) { dConference->eventHandler->subscribe(getChatRoomId()); } +ClientGroupChatRoom::~ClientGroupChatRoom () { + L_D(); + d->setCallSessionListener(nullptr); +} + shared_ptr ClientGroupChatRoom::getCore () const { return ChatRoom::getCore(); } @@ -396,7 +403,10 @@ void ClientGroupChatRoom::join () { if (!session && ((getState() == ChatRoom::State::Instantiated) || (getState() == ChatRoom::State::Terminated))) { d->bgTask.start(); session = d->createSession(); - session->startInvite(nullptr, "", nullptr); + } + if (session) { + if (getState() != ChatRoom::State::TerminationPending) + session->startInvite(nullptr, "", nullptr); d->setState(ChatRoom::State::CreationPending); } } @@ -442,7 +452,7 @@ void ClientGroupChatRoom::onConferenceTerminated (const IdentityAddress &addr) { L_D_T(RemoteConference, dConference); dConference->eventHandler->resetLastNotify(); d->setState(ChatRoom::State::Terminated); - getCore()->getPrivate()->mainDb->addEvent(make_shared( + d->addEvent(make_shared( EventLog::Type::ConferenceTerminated, time(nullptr), d->chatRoomId @@ -471,7 +481,7 @@ void ClientGroupChatRoom::onFirstNotifyReceived (const IdentityAddress &addr) { // TODO: Bug. Event is inserted many times. // Avoid this in the future. Deal with signals/slots system. #if 0 - getCore()->getPrivate()->mainDb->addEvent(make_shared( + d->addEvent(make_shared( EventLog::Type::ConferenceCreated, time(nullptr), d->chatRoomId @@ -481,6 +491,7 @@ void ClientGroupChatRoom::onFirstNotifyReceived (const IdentityAddress &addr) { } void ClientGroupChatRoom::onParticipantAdded (const shared_ptr &event, bool isFullState) { + L_D(); L_D_T(RemoteConference, dConference); const IdentityAddress &addr = event->getParticipantAddress(); @@ -499,7 +510,7 @@ void ClientGroupChatRoom::onParticipantAdded (const shared_ptrgetPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); @@ -511,6 +522,7 @@ void ClientGroupChatRoom::onParticipantAdded (const shared_ptr &event, bool isFullState) { (void)isFullState; + L_D(); L_D_T(RemoteConference, dConference); const IdentityAddress &addr = event->getParticipantAddress(); @@ -521,7 +533,7 @@ void ClientGroupChatRoom::onParticipantRemoved (const shared_ptrparticipants.remove(participant); - getCore()->getPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); @@ -531,6 +543,8 @@ void ClientGroupChatRoom::onParticipantRemoved (const shared_ptr &event, bool isFullState) { + L_D(); + const IdentityAddress &addr = event->getParticipantAddress(); shared_ptr participant; if (isMe(addr)) @@ -550,7 +564,7 @@ void ClientGroupChatRoom::onParticipantSetAdmin (const shared_ptrgetPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); @@ -560,6 +574,8 @@ void ClientGroupChatRoom::onParticipantSetAdmin (const shared_ptr &event, bool isFullState) { + L_D(); + if (getSubject() == event->getSubject()) return; // No change in the local subject, do not notify RemoteConference::setSubject(event->getSubject()); @@ -567,7 +583,7 @@ void ClientGroupChatRoom::onSubjectChanged (const shared_ptrgetPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); @@ -577,6 +593,8 @@ void ClientGroupChatRoom::onSubjectChanged (const shared_ptr &event, bool isFullState) { + L_D(); + const IdentityAddress &addr = event->getParticipantAddress(); shared_ptr participant; if (isMe(addr)) @@ -592,7 +610,7 @@ void ClientGroupChatRoom::onParticipantDeviceAdded (const shared_ptrgetPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); @@ -602,6 +620,8 @@ void ClientGroupChatRoom::onParticipantDeviceAdded (const shared_ptr &event, bool isFullState) { + L_D(); + (void)isFullState; const IdentityAddress &addr = event->getParticipantAddress(); @@ -615,7 +635,7 @@ void ClientGroupChatRoom::onParticipantDeviceRemoved (const shared_ptrgetPrivate()->removeDevice(event->getDeviceAddress()); - getCore()->getPrivate()->mainDb->addEvent(event); + d->addEvent(event); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(this); LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr); diff --git a/src/chat/chat-room/client-group-chat-room.h b/src/chat/chat-room/client-group-chat-room.h index dc518e6e3..3569dd2d2 100644 --- a/src/chat/chat-room/client-group-chat-room.h +++ b/src/chat/chat-room/client-group-chat-room.h @@ -55,8 +55,10 @@ public: unsigned int lastNotifyId ); + ~ClientGroupChatRoom (); + std::shared_ptr getCore () const; - + void allowCpim (bool value) override; void allowMultipart (bool value) override; bool canHandleCpim () const override; @@ -100,11 +102,11 @@ private: void onConferenceTerminated (const IdentityAddress &addr) override; void onFirstNotifyReceived (const IdentityAddress &addr) override; void onParticipantAdded (const std::shared_ptr &event, bool isFullState) override; + void onParticipantDeviceAdded (const std::shared_ptr &event, bool isFullState) override; + void onParticipantDeviceRemoved (const std::shared_ptr &event, bool isFullState) override; void onParticipantRemoved (const std::shared_ptr &event, bool isFullState) override; void onParticipantSetAdmin (const std::shared_ptr &event, bool isFullState) override; void onSubjectChanged (const std::shared_ptr &event, bool isFullState) override; - void onParticipantDeviceAdded (const std::shared_ptr &event, bool isFullState) override; - void onParticipantDeviceRemoved (const std::shared_ptr &event, bool isFullState) override; L_DECLARE_PRIVATE(ClientGroupChatRoom); L_DISABLE_COPY(ClientGroupChatRoom); 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 e5c31bb8c..7ca4ab61f 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 @@ -52,14 +52,14 @@ public: setState(AbstractChatRoom::State::Deleted); } - void onCallSessionSetReleased (const std::shared_ptr &session) override { + void onCallSessionSetReleased (const shared_ptr &session) override { if (!(chatRoom->getCapabilities() & ChatRoom::Capabilities::Conference)) return; static_pointer_cast(chatRoom)->getPrivate()->onCallSessionSetReleased(session); } void onCallSessionStateChanged ( - const shared_ptr &session, + const shared_ptr &session, CallSession::State newState, const string &message ) override { @@ -111,7 +111,7 @@ void ClientGroupToBasicChatRoom::addParticipant ( ProxyChatRoom::addParticipant(participantAddress, params, hasMedia); } void ClientGroupToBasicChatRoom::addParticipants ( - const std::list &addresses, + const list &addresses, const CallSessionParams *params, bool hasMedia ) { diff --git a/src/chat/chat-room/proxy-chat-room-p.h b/src/chat/chat-room/proxy-chat-room-p.h index 9559c31c4..89fb81e21 100644 --- a/src/chat/chat-room/proxy-chat-room-p.h +++ b/src/chat/chat-room/proxy-chat-room-p.h @@ -21,6 +21,7 @@ #define _L_PROXY_CHAT_ROOM_P_H_ #include "abstract-chat-room-p.h" +#include "proxy-chat-room.h" // ============================================================================= @@ -44,6 +45,10 @@ public: chatRoom->getPrivate()->sendChatMessage(chatMessage); } + inline void addEvent (const std::shared_ptr &eventLog) override { + chatRoom->getPrivate()->addEvent(eventLog); + } + inline void addTransientEvent (const std::shared_ptr &eventLog) override { chatRoom->getPrivate()->addTransientEvent(eventLog); } diff --git a/src/chat/chat-room/proxy-chat-room.cpp b/src/chat/chat-room/proxy-chat-room.cpp index bf984d2a1..547317551 100644 --- a/src/chat/chat-room/proxy-chat-room.cpp +++ b/src/chat/chat-room/proxy-chat-room.cpp @@ -195,6 +195,16 @@ bool ProxyChatRoom::hasBeenLeft () const { // ----------------------------------------------------------------------------- +list> ProxyChatRoom::getMessageHistory (int nLast) const { + L_D(); + return d->chatRoom->getMessageHistory(nLast); +} + +list> ProxyChatRoom::getMessageHistoryRange (int begin, int end) const { + L_D(); + return d->chatRoom->getMessageHistoryRange(begin, end); +} + list> ProxyChatRoom::getHistory (int nLast) const { L_D(); return d->chatRoom->getHistory(nLast); diff --git a/src/chat/chat-room/proxy-chat-room.h b/src/chat/chat-room/proxy-chat-room.h index 16a5c49aa..4f162e8ad 100644 --- a/src/chat/chat-room/proxy-chat-room.h +++ b/src/chat/chat-room/proxy-chat-room.h @@ -43,6 +43,8 @@ public: State getState () const override; bool hasBeenLeft () const override; + std::list> getMessageHistory (int nLast) const override; + std::list> getMessageHistoryRange (int begin, int end) const override; std::list> getHistory (int nLast) const override; std::list> getHistoryRange (int begin, int end) const override; int getHistorySize () const override; diff --git a/src/chat/chat-room/real-time-text-chat-room-p.h b/src/chat/chat-room/real-time-text-chat-room-p.h index c8e5e2b62..ae9af9de3 100644 --- a/src/chat/chat-room/real-time-text-chat-room-p.h +++ b/src/chat/chat-room/real-time-text-chat-room-p.h @@ -29,13 +29,19 @@ LINPHONE_BEGIN_NAMESPACE class RealTimeTextChatRoomPrivate : public BasicChatRoomPrivate { public: + struct Character { + uint32_t value; + bool hasBeenRead; + }; + RealTimeTextChatRoomPrivate () = default; - void realtimeTextReceived (uint32_t character, LinphoneCall *call); + void realtimeTextReceived (uint32_t character, const std::shared_ptr &call); void sendChatMessage (const std::shared_ptr &chatMessage) override; + void setCall (const std::shared_ptr &value) { call = value; } - LinphoneCall *call = nullptr; - std::list receivedRttCharacters; + std::weak_ptr call; + std::list receivedRttCharacters; std::shared_ptr pendingMessage = nullptr; private: diff --git a/src/chat/chat-room/real-time-text-chat-room.cpp b/src/chat/chat-room/real-time-text-chat-room.cpp index f12e120b3..f33162682 100644 --- a/src/chat/chat-room/real-time-text-chat-room.cpp +++ b/src/chat/chat-room/real-time-text-chat-room.cpp @@ -18,6 +18,7 @@ */ #include "c-wrapper/c-wrapper.h" +#include "call/call.h" #include "chat/chat-message/chat-message-p.h" #include "conference/participant.h" #include "core/core.h" @@ -32,7 +33,7 @@ LINPHONE_BEGIN_NAMESPACE // ----------------------------------------------------------------------------- -void RealTimeTextChatRoomPrivate::realtimeTextReceived (uint32_t character, LinphoneCall *call) { +void RealTimeTextChatRoomPrivate::realtimeTextReceived (uint32_t character, const shared_ptr &call) { L_Q(); const uint32_t new_line = 0x2028; const uint32_t crlf = 0x0D0A; @@ -41,56 +42,48 @@ void RealTimeTextChatRoomPrivate::realtimeTextReceived (uint32_t character, Linp shared_ptr core = q->getCore(); LinphoneCore *cCore = core->getCCore(); - if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { - LinphoneChatMessageCharacter *cmc = bctbx_new0(LinphoneChatMessageCharacter, 1); - + if (call && call->getCurrentParams()->realtimeTextEnabled()) { if (!pendingMessage) pendingMessage = q->createChatMessage(""); - cmc->value = character; - cmc->has_been_read = FALSE; + Character cmc; + cmc.value = character; + cmc.hasBeenRead = false; receivedRttCharacters.push_back(cmc); remoteIsComposing.push_back(q->getPeerAddress()); linphone_core_notify_is_composing_received(cCore, L_GET_C_BACK_PTR(q)); if ((character == new_line) || (character == crlf) || (character == lf)) { - /* End of message */ + // End of message lDebug() << "New line received, forge a message with content " << pendingMessage->getPrivate()->getText().c_str(); - // TODO: REPAIR ME. - // pendingMessage->setFromAddress(peerAddress); - // pendingMessage->setToAddress( - // Address( - // linphone_call_get_dest_proxy(call) - // ? linphone_address_as_string(linphone_call_get_dest_proxy(call)->identity_address) - // : linphone_core_get_identity(cCore) - // ) - // ); pendingMessage->getPrivate()->setState(ChatMessage::State::Delivered); pendingMessage->getPrivate()->setDirection(ChatMessage::Direction::Incoming); + pendingMessage->getPrivate()->setTime(::ms_time(0)); if (lp_config_get_int(linphone_core_get_config(cCore), "misc", "store_rtt_messages", 1) == 1) pendingMessage->getPrivate()->storeInDb(); onChatMessageReceived(pendingMessage); pendingMessage = nullptr; - for (auto &rttChars : receivedRttCharacters) - ms_free(rttChars); receivedRttCharacters.clear(); } else { char *value = Utils::utf8ToChar(character); - char *text = (char *)pendingMessage->getPrivate()->getText().c_str(); - pendingMessage->getPrivate()->setText(ms_strcat_printf(text, value)); + string text(pendingMessage->getPrivate()->getText()); + text += string(value); + pendingMessage->getPrivate()->setText(text); lDebug() << "Received RTT character: " << value << " (" << character << "), pending text is " << pendingMessage->getPrivate()->getText(); - delete value; + delete[] value; } } } void RealTimeTextChatRoomPrivate::sendChatMessage (const shared_ptr &chatMessage) { - if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { - uint32_t new_line = 0x2028; - chatMessage->putCharacter(new_line); + L_Q(); + shared_ptr call = q->getCall(); + if (call && call->getCurrentParams()->realtimeTextEnabled()) { + uint32_t newLine = 0x2028; + chatMessage->putCharacter(newLine); } } @@ -99,26 +92,16 @@ void RealTimeTextChatRoomPrivate::sendChatMessage (const shared_ptr RealTimeTextChatRoom::RealTimeTextChatRoom (const shared_ptr &core, const ChatRoomId &chatRoomId) : BasicChatRoom(*new RealTimeTextChatRoomPrivate, core, chatRoomId) {} -RealTimeTextChatRoom::~RealTimeTextChatRoom () { - L_D(); - - if (!d->receivedRttCharacters.empty()) - for (auto &rttChars : d->receivedRttCharacters) - bctbx_free(rttChars); -} - RealTimeTextChatRoom::CapabilitiesMask RealTimeTextChatRoom::getCapabilities () const { return BasicChatRoom::getCapabilities() | Capabilities::RealTimeText; } uint32_t RealTimeTextChatRoom::getChar () const { L_D(); - if (!d->receivedRttCharacters.empty()) { - for (auto &cmc : d->receivedRttCharacters) { - if (!cmc->has_been_read) { - cmc->has_been_read = TRUE; - return cmc->value; - } + for (const auto &cmc : d->receivedRttCharacters) { + if (!cmc.hasBeenRead) { + const_cast(&cmc)->hasBeenRead = true; + return cmc.value; } } return 0; @@ -126,9 +109,9 @@ uint32_t RealTimeTextChatRoom::getChar () const { // ----------------------------------------------------------------------------- -LinphoneCall *RealTimeTextChatRoom::getCall () const { +shared_ptr RealTimeTextChatRoom::getCall () const { L_D(); - return d->call; + return d->call.lock(); } LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-room/real-time-text-chat-room.h b/src/chat/chat-room/real-time-text-chat-room.h index 7206bf3bc..4f0d8cb7f 100644 --- a/src/chat/chat-room/real-time-text-chat-room.h +++ b/src/chat/chat-room/real-time-text-chat-room.h @@ -26,18 +26,20 @@ LINPHONE_BEGIN_NAMESPACE +class Call; class RealTimeTextChatRoomPrivate; class LINPHONE_PUBLIC RealTimeTextChatRoom : public BasicChatRoom { + friend class CallPrivate; friend class CorePrivate; public: - ~RealTimeTextChatRoom (); + ~RealTimeTextChatRoom () = default; CapabilitiesMask getCapabilities () const override; uint32_t getChar () const; - LinphoneCall *getCall () const; + std::shared_ptr getCall () const; private: RealTimeTextChatRoom (const std::shared_ptr &core, const ChatRoomId &chatRoomId); 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 6b9ef0da0..bfb58f200 100644 --- a/src/chat/chat-room/server-group-chat-room-p.h +++ b/src/chat/chat-room/server-group-chat-room-p.h @@ -26,6 +26,7 @@ #include "chat-room-p.h" #include "server-group-chat-room.h" +#include "conference/participant-device.h" // ============================================================================= @@ -41,6 +42,10 @@ public: std::shared_ptr findFilteredParticipant (const std::shared_ptr &session) const; std::shared_ptr findFilteredParticipant (const IdentityAddress &participantAddress) const; + ParticipantDevice::State getParticipantDeviceState (const std::shared_ptr &device) const; + void setParticipantDeviceState (const std::shared_ptr &device, ParticipantDevice::State state); + + void acceptSession (const std::shared_ptr &session); void confirmCreation (); void confirmJoining (SalCallOp *op); void confirmRecreation (SalCallOp *op); @@ -79,6 +84,7 @@ private: bool isAdminLeft () const; void queueMessage (const std::shared_ptr &message); void queueMessage (const std::shared_ptr &msg, const IdentityAddress &deviceAddress); + void removeNonPresentParticipants (const std::list &compatibleParticipants); void onParticipantDeviceLeft (const std::shared_ptr &session); @@ -89,7 +95,7 @@ private: // CallSessionListener void onCallSessionStateChanged ( - const std::shared_ptr &session, + const std::shared_ptr &session, CallSession::State newState, const std::string &message ) override; 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 0e59145ab..90950be2f 100644 --- a/src/chat/chat-room/server-group-chat-room-stub.cpp +++ b/src/chat/chat-room/server-group-chat-room-stub.cpp @@ -42,8 +42,18 @@ shared_ptr ServerGroupChatRoomPrivate::findFilteredParticipant (con return nullptr; } +ParticipantDevice::State ServerGroupChatRoomPrivate::getParticipantDeviceState (const shared_ptr &device) const { + return device->getState(); +} + +void ServerGroupChatRoomPrivate::setParticipantDeviceState (const shared_ptr &device, ParticipantDevice::State state) { + device->setState(state); +} + // ----------------------------------------------------------------------------- +void ServerGroupChatRoomPrivate::acceptSession (const shared_ptr &session) {} + void ServerGroupChatRoomPrivate::confirmCreation () {} void ServerGroupChatRoomPrivate::confirmJoining (SalCallOp *) {} @@ -80,23 +90,25 @@ LinphoneReason ServerGroupChatRoomPrivate::onSipMessageReceived (SalOp *, const void ServerGroupChatRoomPrivate::designateAdmin () {} -void ServerGroupChatRoomPrivate::dispatchMessage (const std::shared_ptr &message, const std::string &uri) {} +void ServerGroupChatRoomPrivate::dispatchMessage (const shared_ptr &message, const string &uri) {} void ServerGroupChatRoomPrivate::finalizeCreation () {} -void ServerGroupChatRoomPrivate::inviteDevice (const std::shared_ptr &device) {} +void ServerGroupChatRoomPrivate::inviteDevice (const shared_ptr &device) {} bool ServerGroupChatRoomPrivate::isAdminLeft () const { return false; } -void ServerGroupChatRoomPrivate::queueMessage (const std::shared_ptr &message) {} +void ServerGroupChatRoomPrivate::queueMessage (const shared_ptr &message) {} -void ServerGroupChatRoomPrivate::queueMessage (const std::shared_ptr &msg, const IdentityAddress &deviceAddress) {} +void ServerGroupChatRoomPrivate::queueMessage (const shared_ptr &msg, const IdentityAddress &deviceAddress) {} + +void ServerGroupChatRoomPrivate::removeNonPresentParticipants (const list &compatibleParticipants) {} // ----------------------------------------------------------------------------- -void ServerGroupChatRoomPrivate::onParticipantDeviceLeft (const std::shared_ptr &session) {} +void ServerGroupChatRoomPrivate::onParticipantDeviceLeft (const shared_ptr &session) {} // ----------------------------------------------------------------------------- @@ -109,7 +121,7 @@ void ServerGroupChatRoomPrivate::onChatRoomDeleteRequested (const shared_ptr &, + const shared_ptr &, CallSession::State, const string & ) {} diff --git a/src/chat/modifier/file-transfer-chat-message-modifier.cpp b/src/chat/modifier/file-transfer-chat-message-modifier.cpp index 951efa582..41ffa66c8 100644 --- a/src/chat/modifier/file-transfer-chat-message-modifier.cpp +++ b/src/chat/modifier/file-transfer-chat-message-modifier.cpp @@ -364,24 +364,24 @@ void FileTransferChatMessageModifier::processResponseFromPostFile (const belle_h message->removeContent(*fileContent); message->addContent(*fileTransferContent); - message->updateState(ChatMessage::State::FileTransferDone); + message->getPrivate()->setState(ChatMessage::State::FileTransferDone); releaseHttpRequest(); message->getPrivate()->send(); fileUploadEndBackgroundTask(); } else { lWarning() << "Received empty response from server, file transfer failed"; - message->updateState(ChatMessage::State::NotDelivered); + message->getPrivate()->setState(ChatMessage::State::NotDelivered); releaseHttpRequest(); fileUploadEndBackgroundTask(); } } else if (code == 400) { lWarning() << "Received HTTP code response " << code << " for file transfer, probably meaning file is too large"; - message->updateState(ChatMessage::State::FileTransferError); + message->getPrivate()->setState(ChatMessage::State::FileTransferError); releaseHttpRequest(); fileUploadEndBackgroundTask(); } else { lWarning() << "Unhandled HTTP code response " << code << " for file transfer"; - message->updateState(ChatMessage::State::NotDelivered); + message->getPrivate()->setState(ChatMessage::State::NotDelivered); releaseHttpRequest(); fileUploadEndBackgroundTask(); } @@ -398,7 +398,7 @@ void FileTransferChatMessageModifier::processIoErrorUpload (const belle_sip_io_e shared_ptr message = chatMessage.lock(); if (!message) return; - message->updateState(ChatMessage::State::NotDelivered); + message->getPrivate()->setState(ChatMessage::State::NotDelivered); releaseHttpRequest(); } @@ -412,7 +412,7 @@ void FileTransferChatMessageModifier::processAuthRequestedUpload (const belle_si shared_ptr message = chatMessage.lock(); if (!message) return; - message->updateState(ChatMessage::State::NotDelivered); + message->getPrivate()->setState(ChatMessage::State::NotDelivered); releaseHttpRequest(); } @@ -844,7 +844,7 @@ void FileTransferChatMessageModifier::processAuthRequestedDownload (const belle_ shared_ptr message = chatMessage.lock(); if (!message) return; - message->updateState(ChatMessage::State::FileTransferError); + message->getPrivate()->setState(ChatMessage::State::FileTransferError); releaseHttpRequest(); } @@ -858,7 +858,7 @@ void FileTransferChatMessageModifier::processIoErrorDownload (const belle_sip_io shared_ptr message = chatMessage.lock(); if (!message) return; - message->updateState(ChatMessage::State::FileTransferError); + message->getPrivate()->setState(ChatMessage::State::FileTransferError); releaseHttpRequest(); } diff --git a/src/chat/modifier/multipart-chat-message-modifier.cpp b/src/chat/modifier/multipart-chat-message-modifier.cpp index 4abf91210..243cb32e1 100644 --- a/src/chat/modifier/multipart-chat-message-modifier.cpp +++ b/src/chat/modifier/multipart-chat-message-modifier.cpp @@ -59,7 +59,7 @@ ChatMessageModifier::Result MultipartChatMessageModifier::encode ( multipartMessage << "--"; Content newContent; - ContentType newContentType("multipart/mixed"); + ContentType newContentType(ContentType::Multipart); newContentType.setParameter("boundary=" + boundary); newContent.setContentType(newContentType); newContent.setBody(multipartMessage.str()); diff --git a/src/chat/notification/imdn.cpp b/src/chat/notification/imdn.cpp index b3412ce12..817ae3a2a 100644 --- a/src/chat/notification/imdn.cpp +++ b/src/chat/notification/imdn.cpp @@ -17,7 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "chat/chat-message/chat-message.h" +#include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/chat-room.h" #include "core/core.h" #include "logger/logger.h" @@ -40,7 +40,8 @@ string Imdn::createXml (const string &id, time_t time, Imdn::Type imdnType, Linp char *datetime = nullptr; // Check that the chat message has a message id. - if (id.empty()) return nullptr; + if (id.empty()) + return content; buf = xmlBufferCreate(); if (buf == nullptr) { @@ -132,18 +133,18 @@ string Imdn::createXml (const string &id, time_t time, Imdn::Type imdnType, Linp return content; } -void Imdn::parse (ChatRoom &cr, const string &text) { +void Imdn::parse (const shared_ptr &chatMessage) { xmlparsing_context_t *xmlCtx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xmlCtx, linphone_xmlparsing_genericxml_error); - xmlCtx->doc = xmlReadDoc((const unsigned char *)text.c_str(), 0, nullptr, 0); + xmlCtx->doc = xmlReadDoc((const unsigned char *)chatMessage->getPrivate()->getText().c_str(), 0, nullptr, 0); if (xmlCtx->doc) - parse(cr, xmlCtx); + parse(chatMessage, xmlCtx); else lWarning() << "Wrongly formatted IMDN XML: " << xmlCtx->errorBuffer; linphone_xmlparsing_context_destroy(xmlCtx); } -void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) { +void Imdn::parse (const shared_ptr &imdnMessage, xmlparsing_context_t *xmlCtx) { char xpathStr[MAX_XPATH_LENGTH]; char *messageIdStr = nullptr; char *datetimeStr = nullptr; @@ -163,11 +164,13 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) { } if (messageIdStr && datetimeStr) { - shared_ptr cm = cr.findChatMessage(messageIdStr, ChatMessage::Direction::Outgoing); + shared_ptr cr = imdnMessage->getChatRoom(); + shared_ptr cm = cr->findChatMessage(messageIdStr, ChatMessage::Direction::Outgoing); + const IdentityAddress &participantAddress = imdnMessage->getFromAddress().getAddressWithoutGruu(); if (!cm) { lWarning() << "Received IMDN for unknown message " << messageIdStr; } else { - LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr.getCore()->getCCore()); + LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore()); snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:delivery-notification/imdn:status", imdnPrefix.c_str()); xmlXPathObjectPtr deliveryStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr); snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:display-notification/imdn:status", imdnPrefix.c_str()); @@ -177,9 +180,9 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) { xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0]; if (node->children && node->children->name) { if (strcmp((const char *)node->children->name, "delivered") == 0) { - cm->updateState(ChatMessage::State::DeliveredToUser); + cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser); } else if (strcmp((const char *)node->children->name, "error") == 0) { - cm->updateState(ChatMessage::State::NotDelivered); + cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered); } } } @@ -190,7 +193,7 @@ void Imdn::parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx) { xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0]; if (node->children && node->children->name) { if (strcmp((const char *)node->children->name, "displayed") == 0) { - cm->updateState(ChatMessage::State::Displayed); + cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed); } } } diff --git a/src/chat/notification/imdn.h b/src/chat/notification/imdn.h index ed51696b0..e6eea4766 100644 --- a/src/chat/notification/imdn.h +++ b/src/chat/notification/imdn.h @@ -38,10 +38,10 @@ public: }; static std::string createXml (const std::string &id, time_t time, Imdn::Type imdnType, LinphoneReason reason); - static void parse (ChatRoom &cr, const std::string &content); + static void parse (const std::shared_ptr &chatMessage); private: - static void parse (ChatRoom &cr, xmlparsing_context_t *xmlCtx); + static void parse (const std::shared_ptr &chatMessage, xmlparsing_context_t *xmlCtx); private: static const std::string imdnPrefix; diff --git a/src/conference/conference.cpp b/src/conference/conference.cpp index 1aa3459e6..efba1bad8 100644 --- a/src/conference/conference.cpp +++ b/src/conference/conference.cpp @@ -61,11 +61,8 @@ void Conference::addParticipants (const list &addresses, const list sortedAddresses(addresses); sortedAddresses.sort(); sortedAddresses.unique(); - for (const auto &addr: sortedAddresses) { - shared_ptr participant = findParticipant(addr); - if (!participant) - addParticipant(addr, params, hasMedia); - } + for (const auto &addr: sortedAddresses) + addParticipant(addr, params, hasMedia); } bool Conference::canHandleParticipants () const { diff --git a/src/conference/handlers/remote-conference-event-handler.cpp b/src/conference/handlers/remote-conference-event-handler.cpp index 6df681ccf..090c81ff6 100644 --- a/src/conference/handlers/remote-conference-event-handler.cpp +++ b/src/conference/handlers/remote-conference-event-handler.cpp @@ -19,6 +19,7 @@ #include +#include "linphone/utils/algorithm.h" #include "linphone/utils/utils.h" #include "conference/remote-conference.h" @@ -45,135 +46,150 @@ using namespace Xsd::ConferenceInfo; void RemoteConferenceEventHandlerPrivate::simpleNotifyReceived (const string &xmlBody) { istringstream data(xmlBody); unique_ptr confInfo = parseConferenceInfo(data, Xsd::XmlSchema::Flags::dont_validate); - time_t tm = time(nullptr); - if (confInfo->getConferenceDescription()->getFreeText().present()) - tm = static_cast(Utils::stoll(confInfo->getConferenceDescription()->getFreeText().get())); - - bool isFullState = (confInfo->getState() == StateType::full); - - ConferenceListener *confListener = static_cast(conf); IdentityAddress entityAddress(confInfo->getEntity().c_str()); - if (entityAddress == chatRoomId.getPeerAddress()) { - if (confInfo->getVersion().present()) - lastNotify = confInfo->getVersion().get(); + if (entityAddress != chatRoomId.getPeerAddress()) + return; - 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); - } - } + auto &confDescription = confInfo->getConferenceDescription(); - if (!confInfo->getUsers().present()) - return; - - for (const auto &user : confInfo->getUsers()->getUser()) { - LinphoneAddress *cAddr = linphone_core_interpret_url(conf->getCore()->getCCore(), user.getEntity()->c_str()); - char *cAddrStr = linphone_address_as_string(cAddr); - Address addr(cAddrStr); - bctbx_free(cAddrStr); - if (user.getState() == StateType::deleted) { - confListener->onParticipantRemoved( - make_shared( - EventLog::Type::ConferenceParticipantRemoved, - tm, - chatRoomId, - lastNotify, - addr - ), - isFullState - ); - } else { - if (user.getState() == StateType::full) { - confListener->onParticipantAdded( - make_shared( - EventLog::Type::ConferenceParticipantAdded, - tm, - chatRoomId, - lastNotify, - addr - ), - isFullState - ); - } - - if (user.getRoles()) { - bool isAdmin = false; - for (const auto &entry : user.getRoles()->getEntry()) { - if (entry == "admin") { - isAdmin = true; - break; - } - } - confListener->onParticipantSetAdmin( - make_shared( - isAdmin ? EventLog::Type::ConferenceParticipantSetAdmin : EventLog::Type::ConferenceParticipantUnsetAdmin, - tm, - chatRoomId, - lastNotify, - addr - ), - isFullState - ); - } - - for (const auto &endpoint : user.getEndpoint()) { - if (!endpoint.getEntity().present()) - break; - - Address gruu(endpoint.getEntity().get()); - if (endpoint.getState() == StateType::deleted) { - confListener->onParticipantDeviceRemoved( - make_shared( - EventLog::Type::ConferenceParticipantDeviceRemoved, - tm, - chatRoomId, - lastNotify, - addr, - gruu - ), - isFullState - ); - } else if (endpoint.getState() == StateType::full) { - confListener->onParticipantDeviceAdded( - make_shared( - EventLog::Type::ConferenceParticipantDeviceAdded, - tm, - chatRoomId, - lastNotify, - addr, - gruu - ), - isFullState - ); - } - } - } - linphone_address_unref(cAddr); - } - - if (isFullState) - confListener->onFirstNotifyReceived(chatRoomId.getPeerAddress()); + // 1. Compute event time. + time_t creationTime = time(nullptr); + { + auto &freeText = confDescription->getFreeText(); + if (freeText.present()) + creationTime = static_cast(Utils::stoll(freeText.get())); } + + // 2. Update last notify. + { + auto &version = confInfo->getVersion(); + if (version.present()) + lastNotify = version.get(); + } + + bool isFullState = confInfo->getState() == StateType::full; + ConferenceListener *confListener = static_cast(conf); + + // 3. Notify subject and keywords. + if (confDescription.present()) { + auto &subject = confDescription.get().getSubject(); + if (subject.present() && !subject.get().empty()) + confListener->onSubjectChanged( + make_shared( + creationTime, + chatRoomId, + lastNotify, + subject.get() + ), + isFullState + ); + + auto &keywords = confDescription.get().getKeywords(); + if (keywords.present() && !keywords.get().empty()) { + KeywordsType xmlKeywords = keywords.get(); + confListener->onConferenceKeywordsChanged( + vector(xmlKeywords.begin(), xmlKeywords.end()) + ); + } + } + + auto &users = confInfo->getUsers(); + if (!users.present()) + return; + + // 4. Notify changes on users. + for (auto &user : users->getUser()) { + LinphoneAddress *cAddr = linphone_core_interpret_url(conf->getCore()->getCCore(), user.getEntity()->c_str()); + char *cAddrStr = linphone_address_as_string(cAddr); + linphone_address_unref(cAddr); + + Address addr(cAddrStr); + bctbx_free(cAddrStr); + + StateType state = user.getState(); + + if (state == StateType::deleted) { + confListener->onParticipantRemoved( + make_shared( + EventLog::Type::ConferenceParticipantRemoved, + creationTime, + chatRoomId, + lastNotify, + addr + ), + isFullState + ); + + continue; + } + + if (state == StateType::full) + confListener->onParticipantAdded( + make_shared( + EventLog::Type::ConferenceParticipantAdded, + creationTime, + chatRoomId, + lastNotify, + addr + ), + isFullState + ); + + auto &roles = user.getRoles(); + if (roles) { + auto &entry = roles->getEntry(); + confListener->onParticipantSetAdmin( + make_shared( + find(entry, "admin") != entry.end() + ? EventLog::Type::ConferenceParticipantSetAdmin + : EventLog::Type::ConferenceParticipantUnsetAdmin, + creationTime, + chatRoomId, + lastNotify, + addr + ), + isFullState + ); + } + + for (const auto &endpoint : user.getEndpoint()) { + if (!endpoint.getEntity().present()) + break; + + Address gruu(endpoint.getEntity().get()); + StateType state = endpoint.getState(); + + if (state == StateType::deleted) { + confListener->onParticipantDeviceRemoved( + make_shared( + EventLog::Type::ConferenceParticipantDeviceRemoved, + creationTime, + chatRoomId, + lastNotify, + addr, + gruu + ), + isFullState + ); + } else if (state == StateType::full) { + confListener->onParticipantDeviceAdded( + make_shared( + EventLog::Type::ConferenceParticipantDeviceAdded, + creationTime, + chatRoomId, + lastNotify, + addr, + gruu + ), + isFullState + ); + } + } + } + + if (isFullState) + confListener->onFirstNotifyReceived(chatRoomId.getPeerAddress()); } // ----------------------------------------------------------------------------- diff --git a/src/conference/local-conference.cpp b/src/conference/local-conference.cpp index b07f17287..fe06f6464 100644 --- a/src/conference/local-conference.cpp +++ b/src/conference/local-conference.cpp @@ -21,6 +21,7 @@ #include "content/content-type.h" #include "handlers/local-conference-event-handler.h" #include "local-conference-p.h" +#include "logger/logger.h" #include "participant-p.h" #include "xml/resource-lists.h" @@ -46,8 +47,10 @@ LocalConference::~LocalConference () { void LocalConference::addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) { L_D(); shared_ptr participant = findParticipant(addr); - if (participant) + if (participant) { + lInfo() << "Not adding participant '" << addr.asString() << "' because it is already a participant of the LocalConference"; return; + } participant = make_shared(addr); participant->getPrivate()->createSession(*this, params, hasMedia, d->listener); d->participants.push_back(participant); diff --git a/src/conference/participant-device.cpp b/src/conference/participant-device.cpp index ea8b1c7a8..e04ecef3f 100644 --- a/src/conference/participant-device.cpp +++ b/src/conference/participant-device.cpp @@ -29,9 +29,8 @@ LINPHONE_BEGIN_NAMESPACE ParticipantDevice::ParticipantDevice () {} -ParticipantDevice::ParticipantDevice (const IdentityAddress &gruu) { - mGruu = gruu; -} +ParticipantDevice::ParticipantDevice (const Participant *participant, const IdentityAddress &gruu) + : mParticipant(participant), mGruu(gruu) {} ParticipantDevice::~ParticipantDevice () { if (mConferenceSubscribeEvent) diff --git a/src/conference/participant-device.h b/src/conference/participant-device.h index 09fa3101c..f1abe8e1e 100644 --- a/src/conference/participant-device.h +++ b/src/conference/participant-device.h @@ -23,6 +23,7 @@ #include #include "address/identity-address.h" + #include "linphone/types.h" #include "linphone/utils/general.h" @@ -31,6 +32,7 @@ LINPHONE_BEGIN_NAMESPACE class CallSession; +class Participant; class ParticipantDevice { public: @@ -42,12 +44,13 @@ public: }; ParticipantDevice (); - explicit ParticipantDevice (const IdentityAddress &gruu); + explicit ParticipantDevice (const Participant *participant, const IdentityAddress &gruu); virtual ~ParticipantDevice (); bool operator== (const ParticipantDevice &device) const; inline const IdentityAddress &getAddress () const { return mGruu; } + const Participant *getParticipant () const { return mParticipant; } inline std::shared_ptr getSession () const { return mSession; } inline void setSession (std::shared_ptr session) { mSession = session; } inline State getState () const { return mState; } @@ -60,6 +63,7 @@ public: bool isValid () const { return mGruu.isValid(); } private: + const Participant *mParticipant = nullptr; IdentityAddress mGruu; std::shared_ptr mSession; LinphoneEvent *mConferenceSubscribeEvent = nullptr; diff --git a/src/conference/participant.cpp b/src/conference/participant.cpp index f67694d4e..68d6e26f7 100644 --- a/src/conference/participant.cpp +++ b/src/conference/participant.cpp @@ -47,10 +47,11 @@ shared_ptr ParticipantPrivate::createSession ( // ----------------------------------------------------------------------------- shared_ptr ParticipantPrivate::addDevice (const IdentityAddress &gruu) { + L_Q(); shared_ptr device = findDevice(gruu); if (device) return device; - device = make_shared(gruu); + device = make_shared(q, gruu); devices.push_back(device); return device; } diff --git a/src/conference/remote-conference.cpp b/src/conference/remote-conference.cpp index dd2a8a67d..ffdcde9ab 100644 --- a/src/conference/remote-conference.cpp +++ b/src/conference/remote-conference.cpp @@ -18,6 +18,7 @@ */ #include "handlers/remote-conference-event-handler.h" +#include "logger/logger.h" #include "participant-p.h" #include "remote-conference-p.h" #include "xml/resource-lists.h" @@ -47,8 +48,10 @@ RemoteConference::~RemoteConference () { void RemoteConference::addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) { L_D(); shared_ptr participant = findParticipant(addr); - if (participant) + if (participant) { + lInfo() << "Not adding participant '" << addr.asString() << "' because it is already a participant of the RemoteConference"; return; + } participant = make_shared(addr); participant->getPrivate()->createSession(*this, params, hasMedia, d->listener); d->participants.push_back(participant); diff --git a/src/conference/session/call-session-listener.h b/src/conference/session/call-session-listener.h index f14d9249c..a334614ea 100644 --- a/src/conference/session/call-session-listener.h +++ b/src/conference/session/call-session-listener.h @@ -22,6 +22,8 @@ #include "conference/session/call-session.h" +#include + // ============================================================================= LINPHONE_BEGIN_NAMESPACE @@ -32,49 +34,51 @@ class LINPHONE_PUBLIC CallSessionListener { public: virtual ~CallSessionListener() = default; - virtual void onAckBeingSent (const std::shared_ptr &session, LinphoneHeaders *headers) {} - virtual void onAckReceived (const std::shared_ptr &session, LinphoneHeaders *headers) {} - virtual void onBackgroundTaskToBeStarted (const std::shared_ptr &session) {} - virtual void onBackgroundTaskToBeStopped (const std::shared_ptr &session) {} - virtual bool onCallSessionAccepted (const std::shared_ptr &session) { return false; } - virtual void onCallSessionConferenceStreamStarting (const std::shared_ptr &session, bool mute) {} - virtual void onCallSessionConferenceStreamStopping (const std::shared_ptr &session) {} - virtual void onCallSessionEarlyFailed (const std::shared_ptr &session, LinphoneErrorInfo *ei) {} - virtual void onCallSessionSetReleased (const std::shared_ptr &session) {} - virtual void onCallSessionSetTerminated (const std::shared_ptr &session) {} - virtual void onCallSessionStartReferred (const std::shared_ptr &session) {} - virtual void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) {} - virtual void onCallSessionTransferStateChanged (const std::shared_ptr &session, CallSession::State state) {} - virtual void onCheckForAcceptation (const std::shared_ptr &session) {} - virtual void onDtmfReceived (const std::shared_ptr &session, char dtmf) {} - virtual void onIncomingCallSessionNotified (const std::shared_ptr &session) {} - virtual void onIncomingCallSessionStarted (const std::shared_ptr &session) {} - virtual void onIncomingCallSessionTimeoutCheck (const std::shared_ptr &session, int elapsed, bool oneSecondElapsed) {} - virtual void onInfoReceived (const std::shared_ptr &session, const LinphoneInfoMessage *im) {} - virtual void onNoMediaTimeoutCheck (const std::shared_ptr &session, bool oneSecondElapsed) {} + virtual void onAckBeingSent (const std::shared_ptr &session, LinphoneHeaders *headers) {} + virtual void onAckReceived (const std::shared_ptr &session, LinphoneHeaders *headers) {} + virtual void onBackgroundTaskToBeStarted (const std::shared_ptr &session) {} + virtual void onBackgroundTaskToBeStopped (const std::shared_ptr &session) {} + virtual bool onCallSessionAccepted (const std::shared_ptr &session) { return false; } + virtual void onCallSessionConferenceStreamStarting (const std::shared_ptr &session, bool mute) {} + virtual void onCallSessionConferenceStreamStopping (const std::shared_ptr &session) {} + virtual void onCallSessionEarlyFailed (const std::shared_ptr &session, LinphoneErrorInfo *ei) {} + virtual void onCallSessionSetReleased (const std::shared_ptr &session) {} + virtual void onCallSessionSetTerminated (const std::shared_ptr &session) {} + virtual void onCallSessionStartReferred (const std::shared_ptr &session) {} + virtual void onCallSessionStateChanged (const std::shared_ptr &session, CallSession::State state, const std::string &message) {} + virtual void onCallSessionTransferStateChanged (const std::shared_ptr &session, CallSession::State state) {} + virtual void onCheckForAcceptation (const std::shared_ptr &session) {} + virtual void onDtmfReceived (const std::shared_ptr &session, char dtmf) {} + virtual void onIncomingCallSessionNotified (const std::shared_ptr &session) {} + virtual void onIncomingCallSessionStarted (const std::shared_ptr &session) {} + virtual void onIncomingCallSessionTimeoutCheck (const std::shared_ptr &session, int elapsed, bool oneSecondElapsed) {} + virtual void onInfoReceived (const std::shared_ptr &session, const LinphoneInfoMessage *im) {} + virtual void onNoMediaTimeoutCheck (const std::shared_ptr &session, bool oneSecondElapsed) {} - virtual void onEncryptionChanged (const std::shared_ptr &session, bool activated, const std::string &authToken) {} + virtual void onEncryptionChanged (const std::shared_ptr &session, bool activated, const std::string &authToken) {} - virtual void onCallSessionStateChangedForReporting (const std::shared_ptr &session) {} - virtual void onRtcpUpdateForReporting (const std::shared_ptr &session, SalStreamType type) {} - virtual void onStatsUpdated (const std::shared_ptr &session, const LinphoneCallStats *stats) {} - virtual void onUpdateMediaInfoForReporting (const std::shared_ptr &session, int statsType) {} + virtual void onCallSessionStateChangedForReporting (const std::shared_ptr &session) {} + virtual void onRtcpUpdateForReporting (const std::shared_ptr &session, SalStreamType type) {} + virtual void onStatsUpdated (const std::shared_ptr &session, const LinphoneCallStats *stats) {} + virtual void onUpdateMediaInfoForReporting (const std::shared_ptr &session, int statsType) {} - virtual void onResetCurrentSession (const std::shared_ptr &session) {} - virtual void onSetCurrentSession (const std::shared_ptr &session) {} + virtual void onResetCurrentSession (const std::shared_ptr &session) {} + virtual void onSetCurrentSession (const std::shared_ptr &session) {} - virtual void onFirstVideoFrameDecoded (const std::shared_ptr &session) {} - virtual void onResetFirstVideoFrameDecoded (const std::shared_ptr &session) {} + virtual void onFirstVideoFrameDecoded (const std::shared_ptr &session) {} + virtual void onResetFirstVideoFrameDecoded (const std::shared_ptr &session) {} - virtual void onPlayErrorTone (const std::shared_ptr &session, LinphoneReason reason) {} - virtual void onRingbackToneRequested (const std::shared_ptr &session, bool requested) {} - virtual void onStartRinging (const std::shared_ptr &session) {} - virtual void onStopRinging (const std::shared_ptr &session) {} - virtual void onStopRingingIfInCall (const std::shared_ptr &session) {} - virtual void onStopRingingIfNeeded (const std::shared_ptr &session) {} + virtual void onPlayErrorTone (const std::shared_ptr &session, LinphoneReason reason) {} + virtual void onRingbackToneRequested (const std::shared_ptr &session, bool requested) {} + virtual void onStartRinging (const std::shared_ptr &session) {} + virtual void onStopRinging (const std::shared_ptr &session) {} + virtual void onStopRingingIfInCall (const std::shared_ptr &session) {} + virtual void onStopRingingIfNeeded (const std::shared_ptr &session) {} - virtual bool areSoundResourcesAvailable (const std::shared_ptr &session) { return true; } - virtual bool isPlayingRingbackTone (const std::shared_ptr &session) { return false; } + virtual bool areSoundResourcesAvailable (const std::shared_ptr &session) { return true; } + virtual bool isPlayingRingbackTone (const std::shared_ptr &session) { return false; } + + virtual void onRealTimeTextCharacterReceived (const std::shared_ptr &session, RealtimeTextReceivedCharacter *data) {} }; LINPHONE_END_NAMESPACE diff --git a/src/conference/session/call-session-p.h b/src/conference/session/call-session-p.h index 091fd20ca..8ad819ce7 100644 --- a/src/conference/session/call-session-p.h +++ b/src/conference/session/call-session-p.h @@ -75,7 +75,7 @@ protected: void accept (const CallSessionParams *params); virtual LinphoneStatus acceptUpdate (const CallSessionParams *csp, CallSession::State nextState, const std::string &stateInfo); - LinphoneStatus checkForAcceptation () const; + LinphoneStatus checkForAcceptation (); virtual void handleIncomingReceivedStateInIncomingNotification (); virtual bool isReadyForInvite () const; bool isUpdateAllowed (CallSession::State &nextState) const; diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index 97aa06f6f..1e640f584 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -521,7 +521,7 @@ LinphoneStatus CallSessionPrivate::acceptUpdate (const CallSessionParams *csp, C return startAcceptUpdate(nextState, stateInfo); } -LinphoneStatus CallSessionPrivate::checkForAcceptation () const { +LinphoneStatus CallSessionPrivate::checkForAcceptation () { L_Q(); switch (state) { case CallSession::State::IncomingReceived: @@ -1226,6 +1226,12 @@ const LinphoneErrorInfo * CallSession::getErrorInfo () const { return d->ei; } +const Address& CallSession::getLocalAddress () const { + L_D(); + return *L_GET_CPP_PTR_FROM_C_OBJECT((d->direction == LinphoneCallIncoming) + ? linphone_call_log_get_to(d->log) : linphone_call_log_get_from(d->log)); +} + LinphoneCallLog * CallSession::getLog () const { L_D(); return d->log; @@ -1251,10 +1257,6 @@ const Address& CallSession::getRemoteAddress () const { ? linphone_call_log_get_from(d->log) : linphone_call_log_get_to(d->log)); } -string CallSession::getRemoteAddressAsString () const { - return getRemoteAddress().asString(); -} - string CallSession::getRemoteContact () const { L_D(); if (d->op) { diff --git a/src/conference/session/call-session.h b/src/conference/session/call-session.h index cddbfbaa4..563ca89bb 100644 --- a/src/conference/session/call-session.h +++ b/src/conference/session/call-session.h @@ -57,7 +57,7 @@ public: virtual ~CallSession (); LinphoneStatus accept (const CallSessionParams *csp = nullptr); - LinphoneStatus acceptUpdate (const CallSessionParams *csp); + LinphoneStatus acceptUpdate (const CallSessionParams *csp = nullptr); virtual void configure (LinphoneCallDir direction, LinphoneProxyConfig *cfg, SalCallOp *op, const Address &from, const Address &to); LinphoneStatus decline (LinphoneReason reason); LinphoneStatus decline (const LinphoneErrorInfo *ei); @@ -78,23 +78,23 @@ public: CallSessionParams *getCurrentParams () const; LinphoneCallDir getDirection () const; - const Address& getDiversionAddress () const; + const Address &getDiversionAddress () const; int getDuration () const; const LinphoneErrorInfo * getErrorInfo () const; - LinphoneCallLog * getLog () const; + const Address &getLocalAddress () const; + LinphoneCallLog *getLog () const; virtual const CallSessionParams *getParams () const; LinphoneReason getReason () const; std::shared_ptr getReferer () const; std::string getReferTo () const; - const Address& getRemoteAddress () const; - std::string getRemoteAddressAsString () const; + const Address &getRemoteAddress () const; std::string getRemoteContact () const; const Address *getRemoteContactAddress () const; const CallSessionParams *getRemoteParams (); std::string getRemoteUserAgent () const; std::shared_ptr getReplacedCallSession () const; CallSession::State getState () const; - const Address& getToAddress () const; + const Address &getToAddress () const; CallSession::State getTransferState () const; std::shared_ptr getTransferTarget () const; std::string getToHeader (const std::string &name) const; diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h index a4ba68257..a238078c1 100644 --- a/src/conference/session/media-session-p.h +++ b/src/conference/session/media-session-p.h @@ -135,7 +135,7 @@ private: void updateRemoteSessionIdAndVer (); void initStats (LinphoneCallStats *stats, LinphoneStreamType type); - void notifyStatsUpdated (int streamIndex) const; + void notifyStatsUpdated (int streamIndex); OrtpEvQueue *getEventQueue (int streamIndex) const; MediaStream *getMediaStream (int streamIndex) const; diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index b3b6f04cd..c21b2fa22 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -933,7 +933,7 @@ void MediaSessionPrivate::initStats (LinphoneCallStats *stats, LinphoneStreamTyp _linphone_call_stats_set_ice_state(stats, LinphoneIceStateNotActivated); } -void MediaSessionPrivate::notifyStatsUpdated (int streamIndex) const { +void MediaSessionPrivate::notifyStatsUpdated (int streamIndex) { L_Q(); LinphoneCallStats *stats = nullptr; if (streamIndex == mainAudioStreamIndex) @@ -2830,7 +2830,7 @@ void MediaSessionPrivate::startTextStream () { rtp_session_set_multicast_ttl(textStream->ms.sessions.rtp_session, tstream->ttl); text_stream_start(textStream, textProfile, rtpAddr, tstream->rtp_port, rtcpAddr, (linphone_core_rtcp_enabled(q->getCore()->getCCore()) && !isMulticast) ? (tstream->rtcp_port ? tstream->rtcp_port : tstream->rtp_port + 1) : 0, usedPt); - ms_filter_add_notify_callback(textStream->rttsink, realTimeTextCharacterReceived, q, false); + ms_filter_add_notify_callback(textStream->rttsink, realTimeTextCharacterReceived, this, false); ms_media_stream_sessions_set_encryption_mandatory(&textStream->ms.sessions, isEncryptionMandatory()); } } else @@ -3806,12 +3806,11 @@ void MediaSessionPrivate::videoStreamEventCb (const MSFilter *f, const unsigned #endif void MediaSessionPrivate::realTimeTextCharacterReceived (MSFilter *f, unsigned int id, void *arg) { + L_Q(); if (id == MS_RTT_4103_RECEIVED_CHAR) { -#if 0 RealtimeTextReceivedCharacter *data = reinterpret_cast(arg); - LinphoneChatRoom * chat_room = linphone_call_get_chat_room(call); - linphone_core_real_time_text_received(call->core, chat_room, data->character, call); -#endif + if (listener) + listener->onRealTimeTextCharacterReceived(q->getSharedFromThis(), data); } } diff --git a/src/content/content-manager.cpp b/src/content/content-manager.cpp index 0eb453329..fea82b66c 100644 --- a/src/content/content-manager.cpp +++ b/src/content/content-manager.cpp @@ -96,7 +96,10 @@ Content ContentManager::contentListToMultipart (const list &contents) { Content content; content.setBody(desc); - content.setContentType(ContentType::Multipart); + ContentType contentType = ContentType::Multipart; + string boundary = "boundary=" + string(MULTIPART_BOUNDARY); + contentType.setParameter(boundary); + content.setContentType(contentType); belle_sip_free(desc); belle_sip_object_unref(mpbh); diff --git a/src/db/abstract/abstract-db.cpp b/src/db/abstract/abstract-db.cpp index ed6295891..54d63ceb1 100644 --- a/src/db/abstract/abstract-db.cpp +++ b/src/db/abstract/abstract-db.cpp @@ -92,6 +92,7 @@ bool AbstractDb::forceReconnect () { try { lInfo() << "Reconnect... Try: " << i; session->reconnect(); + init(); lInfo() << "Database reconnection successful!"; return true; } catch (const soci::soci_error &e) { diff --git a/src/db/main-db-p.h b/src/db/main-db-p.h index 5f3ea6896..81e5535b4 100644 --- a/src/db/main-db-p.h +++ b/src/db/main-db-p.h @@ -28,25 +28,21 @@ // ============================================================================= -namespace soci { - class row; -} - LINPHONE_BEGIN_NAMESPACE class Content; class MainDbPrivate : public AbstractDbPrivate { public: + struct Statements; + mutable std::unordered_map> storageIdToEvent; mutable std::unordered_map> storageIdToChatMessage; private: - // --------------------------------------------------------------------------- - // SOCI helpers. - // --------------------------------------------------------------------------- + std::unique_ptr statements; - long long resolveId (const soci::row &row, int col) const; + void initStatements (); // --------------------------------------------------------------------------- // Low level API. @@ -156,6 +152,7 @@ private: unsigned int getModuleVersion (const std::string &name); void updateModuleVersion (const std::string &name, unsigned int version); + void updateSchema (); // --------------------------------------------------------------------------- // Import. diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index cf92abd52..41ece4212 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -17,9 +17,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include +#include "linphone/utils/algorithm.h" #include "linphone/utils/utils.h" #include "chat/chat-message/chat-message-p.h" @@ -36,11 +36,6 @@ #include "main-db-key-p.h" #include "main-db-p.h" -#define DB_MODULE_VERSION_EVENTS L_VERSION(1, 0, 0) -#define DB_MODULE_VERSION_FRIENDS L_VERSION(1, 0, 0) -#define DB_MODULE_VERSION_LEGACY_FRIENDS_IMPORT L_VERSION(1, 0, 0) -#define DB_MODULE_VERSION_LEGACY_HISTORY_IMPORT L_VERSION(1, 0, 0) - // ============================================================================= // See: http://soci.sourceforge.net/doc/3.2/exchange.html @@ -52,6 +47,41 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE +namespace { + constexpr unsigned int ModuleVersionEvents = makeVersion(1, 0, 1); + constexpr unsigned int ModuleVersionFriends = makeVersion(1, 0, 0); + constexpr unsigned int ModuleVersionLegacyFriendsImport = makeVersion(1, 0, 0); + constexpr unsigned int ModuleVersionLegacyHistoryImport = makeVersion(1, 0, 0); + + constexpr int LegacyFriendListColId = 0; + constexpr int LegacyFriendListColName = 1; + constexpr int LegacyFriendListColRlsUri = 2; + constexpr int LegacyFriendListColSyncUri = 3; + constexpr int LegacyFriendListColRevision = 4; + + constexpr int LegacyFriendColFriendListId = 1; + constexpr int LegacyFriendColSipAddress = 2; + constexpr int LegacyFriendColSubscribePolicy = 3; + constexpr int LegacyFriendColSendSubscribe = 4; + constexpr int LegacyFriendColRefKey = 5; + constexpr int LegacyFriendColVCard = 6; + constexpr int LegacyFriendColVCardEtag = 7; + constexpr int LegacyFriendColVCardSyncUri = 8; + constexpr int LegacyFriendColPresenceReceived = 9; + + constexpr int LegacyMessageColLocalAddress = 1; + constexpr int LegacyMessageColRemoteAddress = 2; + constexpr int LegacyMessageColDirection = 3; + constexpr int LegacyMessageColText = 4; + constexpr int LegacyMessageColState = 7; + constexpr int LegacyMessageColUrl = 8; + constexpr int LegacyMessageColDate = 9; + constexpr int LegacyMessageColAppData = 10; + constexpr int LegacyMessageColContentId = 11; + constexpr int LegacyMessageColContentType = 13; + constexpr int LegacyMessageColIsSecured = 14; +} + // ----------------------------------------------------------------------------- MainDb::MainDb (const shared_ptr &core) : AbstractDb(*new MainDbPrivate), CoreAccessor(core) {} @@ -80,20 +110,30 @@ struct SafeTransactionInfo { template class SafeTransaction { + using InternalReturnType = typename remove_reference()())>::type; + public: - using ReturnType = typename remove_reference()())>::type; + using ReturnType = typename std::conditional< + std::is_same::value, + bool, + InternalReturnType + >::type; SafeTransaction (SafeTransactionInfo &info, Function function) : mFunction(move(function)) { try { - mResult = mFunction(); + mResult= exec(); } catch (const soci::soci_error &e) { - lWarning() << "Catched exception in MainDb::" << info.name << "."; + lWarning() << "Catched exception in MainDb::" << info.name << "(" << e.what() << ")."; soci::soci_error::error_category category = e.get_error_category(); if ( (category == soci::soci_error::connection_error || category == soci::soci_error::unknown) && info.mainDb->forceReconnect() ) { - mResult = mFunction(); + try { + mResult = exec(); + } catch (const exception &e) { + lError() << "Unable to execute query after reconnect in MainDb::" << info.name << "(" << e.what() << ")."; + } return; } lError() << "Unhandled [" << getErrorCategoryAsString(category) << "] exception in MainDb::" << @@ -110,6 +150,19 @@ public: } private: + // Exec function with no return type. + template + typename std::enable_if::value, bool>::type exec () const { + mFunction(); + return true; + } + + // Exec function with return type. + template + typename std::enable_if::value, T>::type exec () const { + return mFunction(); + } + static const char *getErrorCategoryAsString (soci::soci_error::error_category category) { switch (category) { case soci::soci_error::connection_error: @@ -163,10 +216,12 @@ static constexpr const char *mapEnumToSql (const EnumToSql enumToSql[], size_ ); } +// Update me event-log-enums values are changed! static constexpr EnumToSql eventFilterToSql[] = { { MainDb::ConferenceCallFilter, "3, 4" }, { MainDb::ConferenceChatMessageFilter, "5" }, - { MainDb::ConferenceInfoFilter, "1, 2, 6, 7, 8, 9, 10, 11, 12" } + { MainDb::ConferenceInfoFilter, "1, 2, 6, 7, 8, 9, 10, 11, 12" }, + { MainDb::ConferenceInfoNoDeviceFilter, "1, 2, 6, 7, 8, 9, 12" } }; static constexpr const char *mapEventFilterToSql (MainDb::Filter filter) { @@ -182,11 +237,7 @@ static string buildSqlEventFilter ( MainDb::FilterMask mask, const string &condKeyWord = "WHERE" ) { - L_ASSERT( - find_if(filters.cbegin(), filters.cend(), [](const MainDb::Filter &filter) { - return filter == MainDb::NoFilter; - }) == filters.cend() - ); + L_ASSERT(findIf(filters, [](const MainDb::Filter &filter) { return filter == MainDb::NoFilter; }) == filters.cend()); if (mask == MainDb::NoFilter) return ""; @@ -231,15 +282,57 @@ static constexpr string &blobToString (string &in) { return in; } +// ----------------------------------------------------------------------------- +// Statements and helpers. // ----------------------------------------------------------------------------- -long long MainDbPrivate::resolveId (const soci::row &row, int col) const { - L_Q(); - // See: http://soci.sourceforge.net/doc/master/backends/ - // `row id` is not supported by soci on Sqlite3. It's necessary to cast id to int... - return q->getBackend() == AbstractDb::Sqlite3 - ? static_cast(row.get(0)) - : static_cast(row.get(0)); +class StatementBind { +public: + StatementBind (soci::statement &stmt) : mStmt(stmt) {} + + ~StatementBind () { + mStmt.bind_clean_up(); + } + + template + void bind (const T &var, const char *name) { + mStmt.exchange(soci::use(var, name)); + } + + template + void bindResult (T &var) { + mStmt.exchange(soci::into(var)); + } + + bool exec () { + mStmt.define_and_bind(); + return mStmt.execute(true); + } + +private: + soci::statement &mStmt; +}; + +static inline unique_ptr makeStatement (soci::session &session, const char *stmt) { + return makeUnique(session.prepare << stmt); +} + +struct MainDbPrivate::Statements { + typedef unique_ptr Statement; + + Statement selectSipAddressId; + Statement selectChatRoomId; +}; + +void MainDbPrivate::initStatements () { + soci::session *session = dbSession.getBackendSession(); + statements = makeUnique(); + + statements->selectSipAddressId = makeStatement(*session, "SELECT id FROM sip_address WHERE value = :sipAddress"); + statements->selectChatRoomId = makeStatement( + *session, + "SELECT id FROM chat_room WHERE peer_sip_address_id = :peerSipAddressId AND local_sip_address_id = :localSipAddressId" + ); } // ----------------------------------------------------------------------------- @@ -424,39 +517,35 @@ void MainDbPrivate::insertChatRoomParticipantDevice ( } void MainDbPrivate::insertChatMessageParticipant (long long eventId, long long sipAddressId, int state) { - // TODO: Deal with read messages. - // Remove if displayed? Think a good alorithm for mark as read. - soci::session *session = dbSession.getBackendSession(); - soci::statement statement = ( - session->prepare << "UPDATE chat_message_participant SET state = :state" - " WHERE event_id = :eventId AND participant_sip_address_id = :sipAddressId", - soci::use(state), soci::use(eventId), soci::use(sipAddressId) - ); - statement.execute(); - if (statement.get_affected_rows() == 0 && state != static_cast(ChatMessage::State::Displayed)) + if (state != static_cast(ChatMessage::State::Displayed)) { + soci::session *session = dbSession.getBackendSession(); *session << "INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state)" " VALUES (:eventId, :sipAddressId, :state)", soci::use(eventId), soci::use(sipAddressId), soci::use(state); + } } // ----------------------------------------------------------------------------- long long MainDbPrivate::selectSipAddressId (const string &sipAddress) const { - soci::session *session = dbSession.getBackendSession(); - long long id; - *session << "SELECT id FROM sip_address WHERE value = :sipAddress", soci::use(sipAddress), soci::into(id); - return session->got_data() ? id : -1; + + StatementBind stmt(*statements->selectSipAddressId); + stmt.bind(sipAddress, "sipAddress"); + stmt.bindResult(id); + + return stmt.exec() ? id : -1; } long long MainDbPrivate::selectChatRoomId (long long peerSipAddressId, long long localSipAddressId) const { - soci::session *session = dbSession.getBackendSession(); - long long id; - *session << "SELECT id FROM chat_room" - " WHERE peer_sip_address_id = :peerSipAddressId AND local_sip_address_id = :localSipAddressId", - soci::use(peerSipAddressId), soci::use(localSipAddressId), soci::into(id); - return session->got_data() ? id : -1; + + StatementBind stmt(*statements->selectChatRoomId); + stmt.bind(peerSipAddressId, "peerSipAddressId"); + stmt.bind(localSipAddressId, "localSipAddressId"); + stmt.bindResult(id); + + return stmt.exec() ? id : -1; } long long MainDbPrivate::selectChatRoomId (const ChatRoomId &chatRoomId) const { @@ -650,12 +739,12 @@ shared_ptr MainDbPrivate::selectConferenceChatMessageEvent ( chatMessage = shared_ptr(new ChatMessage( chatRoom, - static_cast(direction) + ChatMessage::Direction(direction) )); - chatMessage->setIsSecured(static_cast(isSecured)); + chatMessage->setIsSecured(bool(isSecured)); ChatMessagePrivate *dChatMessage = chatMessage->getPrivate(); - dChatMessage->setState(static_cast(state), true); + dChatMessage->setState(ChatMessage::State(state), true); dChatMessage->forceFromAddress(IdentityAddress(fromSipAddress)); dChatMessage->forceToAddress(IdentityAddress(toSipAddress)); @@ -673,7 +762,7 @@ shared_ptr MainDbPrivate::selectConferenceChatMessageEvent ( soci::rowset rows = (session->prepare << query, soci::use(eventId)); for (const auto &row : rows) { ContentType contentType(row.get(2)); - const long long &contentId = resolveId(row, 0); + const long long &contentId = dbSession.resolveId(row, 0); Content *content; if (contentType == ContentType::FileTransfer) { @@ -692,7 +781,7 @@ shared_ptr MainDbPrivate::selectConferenceChatMessageEvent ( FileContent *fileContent = new FileContent(); fileContent->setFileName(name); - fileContent->setFileSize(static_cast(size)); + fileContent->setFileSize(size_t(size)); fileContent->setFilePath(path); content = fileContent; @@ -703,6 +792,7 @@ shared_ptr MainDbPrivate::selectConferenceChatMessageEvent ( content->setBody(row.get(3)); // 2.2 - Fetch contents' app data. + // TODO: Do not test backend, encapsulate!!! if (q->getBackend() == MainDb::Backend::Sqlite3) { soci::blob data(*session); fetchContentAppData(session, *content, contentId, data); @@ -813,7 +903,7 @@ shared_ptr MainDbPrivate::selectConferenceSubjectEvent ( long long MainDbPrivate::insertEvent (const shared_ptr &eventLog) { soci::session *session = dbSession.getBackendSession(); - const int &type = static_cast(eventLog->getType()); + const int &type = int(eventLog->getType()); const tm &creationTime = Utils::getTimeTAsTm(eventLog->getCreationTime()); *session << "INSERT INTO event (type, creation_time) VALUES (:type, :creationTime)", soci::use(type), @@ -878,8 +968,8 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptrgetFromAddress().asString()); const long long &toSipAddressId = insertSipAddress(chatMessage->getToAddress().asString()); const tm &messageTime = Utils::getTimeTAsTm(chatMessage->getTime()); - const int &state = static_cast(chatMessage->getState()); - const int &direction = static_cast(chatMessage->getDirection()); + const int &state = int(chatMessage->getState()); + const int &direction = int(chatMessage->getDirection()); const string &imdnMessageId = chatMessage->getImdnMessageId(); const int &isSecured = chatMessage->isSecured() ? 1 : 0; @@ -896,6 +986,11 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptrgetContents()) insertContent(eventId, *content); + for (const auto &participant : chatRoom->getParticipants()) { + const long long &participantSipAddressId = selectSipAddressId(participant->getAddress().asString()); + insertChatMessageParticipant(eventId, participantSipAddressId, state); + } + return eventId; } @@ -912,7 +1007,7 @@ void MainDbPrivate::updateConferenceChatMessageEvent (const shared_ptr const long long &eventId = dEventKey->storageId; soci::session *session = dbSession.getBackendSession(); - const int &state = static_cast(chatMessage->getState()); + const int &state = int(chatMessage->getState()); const string &imdnMessageId = chatMessage->getImdnMessageId(); *session << "UPDATE conference_chat_message_event SET state = :state, imdn_message_id = :imdnMessageId" " WHERE event_id = :eventId", @@ -1096,7 +1191,7 @@ void MainDbPrivate::invalidConferenceEventsFromQuery (const string &query, long soci::session *session = dbSession.getBackendSession(); soci::rowset rows = (session->prepare << query, soci::use(chatRoomId)); for (const auto &row : rows) { - long long eventId = resolveId(row, 0); + long long eventId = dbSession.resolveId(row, 0); shared_ptr eventLog = getEventFromCache(eventId); if (eventLog) { const EventLogPrivate *dEventLog = eventLog->getPrivate(); @@ -1133,60 +1228,51 @@ void MainDbPrivate::updateModuleVersion (const string &name, unsigned int versio soci::use(name), soci::use(version); } +void MainDbPrivate::updateSchema () { + soci::session *session = dbSession.getBackendSession(); + unsigned int version = getModuleVersion("events"); + + if (version < makeVersion(1, 0, 1)) { + *session << "ALTER TABLE chat_room_participant_device ADD COLUMN state TINYINT UNSIGNED DEFAULT 0"; + } +} + // ----------------------------------------------------------------------------- -#define CHECK_LEGACY_TABLE_EXISTS(SESSION, NAME) \ - do { \ - SESSION << "SELECT name FROM sqlite_master WHERE type='table' AND name='" NAME "'"; \ - return SESSION.got_data() > 0; \ - } while (false); +// NOTE: Legacy supports only sqlite. +static inline bool checkLegacyTableExists (soci::session &session, const string &name) { + session << "SELECT name FROM sqlite_master WHERE type='table' AND name = :name", soci::use(name); + return session.got_data() > 0; +} static inline bool checkLegacyFriendsTableExists (soci::session &session) { - CHECK_LEGACY_TABLE_EXISTS(session, "friends"); + return checkLegacyTableExists(session, "friends"); } static inline bool checkLegacyHistoryTableExists (soci::session &session) { - CHECK_LEGACY_TABLE_EXISTS(session, "history"); + return checkLegacyTableExists(session, "history"); } template static T getValueFromRow (const soci::row &row, int index, bool &isNull) { isNull = false; - try { - return row.get(static_cast(index)); - } catch (const exception &) { + if (row.get_indicator(size_t(index)) == soci::i_null){ isNull = true; + return T(); } - - return T(); + return row.get(size_t(index)); } // ----------------------------------------------------------------------------- -#define LEGACY_FRIEND_LIST_COL_ID 0 -#define LEGACY_FRIEND_LIST_COL_NAME 1 -#define LEGACY_FRIEND_LIST_COL_RLS_URI 2 -#define LEGACY_FRIEND_LIST_COL_SYNC_URI 3 -#define LEGACY_FRIEND_LIST_COL_REVISION 4 - -#define LEGACY_FRIEND_COL_FRIEND_LIST_ID 1 -#define LEGACY_FRIEND_COL_SIP_ADDRESS 2 -#define LEGACY_FRIEND_COL_SUBSCRIBE_POLICY 3 -#define LEGACY_FRIEND_COL_SEND_SUBSCRIBE 4 -#define LEGACY_FRIEND_COL_REF_KEY 5 -#define LEGACY_FRIEND_COL_V_CARD 6 -#define LEGACY_FRIEND_COL_V_CARD_ETAG 7 -#define LEGACY_FRIEND_COL_V_CARD_SYNC_URI 8 -#define LEGACY_FRIEND_COL_PRESENCE_RECEIVED 9 - void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { soci::session *inSession = inDbSession.getBackendSession(); soci::transaction tr(*dbSession.getBackendSession()); - if (getModuleVersion("legacy-friends-import") >= L_VERSION(1, 0, 0)) + if (getModuleVersion("legacy-friends-import") >= makeVersion(1, 0, 0)) return; - updateModuleVersion("legacy-friends-import", DB_MODULE_VERSION_LEGACY_FRIENDS_IMPORT); + updateModuleVersion("legacy-friends-import", ModuleVersionLegacyFriendsImport); if (!checkLegacyFriendsTableExists(*inSession)) return; @@ -1198,10 +1284,10 @@ void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { try { set names; for (const auto &friendList : friendsLists) { - const string &name = friendList.get(LEGACY_FRIEND_LIST_COL_NAME, ""); - const string &rlsUri = friendList.get(LEGACY_FRIEND_LIST_COL_RLS_URI, ""); - const string &syncUri = friendList.get(LEGACY_FRIEND_LIST_COL_SYNC_URI, ""); - const int &revision = friendList.get(LEGACY_FRIEND_LIST_COL_REVISION, 0); + const string &name = friendList.get(LegacyFriendListColName, ""); + const string &rlsUri = friendList.get(LegacyFriendListColRlsUri, ""); + const string &syncUri = friendList.get(LegacyFriendListColSyncUri, ""); + const int &revision = friendList.get(LegacyFriendListColRevision, 0); string uniqueName = name; for (int id = 0; names.find(uniqueName) != names.end(); uniqueName = name + "-" + Utils::toString(id++)); @@ -1210,7 +1296,7 @@ void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { *session << "INSERT INTO friends_list (name, rls_uri, sync_uri, revision) VALUES (" " :name, :rlsUri, :syncUri, :revision" ")", soci::use(uniqueName), soci::use(rlsUri), soci::use(syncUri), soci::use(revision); - resolvedListsIds[friendList.get(LEGACY_FRIEND_LIST_COL_ID)] = dbSession.getLastInsertId(); + resolvedListsIds[friendList.get(LegacyFriendListColId)] = dbSession.getLastInsertId(); } } catch (const exception &e) { lWarning() << "Failed to import legacy friends list: " << e.what() << "."; @@ -1222,19 +1308,19 @@ void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { for (const auto &friendInfo : friends) { long long friendsListId; { - auto it = resolvedListsIds.find(friendInfo.get(LEGACY_FRIEND_COL_FRIEND_LIST_ID, -1)); + auto it = resolvedListsIds.find(friendInfo.get(LegacyFriendColFriendListId, -1)); if (it == resolvedListsIds.end()) continue; friendsListId = it->second; } - const long long &sipAddressId = insertSipAddress(friendInfo.get(LEGACY_FRIEND_COL_SIP_ADDRESS, "")); - const int &subscribePolicy = friendInfo.get(LEGACY_FRIEND_COL_SUBSCRIBE_POLICY, LinphoneSPAccept); - const int &sendSubscribe = friendInfo.get(LEGACY_FRIEND_COL_SEND_SUBSCRIBE, 1); - const string &vCard = friendInfo.get(LEGACY_FRIEND_COL_V_CARD, ""); - const string &vCardEtag = friendInfo.get(LEGACY_FRIEND_COL_V_CARD_ETAG, ""); - const string &vCardSyncUri = friendInfo.get(LEGACY_FRIEND_COL_V_CARD_SYNC_URI, ""); - const int &presenceReveived = friendInfo.get(LEGACY_FRIEND_COL_PRESENCE_RECEIVED, 0); + const long long &sipAddressId = insertSipAddress(friendInfo.get(LegacyFriendColSipAddress, "")); + const int &subscribePolicy = friendInfo.get(LegacyFriendColSubscribePolicy, LinphoneSPAccept); + const int &sendSubscribe = friendInfo.get(LegacyFriendColSendSubscribe, 1); + const string &vCard = friendInfo.get(LegacyFriendColVCard, ""); + const string &vCardEtag = friendInfo.get(LegacyFriendColVCardEtag, ""); + const string &vCardSyncUri = friendInfo.get(LegacyFriendColVCardSyncUri, ""); + const int &presenceReveived = friendInfo.get(LegacyFriendColPresenceReceived, 0); *session << "INSERT INTO friend (" " sip_address_id, friends_list_id, subscribe_policy, send_subscribe," @@ -1246,7 +1332,7 @@ void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { soci::use(presenceReveived), soci::use(vCard), soci::use(vCardEtag), soci::use(vCardSyncUri); bool isNull; - const string &data = getValueFromRow(friendInfo, LEGACY_FRIEND_COL_REF_KEY, isNull); + const string &data = getValueFromRow(friendInfo, LegacyFriendColRefKey, isNull); if (!isNull) *session << "INSERT INTO friend_app_data (friend_id, name, data) VALUES" " (:friendId, 'legacy', :data)", @@ -1261,27 +1347,14 @@ void MainDbPrivate::importLegacyFriends (DbSession &inDbSession) { lInfo() << "Successful import of legacy friends."; } -#define LEGACY_MESSAGE_COL_LOCAL_ADDRESS 1 -#define LEGACY_MESSAGE_COL_REMOTE_ADDRESS 2 -#define LEGACY_MESSAGE_COL_DIRECTION 3 -#define LEGACY_MESSAGE_COL_TEXT 4 -#define LEGACY_MESSAGE_COL_STATE 7 -#define LEGACY_MESSAGE_COL_URL 8 -#define LEGACY_MESSAGE_COL_DATE 9 -#define LEGACY_MESSAGE_COL_APP_DATA 10 -#define LEGACY_MESSAGE_COL_CONTENT_ID 11 -#define LEGACY_MESSAGE_COL_IMDN_MESSAGE_ID 12 -#define LEGACY_MESSAGE_COL_CONTENT_TYPE 13 -#define LEGACY_MESSAGE_COL_IS_SECURED 14 - void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { soci::session *inSession = inDbSession.getBackendSession(); soci::transaction tr(*dbSession.getBackendSession()); unsigned int version = getModuleVersion("legacy-history-import"); - if (version >= L_VERSION(1, 0, 0)) + if (version >= makeVersion(1, 0, 0)) return; - updateModuleVersion("legacy-history-import", DB_MODULE_VERSION_LEGACY_HISTORY_IMPORT); + updateModuleVersion("legacy-history-import", ModuleVersionLegacyHistoryImport); if (!checkLegacyHistoryTableExists(*inSession)) return; @@ -1289,27 +1362,27 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { soci::rowset messages = (inSession->prepare << "SELECT * FROM history"); try { for (const auto &message : messages) { - const int direction = message.get(LEGACY_MESSAGE_COL_DIRECTION); + const int direction = message.get(LegacyMessageColDirection); if (direction != 0 && direction != 1) { lWarning() << "Unable to import legacy message with invalid direction."; continue; } const int &state = message.get( - LEGACY_MESSAGE_COL_STATE, static_cast(ChatMessage::State::Displayed) + LegacyMessageColState, int(ChatMessage::State::Displayed) ); - if (state < 0 || state > static_cast(ChatMessage::State::Displayed)) { + if (state < 0 || state > int(ChatMessage::State::Displayed)) { lWarning() << "Unable to import legacy message with invalid state."; continue; } - const tm &creationTime = Utils::getTimeTAsTm(message.get(LEGACY_MESSAGE_COL_DATE, 0)); + const tm &creationTime = Utils::getTimeTAsTm(message.get(LegacyMessageColDate, 0)); bool isNull; - getValueFromRow(message, LEGACY_MESSAGE_COL_URL, isNull); + getValueFromRow(message, LegacyMessageColUrl, isNull); - const int &contentId = message.get(LEGACY_MESSAGE_COL_CONTENT_ID, -1); - ContentType contentType(message.get(LEGACY_MESSAGE_COL_CONTENT_TYPE, "")); + const int &contentId = message.get(LegacyMessageColContentId, -1); + ContentType contentType(message.get(LegacyMessageColContentType, "")); if (!contentType.isValid()) contentType = contentId != -1 ? ContentType::FileTransfer @@ -1319,7 +1392,7 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { continue; } - const string &text = getValueFromRow(message, LEGACY_MESSAGE_COL_TEXT, isNull); + const string &text = getValueFromRow(message, LegacyMessageColText, isNull); Content content; content.setContentType(contentType); @@ -1335,7 +1408,7 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { continue; } - const string appData = getValueFromRow(message, LEGACY_MESSAGE_COL_APP_DATA, isNull); + const string appData = getValueFromRow(message, LegacyMessageColAppData, isNull); if (isNull) { lWarning() << "Unable to import legacy file message without app data."; continue; @@ -1345,19 +1418,19 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { } soci::session *session = dbSession.getBackendSession(); - const int &eventType = static_cast(EventLog::Type::ConferenceChatMessage); + const int &eventType = int(EventLog::Type::ConferenceChatMessage); *session << "INSERT INTO event (type, creation_time) VALUES (:type, :creationTime)", soci::use(eventType), soci::use(creationTime); const long long &eventId = dbSession.getLastInsertId(); - const long long &localSipAddressId = insertSipAddress(message.get(LEGACY_MESSAGE_COL_LOCAL_ADDRESS)); - const long long &remoteSipAddressId = insertSipAddress(message.get(LEGACY_MESSAGE_COL_REMOTE_ADDRESS)); + const long long &localSipAddressId = insertSipAddress(message.get(LegacyMessageColLocalAddress)); + const long long &remoteSipAddressId = insertSipAddress(message.get(LegacyMessageColRemoteAddress)); const long long &chatRoomId = insertOrUpdateImportedBasicChatRoom( remoteSipAddressId, localSipAddressId, creationTime ); - const int &isSecured = message.get(LEGACY_MESSAGE_COL_IS_SECURED, 0); + const int &isSecured = message.get(LegacyMessageColIsSecured, 0); *session << "INSERT INTO conference_event (event_id, chat_room_id)" " VALUES (:eventId, :chatRoomId)", soci::use(eventId), soci::use(chatRoomId); @@ -1375,7 +1448,7 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { insertContent(eventId, content); insertChatRoomParticipant(chatRoomId, remoteSipAddressId, false); - if (state != static_cast(ChatMessage::State::Displayed)) + if (state != int(ChatMessage::State::Displayed)) insertChatMessageParticipant(eventId, remoteSipAddressId, state); } @@ -1674,32 +1747,6 @@ void MainDb::init () { " ON DELETE CASCADE" ") " + charset; - // Trigger to delete participant_message cache entries. - // TODO: Fix me in the future. (Problem on Mysql backend.) - #if 0 - string displayedId = Utils::toString(static_cast(ChatMessage::State::Displayed)); - string participantMessageDeleter = - "CREATE TRIGGER IF NOT EXISTS chat_message_participant_deleter" - " AFTER UPDATE OF state ON chat_message_participant FOR EACH ROW" - " WHEN NEW.state = "; - participantMessageDeleter += displayedId; - participantMessageDeleter += " AND (SELECT COUNT(*) FROM (" - " SELECT state FROM chat_message_participant WHERE" - " NEW.event_id = chat_message_participant.event_id" - " AND state <> "; - participantMessageDeleter += displayedId; - participantMessageDeleter += " LIMIT 1" - " )) = 0" - " BEGIN" - " DELETE FROM chat_message_participant WHERE NEW.event_id = chat_message_participant.event_id;" - " UPDATE conference_chat_message_event SET state = "; - participantMessageDeleter += displayedId; - participantMessageDeleter += " WHERE event_id = NEW.event_id;" - " END"; - - *session << participantMessageDeleter; - #endif - *session << "CREATE TABLE IF NOT EXISTS friends_list (" " id" + primaryKeyStr("INT UNSIGNED") + "," @@ -1752,8 +1799,30 @@ void MainDb::init () { " version INT UNSIGNED NOT NULL" ") " + charset; - d->updateModuleVersion("events", DB_MODULE_VERSION_EVENTS); - d->updateModuleVersion("friends", DB_MODULE_VERSION_FRIENDS); + if (getBackend() == Backend::Mysql) + *session << + "CREATE TRIGGER IF NOT EXISTS chat_message_participant_deleter" + " AFTER UPDATE ON conference_chat_message_event FOR EACH ROW" + " BEGIN" + " IF NEW.state = " + Utils::toString(int(ChatMessage::State::Displayed)) + " THEN" + " DELETE FROM chat_message_participant WHERE event_id = NEW.event_id;" + " END IF;" + " END "; + else + *session << + "CREATE TRIGGER IF NOT EXISTS chat_message_participant_deleter" + " AFTER UPDATE OF state ON conference_chat_message_event FOR EACH ROW" + " WHEN NEW.state = " + Utils::toString(int(ChatMessage::State::Displayed)) + + " BEGIN" + " DELETE FROM chat_message_participant WHERE event_id = NEW.event_id;" + " END "; + + d->updateSchema(); + + d->updateModuleVersion("events", ModuleVersionEvents); + d->updateModuleVersion("friends", ModuleVersionFriends); + + d->initStatements(); } bool MainDb::addEvent (const shared_ptr &eventLog) { @@ -1890,7 +1959,7 @@ bool MainDb::deleteEvent (const shared_ptr &eventLog) { int MainDb::getEventCount (FilterMask mask) const { string query = "SELECT COUNT(*) FROM event" + - buildSqlEventFilter({ ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter }, mask); + buildSqlEventFilter({ ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter, ConferenceInfoNoDeviceFilter }, mask); DurationLogger durationLogger( "Get events count with mask=" + Utils::toString(mask) + "." @@ -1944,7 +2013,7 @@ shared_ptr MainDb::getEventFromKey (const MainDbKey &dbKey) { return d->selectGenericConferenceEvent( storageId, - static_cast(type), + EventLog::Type(type), Utils::getTmAsTimeT(creationTime), ChatRoomId(IdentityAddress(peerSipAddress), IdentityAddress(localSipAddress)) ); @@ -1979,12 +2048,12 @@ list> MainDb::getConferenceNotifiedEvents ( list> events; soci::rowset rows = (session->prepare << query, soci::use(dbChatRoomId), soci::use(lastNotifyId)); for (const auto &row : rows) { - long long eventId = d->resolveId(row, 0); + long long eventId = d->dbSession.resolveId(row, 0); shared_ptr eventLog = d->getEventFromCache(eventId); events.push_back(eventLog ? eventLog : d->selectGenericConferenceEvent( eventId, - static_cast(row.get(1)), + EventLog::Type(row.get(1)), Utils::getTmAsTimeT(row.get(2)), chatRoomId )); @@ -2031,8 +2100,8 @@ int MainDb::getUnreadChatMessageCount (const ChatRoomId &chatRoomId) const { " SELECT event_id FROM conference_event WHERE chat_room_id = :chatRoomId" ") AND"; - query += " direction = " + Utils::toString(static_cast(ChatMessage::Direction::Incoming)) + - + " AND state <> " + Utils::toString(static_cast(ChatMessage::State::Displayed)); + query += " direction = " + Utils::toString(int(ChatMessage::Direction::Incoming)) + + + " AND state <> " + Utils::toString(int(ChatMessage::State::Displayed)); DurationLogger durationLogger( "Get unread chat messages count of: (peer=" + chatRoomId.getPeerAddress().asString() + @@ -2063,13 +2132,13 @@ void MainDb::markChatMessagesAsRead (const ChatRoomId &chatRoomId) const { return; string query = "UPDATE conference_chat_message_event" - " SET state = " + Utils::toString(static_cast(ChatMessage::State::Displayed)) + " "; + " SET state = " + Utils::toString(int(ChatMessage::State::Displayed)) + " "; query += "WHERE"; if (chatRoomId.isValid()) query += " event_id IN (" " SELECT event_id FROM conference_event WHERE chat_room_id = :chatRoomId" ") AND"; - query += " direction = " + Utils::toString(static_cast(ChatMessage::Direction::Incoming)); + query += " direction = " + Utils::toString(int(ChatMessage::Direction::Incoming)); DurationLogger durationLogger( "Mark chat messages as read of: (peer=" + chatRoomId.getPeerAddress().asString() + @@ -2089,8 +2158,6 @@ void MainDb::markChatMessagesAsRead (const ChatRoomId &chatRoomId) const { *session << query, soci::use(dbChatRoomId); tr.commit(); } - - return true; }; } @@ -2102,8 +2169,8 @@ list> MainDb::getUnreadChatMessages (const ChatRoomId &c if (chatRoomId.isValid()) query += " chat_room_id = :chatRoomId AND "; query += " conference_event.event_id = conference_chat_message_event.event_id" - " AND direction = " + Utils::toString(static_cast(ChatMessage::Direction::Incoming)) + - " AND state <> " + Utils::toString(static_cast(ChatMessage::State::Displayed)) + + " AND direction = " + Utils::toString(int(ChatMessage::Direction::Incoming)) + + " AND state <> " + Utils::toString(int(ChatMessage::State::Displayed)) + ")"; DurationLogger durationLogger( @@ -2127,7 +2194,7 @@ list> MainDb::getUnreadChatMessages (const ChatRoomId &c : (session->prepare << query); for (const auto &row : rows) { - long long eventId = d->resolveId(row, 0); + long long eventId = d->dbSession.resolveId(row, 0); shared_ptr event = d->getEventFromCache(eventId); if (!event) @@ -2146,6 +2213,75 @@ list> MainDb::getUnreadChatMessages (const ChatRoomId &c }; } +list MainDb::getChatMessageParticipantStates (const shared_ptr &eventLog) const { + return L_SAFE_TRANSACTION { + L_D(); + + soci::session *session = d->dbSession.getBackendSession(); + + const EventLogPrivate *dEventLog = eventLog->getPrivate(); + MainDbKeyPrivate *dEventKey = static_cast(dEventLog->dbKey).getPrivate(); + const long long &eventId = dEventKey->storageId; + list states; + unsigned int state; + + soci::statement statement = (session->prepare + << "SELECT state FROM chat_message_participant WHERE event_id = :eventId", + soci::into(state), soci::use(eventId) + ); + statement.execute(); + while (statement.fetch()) + states.push_back(static_cast(state)); + + return states; + }; +} + +ChatMessage::State MainDb::getChatMessageParticipantState ( + const shared_ptr &eventLog, + const IdentityAddress &participantAddress +) const { + return L_SAFE_TRANSACTION { + L_D(); + + soci::session *session = d->dbSession.getBackendSession(); + + const EventLogPrivate *dEventLog = eventLog->getPrivate(); + MainDbKeyPrivate *dEventKey = static_cast(dEventLog->dbKey).getPrivate(); + const long long &eventId = dEventKey->storageId; + const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString()); + unsigned int state; + + *session << "SELECT state FROM chat_message_participant" + " WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId", + soci::into(state), soci::use(eventId), soci::use(participantSipAddressId); + + return static_cast(state); + }; +} + +void MainDb::setChatMessageParticipantState ( + const shared_ptr &eventLog, + const IdentityAddress &participantAddress, + ChatMessage::State state +) { + L_SAFE_TRANSACTION { + L_D(); + + soci::session *session = d->dbSession.getBackendSession(); + + const EventLogPrivate *dEventLog = eventLog->getPrivate(); + MainDbKeyPrivate *dEventKey = static_cast(dEventLog->dbKey).getPrivate(); + const long long &eventId = dEventKey->storageId; + const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString()); + int stateInt = static_cast(state); + + *session << "UPDATE chat_message_participant SET state = :state" + " WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId", + soci::use(stateInt), soci::use(eventId), soci::use(participantSipAddressId); + }; +} + shared_ptr MainDb::getLastChatMessage (const ChatRoomId &chatRoomId) const { list> chatList = getHistory(chatRoomId, 1, Filter::ConferenceChatMessageFilter); if (chatList.empty()) @@ -2181,13 +2317,13 @@ list> MainDb::findChatMessages ( const long long &dbChatRoomId = d->selectChatRoomId(chatRoomId); soci::rowset rows = (session->prepare << query, soci::use(imdnMessageId), soci::use(dbChatRoomId)); for (const auto &row : rows) { - long long eventId = d->resolveId(row, 0); + long long eventId = d->dbSession.resolveId(row, 0); shared_ptr event = d->getEventFromCache(eventId); if (!event) event = d->selectGenericConferenceEvent( eventId, - static_cast(row.get(1)), + EventLog::Type(row.get(1)), Utils::getTmAsTimeT(row.get(2)), chatRoomId ); @@ -2229,7 +2365,7 @@ list> MainDb::getHistoryRange ( " SELECT event_id FROM conference_event WHERE chat_room_id = :chatRoomId" " )"; query += buildSqlEventFilter({ - ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter + ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter, ConferenceInfoNoDeviceFilter }, mask, "AND"); query += " ORDER BY creation_time DESC"; @@ -2256,13 +2392,13 @@ list> MainDb::getHistoryRange ( const long long &dbChatRoomId = d->selectChatRoomId(chatRoomId); soci::rowset rows = (session->prepare << query, soci::use(dbChatRoomId)); for (const auto &row : rows) { - long long eventId = d->resolveId(row, 0); + long long eventId = d->dbSession.resolveId(row, 0); shared_ptr event = d->getEventFromCache(eventId); if (!event) event = d->selectGenericConferenceEvent( eventId, - static_cast(row.get(1)), + EventLog::Type(row.get(1)), Utils::getTmAsTimeT(row.get(2)), chatRoomId ); @@ -2281,7 +2417,7 @@ int MainDb::getHistorySize (const ChatRoomId &chatRoomId, FilterMask mask) const const string query = "SELECT COUNT(*) FROM event, conference_event" " WHERE chat_room_id = :chatRoomId" " AND event_id = event.id" + buildSqlEventFilter({ - ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter + ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter, ConferenceInfoNoDeviceFilter }, mask, "AND"); return L_SAFE_TRANSACTION { @@ -2301,7 +2437,7 @@ int MainDb::getHistorySize (const ChatRoomId &chatRoomId, FilterMask mask) const void MainDb::cleanHistory (const ChatRoomId &chatRoomId, FilterMask mask) { const string query = "SELECT event_id FROM conference_event WHERE chat_room_id = :chatRoomId" + buildSqlEventFilter({ - ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter + ConferenceCallFilter, ConferenceChatMessageFilter, ConferenceInfoFilter, ConferenceInfoNoDeviceFilter }, mask); DurationLogger durationLogger( @@ -2322,8 +2458,6 @@ void MainDb::cleanHistory (const ChatRoomId &chatRoomId, FilterMask mask) { *session << "DELETE FROM event WHERE id IN (" + query + ")", soci::use(dbChatRoomId); tr.commit(); - - return true; }; } @@ -2373,7 +2507,7 @@ list> MainDb::getChatRooms () const { } else if (capabilities & ChatRoom::CapabilitiesMask(ChatRoom::Capabilities::Conference)) { list> participants; - const long long &dbChatRoomId = d->resolveId(row, 0); + const long long &dbChatRoomId = d->dbSession.resolveId(row, 0); static const string query = "SELECT chat_room_participant.id, sip_address.value, is_admin" " FROM sip_address, chat_room, chat_room_participant" " WHERE chat_room.id = :chatRoomId" @@ -2390,14 +2524,16 @@ list> MainDb::getChatRooms () const { // Fetch devices. { - const long long &participantId = d->resolveId(row, 0); - static const string query = "SELECT sip_address.value FROM chat_room_participant_device, sip_address" + const long long &participantId = d->dbSession.resolveId(row, 0); + static const string query = "SELECT sip_address.value, state FROM chat_room_participant_device, sip_address" " WHERE chat_room_participant_id = :participantId" " AND participant_device_sip_address_id = sip_address.id"; soci::rowset rows = (session->prepare << query, soci::use(participantId)); - for (const auto &row : rows) - dParticipant->addDevice(IdentityAddress(row.get(0))); + for (const auto &row : rows) { + shared_ptr device = dParticipant->addDevice(IdentityAddress(row.get(0))); + device->setState(ParticipantDevice::State(static_cast(row.get(1, 0)))); + } } if (participant->getAddress() == chatRoomId.getLocalAddress().getAddressWithoutGruu()) @@ -2477,8 +2613,6 @@ void MainDb::insertChatRoom (const shared_ptr &chatRoom) { d->insertChatRoom(chatRoom); tr.commit(); - - return true; }; } @@ -2503,8 +2637,6 @@ void MainDb::deleteChatRoom (const ChatRoomId &chatRoomId) { *session << "DELETE FROM chat_room WHERE id = :chatRoomId", soci::use(dbChatRoomId); tr.commit(); - - return true; }; } @@ -2530,19 +2662,6 @@ void MainDb::migrateBasicToClientGroupChatRoom ( const long long &localSipAddressId = d->insertSipAddress(newChatRoomId.getLocalAddress().asString()); const int &capabilities = clientGroupChatRoom->getCapabilities(); - { - shared_ptr buggyChatRoom = getCore()->findChatRoom(newChatRoomId); - if (buggyChatRoom) { - lError() << "Chat room was found with the same chat room id of a new migrated ClientGroupChatRoom!!!"; - AbstractChatRoom::CapabilitiesMask capabilities = buggyChatRoom->getCapabilities(); - if (capabilities & AbstractChatRoom::Capabilities::Basic) { - lError() << "Delete invalid basic chat room..."; - Core::deleteChatRoom(buggyChatRoom); - } else - lError() << "Unable to delete invalid chat room with capabilities: " << capabilities << "."; - } - } - *session << "UPDATE chat_room" " SET capabilities = :capabilities," " peer_sip_address_id = :peerSipAddressId," @@ -2570,8 +2689,6 @@ void MainDb::migrateBasicToClientGroupChatRoom ( } tr.commit(); - - return true; }; } @@ -2666,8 +2783,6 @@ void MainDb::insertOneToOneConferenceChatRoom (const shared_ptr(ChatRoom::Capabilities::Migratable); + capabilities |= int(ChatRoom::Capabilities::Migratable); else - capabilities &= ~static_cast(ChatRoom::Capabilities::Migratable); + capabilities &= ~int(ChatRoom::Capabilities::Migratable); *session << "UPDATE chat_room SET capabilities = :capabilities WHERE id = :chatRoomId", soci::use(capabilities), soci::use(dbChatRoomId); tr.commit(); + }; +} - return true; +void MainDb::updateChatRoomParticipantDevice (const shared_ptr &chatRoom, const shared_ptr &device) { + L_SAFE_TRANSACTION { + L_D(); + + soci::session *session = d->dbSession.getBackendSession(); + soci::transaction tr(*session); + + const long long &dbChatRoomId = d->selectChatRoomId(chatRoom->getChatRoomId()); + const long long &participantSipAddressId = d->selectSipAddressId(device->getParticipant()->getAddress().asString()); + const long long &participantId = d->selectChatRoomParticipantId(dbChatRoomId, participantSipAddressId); + const long long &participantSipDeviceAddressId = d->selectSipAddressId(device->getAddress().asString()); + unsigned int stateInt = static_cast(device->getState()); + *session << "UPDATE chat_room_participant_device SET state = :state" + " WHERE chat_room_participant_id = :participantId AND participant_device_sip_address_id = :participantSipDeviceAddressId", + soci::use(stateInt), soci::use(participantId), soci::use(participantSipDeviceAddressId); + + tr.commit(); }; } @@ -2714,12 +2847,10 @@ bool MainDb::import (Backend, const string ¶meters) { // TODO: Remove condition after cpp migration in friends/friends list. if (false) d->importLegacyFriends(inDbSession); - return true; }; L_SAFE_TRANSACTION { d->importLegacyHistory(inDbSession); - return true; }; return true; diff --git a/src/db/main-db.h b/src/db/main-db.h index f3301b007..6b6c1ee90 100644 --- a/src/db/main-db.h +++ b/src/db/main-db.h @@ -25,6 +25,7 @@ #include "linphone/utils/enum-mask.h" #include "abstract/abstract-db.h" +#include "chat/chat-message/chat-message.h" #include "chat/chat-room/chat-room-id.h" #include "core/core-accessor.h" @@ -38,6 +39,7 @@ class Core; class EventLog; class MainDbKey; class MainDbPrivate; +class ParticipantDevice; class MainDb : public AbstractDb, public CoreAccessor { friend class MainDbChatMessageKey; @@ -48,7 +50,8 @@ public: NoFilter = 0x0, ConferenceCallFilter = 0x1, ConferenceChatMessageFilter = 0x2, - ConferenceInfoFilter = 0x4 + ConferenceInfoFilter = 0x4, + ConferenceInfoNoDeviceFilter = 0x6 }; typedef EnumMask FilterMask; @@ -84,6 +87,17 @@ public: void markChatMessagesAsRead (const ChatRoomId &chatRoomId = ChatRoomId()) const; std::list> getUnreadChatMessages (const ChatRoomId &chatRoomId = ChatRoomId()) const; + std::list getChatMessageParticipantStates (const std::shared_ptr &eventLog) const; + ChatMessage::State getChatMessageParticipantState ( + const std::shared_ptr &eventLog, + const IdentityAddress &participantAddress + ) const; + void setChatMessageParticipantState ( + const std::shared_ptr &eventLog, + const IdentityAddress &participantAddress, + ChatMessage::State state + ); + std::shared_ptr getLastChatMessage (const ChatRoomId &chatRoomId) const; std::list> findChatMessages ( @@ -135,6 +149,11 @@ public: ) const; void insertOneToOneConferenceChatRoom (const std::shared_ptr &chatRoom); + void updateChatRoomParticipantDevice ( + const std::shared_ptr &chatRoom, + const std::shared_ptr &device + ); + // --------------------------------------------------------------------------- // Other. // --------------------------------------------------------------------------- diff --git a/src/db/session/db-session.cpp b/src/db/session/db-session.cpp index 6c2fda1cc..7e4f141d1 100644 --- a/src/db/session/db-session.cpp +++ b/src/db/session/db-session.cpp @@ -34,7 +34,7 @@ DbSession::DbSession (const string &uri) : DbSession() { #ifdef SOCI_ENABLED try { L_D(); - d->backendSession = make_unique(uri); + d->backendSession = makeUnique(uri); d->backend = !uri.find("mysql") ? DbSessionPrivate::Backend::Mysql : DbSessionPrivate::Backend::Sqlite3; } catch (const exception &e) { lWarning() << "Unable to build db session with uri: " << e.what(); @@ -51,12 +51,14 @@ DbSession::operator bool () const { L_USE_DEFAULT_CLONABLE_OBJECT_SHARED_IMPL(DbSession); -#ifdef SOCI_ENABLED - soci::session *DbSession::getBackendSession () const { +soci::session *DbSession::getBackendSession () const { + #ifdef SOCI_ENABLED L_D(); return d->backendSession.get(); - } -#endif // ifdef SOCI_ENABLED + #else + return nullptr; + #endif // ifdef SOCI_ENABLED +} string DbSession::primaryKeyStr (const string &type) const { L_D(); @@ -195,10 +197,31 @@ bool DbSession::checkTableExists (const string &table) const { return false; } L_ASSERT(false); + #else + (void)table; #endif // ifdef SOCI_ENABLED - (void)table; return false; } +long long DbSession::resolveId (const soci::row &row, int col) const { + #ifdef SOCI_ENABLED + L_D(); + switch (d->backend) { + case DbSessionPrivate::Backend::Mysql: + return static_cast(row.get(0)); + case DbSessionPrivate::Backend::Sqlite3: + return static_cast(row.get(0)); + case DbSessionPrivate::Backend::None: + return 0; + } + L_ASSERT(false); + #else + (void)row; + (void)col; + #endif // ifdef SOCI_ENABLED + + return 0; +} + LINPHONE_END_NAMESPACE diff --git a/src/db/session/db-session.h b/src/db/session/db-session.h index 2acfdfc73..1b27635f6 100644 --- a/src/db/session/db-session.h +++ b/src/db/session/db-session.h @@ -28,6 +28,13 @@ // ============================================================================= +#ifndef SOCI_ENABLED + namespace soci { + class row; + class session; + } +#endif // ifndef SOCI_ENABLED + LINPHONE_BEGIN_NAMESPACE class DbSessionPrivate; @@ -44,9 +51,7 @@ public: operator bool () const; - #ifdef SOCI_ENABLED - soci::session *getBackendSession () const; - #endif // ifdef SOCI_ENABLED + soci::session *getBackendSession () const; std::string primaryKeyStr (const std::string &type = "INT") const; std::string primaryKeyRefStr (const std::string &type = "INT") const; @@ -62,6 +67,8 @@ public: bool checkTableExists (const std::string &table) const; + long long resolveId (const soci::row &row, int col) const; + private: L_DECLARE_PRIVATE(DbSession); }; diff --git a/src/logger/logger.h b/src/logger/logger.h index c25438c24..d3729b582 100644 --- a/src/logger/logger.h +++ b/src/logger/logger.h @@ -54,7 +54,7 @@ class DurationLoggerPrivate; class DurationLogger : public BaseObject { public: - DurationLogger (const std::string &label, Logger::Level level = Logger::Debug); + DurationLogger (const std::string &label, Logger::Level level = Logger::Info); ~DurationLogger (); private: diff --git a/src/utils/general.cpp b/src/utils/general.cpp index 16a956214..73ac6f1b8 100644 --- a/src/utils/general.cpp +++ b/src/utils/general.cpp @@ -25,7 +25,7 @@ LINPHONE_BEGIN_NAMESPACE -void l_assert (const char *condition, const char *file, int line) { +void lAssert (const char *condition, const char *file, int line) { lFatal() << "ASSERT: " << condition << " in " << file << " line " << line << "."; } diff --git a/tester/db/linphone.db b/tester/db/linphone.db index 8e7f0f8a8..ae7fe37ab 100644 Binary files a/tester/db/linphone.db and b/tester/db/linphone.db differ diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index f40fe50e6..211494d10 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -888,6 +888,55 @@ static void group_chat_room_remove_admin (void) { linphone_core_manager_destroy(laure); } +static void group_chat_room_admin_creator_leaves_the_room (void) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + LinphoneCoreManager *laure = linphone_core_manager_create("laure_tcp_rc"); + bctbx_list_t *coresManagerList = NULL; + bctbx_list_t *participantsAddresses = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + coresManagerList = bctbx_list_append(coresManagerList, laure); + 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))); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(laure->lc))); + stats initialMarieStats = marie->stat; + stats initialPaulineStats = pauline->stat; + stats initialLaureStats = laure->stat; + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1); + + const LinphoneAddress *confAddr = 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, 2, 0); + + // Check that the chat room is correctly created on Laure's side and that the participants are added + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure, &initialLaureStats, confAddr, initialSubject, 2, 0); + + // Marie leaves the room + linphone_chat_room_leave(marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneChatRoomStateTerminated, initialMarieStats.number_of_LinphoneChatRoomStateTerminated + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participants_removed, initialPaulineStats.number_of_participants_removed + 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_participants_removed, initialLaureStats.number_of_participants_removed + 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participant_admin_statuses_changed, initialPaulineStats.number_of_participant_admin_statuses_changed + 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_participant_admin_statuses_changed, initialLaureStats.number_of_participant_admin_statuses_changed + 1, 1000)); + BC_ASSERT_TRUE(linphone_participant_is_admin(linphone_chat_room_get_me(laureCr))); + + // Clean db from chat room + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_core_manager_delete_chat_room(laure, laureCr, coresList); + linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(laure); +} static void group_chat_room_change_subject (void) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); @@ -1304,7 +1353,7 @@ static void group_chat_room_create_room_with_disconnected_friends_and_initial_me group_chat_room_create_room_with_disconnected_friends_base(TRUE); } -static void group_chat_room_reinvited_after_removed_base (bool_t offline_when_removed) { +static void group_chat_room_reinvited_after_removed_base (bool_t offline_when_removed, bool_t offline_when_reinvited) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); LinphoneCoreManager *laure = linphone_core_manager_create("laure_tcp_rc"); @@ -1345,35 +1394,50 @@ static void group_chat_room_reinvited_after_removed_base (bool_t offline_when_re // Marie removes Laure from the chat room LinphoneParticipant *laureParticipant = linphone_chat_room_find_participant(marieCr, laureAddr); - linphone_address_unref(laureAddr); BC_ASSERT_PTR_NOT_NULL(laureParticipant); linphone_chat_room_remove_participant(marieCr, laureParticipant); BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_participants_removed, initialMarieStats.number_of_participants_removed + 1, 1000)); BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participants_removed, initialPaulineStats.number_of_participants_removed + 1, 1000)); - if (offline_when_removed) { + if (offline_when_removed && !offline_when_reinvited) { linphone_core_manager_configure(laure); lp_config_set_string(linphone_core_get_config(laure->lc), "misc", "uuid", savedLaureUuid); bctbx_free(savedLaureUuid); bctbx_list_t *tmpCoresManagerList = bctbx_list_append(NULL, laure); init_core_for_conference(tmpCoresManagerList); bctbx_list_free(tmpCoresManagerList); + initialLaureStats = laure->stat; linphone_core_manager_start(laure, TRUE); coresList = bctbx_list_append(coresList, laure->lc); coresManagerList = bctbx_list_append(coresManagerList, laure); - initialLaureStats = laure->stat; } - BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateTerminated, initialLaureStats.number_of_LinphoneChatRoomStateTerminated + 1, 3000)); + if (!offline_when_reinvited) + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateTerminated, initialLaureStats.number_of_LinphoneChatRoomStateTerminated + 1, 3000)); // Marie adds Laure to the chat room - participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(laure->lc))); + participantsAddresses = bctbx_list_append(participantsAddresses, laureAddr); linphone_chat_room_add_participants(marieCr, participantsAddresses); bctbx_list_free_with_data(participantsAddresses, (bctbx_list_free_func)linphone_address_unref); participantsAddresses = NULL; - BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreationPending, initialLaureStats.number_of_LinphoneChatRoomStateCreationPending + 1, 5000)); - BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreated, initialLaureStats.number_of_LinphoneChatRoomStateCreated + 1, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_participants_added, initialMarieStats.number_of_participants_added + 1, 1000)); BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_participants_added, initialPaulineStats.number_of_participants_added + 1, 1000)); + if (offline_when_reinvited) { + linphone_core_manager_configure(laure); + lp_config_set_string(linphone_core_get_config(laure->lc), "misc", "uuid", savedLaureUuid); + bctbx_free(savedLaureUuid); + bctbx_list_t *tmpCoresManagerList = bctbx_list_append(NULL, laure); + init_core_for_conference(tmpCoresManagerList); + bctbx_list_free(tmpCoresManagerList); + initialLaureStats = laure->stat; + linphone_core_manager_start(laure, TRUE); + coresList = bctbx_list_append(coresList, laure->lc); + coresManagerList = bctbx_list_append(coresManagerList, laure); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreationPending, initialLaureStats.number_of_LinphoneChatRoomStateCreationPending + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreated, initialLaureStats.number_of_LinphoneChatRoomStateCreated + 2, 5000)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreationPending, initialLaureStats.number_of_LinphoneChatRoomStateCreationPending + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure->stat.number_of_LinphoneChatRoomStateCreated, initialLaureStats.number_of_LinphoneChatRoomStateCreated + 1, 5000)); + } BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); laureAddr = linphone_address_new(linphone_core_get_device_identity(laure->lc)); @@ -1397,11 +1461,15 @@ static void group_chat_room_reinvited_after_removed_base (bool_t offline_when_re } static void group_chat_room_reinvited_after_removed (void) { - group_chat_room_reinvited_after_removed_base(FALSE); + group_chat_room_reinvited_after_removed_base(FALSE, FALSE); } static void group_chat_room_reinvited_after_removed_while_offline (void) { - group_chat_room_reinvited_after_removed_base(TRUE); + group_chat_room_reinvited_after_removed_base(TRUE, FALSE); +} + +static void group_chat_room_reinvited_after_removed_while_offline_2 (void) { + group_chat_room_reinvited_after_removed_base(TRUE, TRUE); } static void group_chat_room_notify_after_disconnection (void) { @@ -2546,6 +2614,79 @@ static void group_chat_room_new_unique_one_to_one_chat_room_after_both_participa linphone_core_manager_destroy(pauline); } +static void imdn_for_group_chat_room (void) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + LinphoneCoreManager *chloe = linphone_core_manager_create("chloe_rc"); + bctbx_list_t *coresManagerList = NULL; + bctbx_list_t *participantsAddresses = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + coresManagerList = bctbx_list_append(coresManagerList, chloe); + 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))); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(chloe->lc))); + stats initialMarieStats = marie->stat; + stats initialPaulineStats = pauline->stat; + stats initialChloeStats = chloe->stat; + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie->lc)); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline->lc)); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(chloe->lc)); + linphone_config_set_bool(linphone_core_get_config(marie->lc), "misc", "enable_simple_group_chat_message_state", FALSE); + linphone_config_set_bool(linphone_core_get_config(pauline->lc), "misc", "enable_simple_group_chat_message_state", FALSE); + linphone_config_set_bool(linphone_core_get_config(chloe->lc), "misc", "enable_simple_group_chat_message_state", FALSE); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = create_chat_room_client_side(coresList, marie, &initialMarieStats, participantsAddresses, initialSubject, -1); + const LinphoneAddress *confAddr = 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, 2, 0); + + // Check that the chat room is correctly created on Chloe's side and that the participants are added + LinphoneChatRoom *chloeCr = check_creation_chat_room_client_side(coresList, chloe, &initialChloeStats, confAddr, initialSubject, 2, 0); + + // Chloe begins composing a message + const char *chloeMessage = "Hello"; + _send_message(chloeCr, chloeMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageReceived, initialMarieStats.number_of_LinphoneMessageReceived + 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, initialPaulineStats.number_of_LinphoneMessageReceived + 1, 10000)); + LinphoneChatMessage *marieLastMsg = marie->stat.last_received_chat_message; + if (!BC_ASSERT_PTR_NOT_NULL(marieLastMsg)) + goto end; + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marieLastMsg), chloeMessage); + LinphoneAddress *chloeAddr = linphone_address_new(linphone_core_get_identity(chloe->lc)); + BC_ASSERT_TRUE(linphone_address_weak_equal(chloeAddr, linphone_chat_message_get_from_address(marieLastMsg))); + linphone_address_unref(chloeAddr); + + // Check that the message has been delivered to Marie and Pauline + BC_ASSERT_TRUE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDeliveredToUser, initialChloeStats.number_of_LinphoneMessageDeliveredToUser + 1, 10000)); + + // Marie marks the message as read, check that the state is not yet displayed on Chloe's side + linphone_chat_room_mark_as_read(marieCr); + BC_ASSERT_FALSE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDisplayed, initialChloeStats.number_of_LinphoneMessageDisplayed + 1, 1000)); + + // Pauline also marks the message as read, check that the state is now displayed on Chloe's side + linphone_chat_room_mark_as_read(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &chloe->stat.number_of_LinphoneMessageDisplayed, initialChloeStats.number_of_LinphoneMessageDisplayed + 1, 1000)); + +end: + // Clean db from chat room + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_core_manager_delete_chat_room(chloe, chloeCr, coresList); + linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(chloe); +} + 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"), @@ -2555,6 +2696,7 @@ test_t group_chat_tests[] = { TEST_TWO_TAGS("Add admin lately notified", group_chat_room_add_admin_lately_notified, "Server", "LeaksMemory"), TEST_TWO_TAGS("Add admin with a non admin", group_chat_room_add_admin_non_admin, "Server", "LeaksMemory"), TEST_TWO_TAGS("Remove admin", group_chat_room_remove_admin, "Server", "LeaksMemory"), + TEST_TWO_TAGS("Admin creator leaves the room", group_chat_room_admin_creator_leaves_the_room, "Server", "LeaksMemory"), TEST_TWO_TAGS("Change subject", group_chat_room_change_subject, "Server", "LeaksMemory"), TEST_TWO_TAGS("Change subject with a non admin", group_chat_room_change_subject_non_admin, "Server", "LeaksMemory"), TEST_TWO_TAGS("Remove participant", group_chat_room_remove_participant, "Server", "LeaksMemory"), @@ -2565,6 +2707,7 @@ test_t group_chat_tests[] = { TEST_TWO_TAGS("Create chat room with disconnected friends and initial message", group_chat_room_create_room_with_disconnected_friends_and_initial_message, "Server", "LeaksMemory"), TEST_TWO_TAGS("Reinvited after removed from group chat room", group_chat_room_reinvited_after_removed, "Server", "LeaksMemory"), TEST_TWO_TAGS("Reinvited after removed from group chat room while offline", group_chat_room_reinvited_after_removed_while_offline, "Server", "LeaksMemory"), + TEST_TWO_TAGS("Reinvited after removed from group chat room while offline 2", group_chat_room_reinvited_after_removed_while_offline_2, "Server", "LeaksMemory"), TEST_TWO_TAGS("Notify after disconnection", group_chat_room_notify_after_disconnection, "Server", "LeaksMemory"), TEST_TWO_TAGS("Send refer to all participants devices", group_chat_room_send_refer_to_all_devices, "Server", "LeaksMemory"), // TODO: Use when we support adding a new device in created conf @@ -2581,7 +2724,8 @@ test_t group_chat_tests[] = { TEST_TWO_TAGS("Unique one-to-one chatroom", group_chat_room_unique_one_to_one_chat_room, "Server", "LeaksMemory"), TEST_TWO_TAGS("Unique one-to-one chatroom recreated from message", group_chat_room_unique_one_to_one_chat_room_recreated_from_message, "Server", "LeaksMemory"), TEST_TWO_TAGS("Unique one-to-one chatroom recreated from message with app restart", group_chat_room_unique_one_to_one_chat_room_recreated_from_message_with_app_restart, "Server", "LeaksMemory"), - TEST_TWO_TAGS("New unique one-to-one chatroom after both participants left", group_chat_room_new_unique_one_to_one_chat_room_after_both_participants_left, "Server", "LeaksMemory") + TEST_TWO_TAGS("New unique one-to-one chatroom after both participants left", group_chat_room_new_unique_one_to_one_chat_room_after_both_participants_left, "Server", "LeaksMemory"), + TEST_TWO_TAGS("IMDN for group chat room", imdn_for_group_chat_room, "Server", "LeaksMemory") }; test_suite_t group_chat_test_suite = { diff --git a/tester/message_tester.c b/tester/message_tester.c index cc62e98c3..bdcafa835 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1709,6 +1709,7 @@ static void real_time_text(bool_t audio_stream_enabled, bool_t srtp_enabled, boo } linphone_chat_room_send_chat_message(pauline_chat_room, rtt_message); BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 1)); + linphone_chat_message_unref(rtt_message); if (sql_storage) { bctbx_list_t *marie_messages = linphone_chat_room_get_history(marie_chat_room, 0); @@ -1827,6 +1828,8 @@ static void real_time_text_conversation(void) { } } + linphone_chat_message_unref(pauline_rtt_message); + linphone_chat_message_unref(marie_rtt_message); reset_counters(&pauline->stat); reset_counters(&marie->stat); pauline_rtt_message = linphone_chat_room_create_message(pauline_chat_room,NULL); @@ -1862,6 +1865,8 @@ static void real_time_text_conversation(void) { BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg), message2_2); } } + linphone_chat_message_unref(pauline_rtt_message); + linphone_chat_message_unref(marie_rtt_message); } end_call(marie, pauline); linphone_call_params_unref(marie_params); @@ -1978,7 +1983,14 @@ static void real_time_text_message_accented_chars(void) { linphone_chat_room_send_chat_message(pauline_chat_room, rtt_message); BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 1)); - BC_ASSERT_EQUAL(strcmp(linphone_chat_message_get_text(marie->stat.last_received_chat_message), "ãæçéîøùÿ"), 0, int, "%i"); + BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message); + if (marie->stat.last_received_chat_message) { + const char *text = linphone_chat_message_get_text(marie->stat.last_received_chat_message); + BC_ASSERT_PTR_NOT_NULL(text); + if (text) + BC_ASSERT_STRING_EQUAL(text, "ãæçéîøùÿ"); + } + linphone_chat_message_unref(rtt_message); } end_call(marie, pauline); } @@ -2031,6 +2043,7 @@ static void real_time_text_copy_paste(void) { } linphone_chat_room_send_chat_message(pauline_chat_room, rtt_message); BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 1)); + linphone_chat_message_unref(rtt_message); } end_call(marie, pauline); @@ -2269,6 +2282,52 @@ void im_encryption_engine_b64_async(void) { im_encryption_engine_b64_base(TRUE); } +void unread_message_count(void) { + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); + + text_message_base(marie, pauline); + + BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message); + if (marie->stat.last_received_chat_message != NULL) { + LinphoneChatRoom *marie_room = linphone_chat_message_get_chat_room(marie->stat.last_received_chat_message); + BC_ASSERT_EQUAL(1, linphone_chat_room_get_unread_messages_count(marie_room), int, "%d"); + linphone_chat_room_mark_as_read(marie_room); + BC_ASSERT_EQUAL(0, linphone_chat_room_get_unread_messages_count(marie_room), int, "%d"); + } + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void message_received_callback(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage* msg) { + BC_ASSERT_PTR_NOT_NULL(room); + BC_ASSERT_EQUAL(1, linphone_chat_room_get_unread_messages_count(room), int, "%d"); + BC_ASSERT_PTR_NOT_NULL(msg); + if (room != NULL) { + linphone_chat_room_mark_as_read(room); + } + BC_ASSERT_EQUAL(0, linphone_chat_room_get_unread_messages_count(room), int, "%d"); +} + +void unread_message_count_callback(void) { + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); + int dummy = 0; + + LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); + linphone_core_cbs_set_message_received(cbs, message_received_callback); + linphone_core_add_callbacks(marie->lc, cbs); + + text_message_base(marie, pauline); + + wait_for_until(pauline->lc, marie->lc, &dummy, 1, 5000); + + linphone_core_cbs_unref(cbs); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + test_t message_tests[] = { TEST_NO_TAG("Text message", text_message), TEST_NO_TAG("Text message with credentials from auth callback", text_message_with_credential_from_auth_callback), @@ -2292,6 +2351,8 @@ test_t message_tests[] = { TEST_NO_TAG("IMDN notifications", imdn_notifications), TEST_NO_TAG("IM notification policy", im_notification_policy), #ifdef SQLITE_STORAGE_ENABLED + TEST_NO_TAG("Unread message count", unread_message_count), + TEST_NO_TAG("Unread message count in callback", unread_message_count_callback), TEST_ONE_TAG("IsComposing notification lime", is_composing_notification_with_lime, "LIME"), TEST_ONE_TAG("IMDN notifications with lime", imdn_notifications_with_lime, "LIME"), TEST_ONE_TAG("IM notification policy with lime", im_notification_policy_with_lime, "LIME"), diff --git a/wrappers/java/jni.mustache b/wrappers/java/jni.mustache index 52152d754..e226d5c68 100644 --- a/wrappers/java/jni.mustache +++ b/wrappers/java/jni.mustache @@ -357,27 +357,47 @@ static {{return}} {{callbackName}}({{params}}) { {{#interfaces}} {{#isSingleListener}} void Java_{{jniPackage}}{{className}}Impl_setListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { + {{classCName}} *cptr = ({{classCName}}*)ptr; + {{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr); + if (jlistener == NULL) { + jobject listener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs); + {{cPrefix}}_cbs_set_user_data(cbs, NULL); + if (listener != NULL) { + env->DeleteGlobalRef(listener); + } + } else { + jobject listener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs); + if (listener == NULL) { + listener = env->NewGlobalRef(jlistener); + } else { + if (env->IsSameObject(listener, jlistener)) { + return; + } else { + env->DeleteGlobalRef(listener); + listener = env->NewGlobalRef(jlistener); + } + } + {{cPrefix}}_cbs_set_user_data(cbs, listener); + {{#callbacksList}} + {{cPrefix}}_cbs_set_{{callback}}(cbs, {{callbackName}}); + {{/callbacksList}} + } +} {{/isSingleListener}} + {{#isMultiListener}} void Java_{{jniPackage}}{{className}}Impl_addListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { -{{/isMultiListener}} + if (jlistener == NULL) return; {{classCName}} *cptr = ({{classCName}}*)ptr; jobject listener = env->NewGlobalRef(jlistener); - {{#isSingleListener}} - {{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr); - {{/isSingleListener}} - {{#isMultiListener}} {{classCName}}Cbs *cbs = linphone_factory_create_{{factoryName}}_cbs(NULL); - {{/isMultiListener}} {{cPrefix}}_cbs_set_user_data(cbs, listener); {{#callbacksList}} {{cPrefix}}_cbs_set_{{callback}}(cbs, {{callbackName}}); {{/callbacksList}} - {{#isMultiListener}} {{cPrefix}}_add_callbacks(cptr, cbs); - {{/isMultiListener}} + {{cPrefix}}_cbs_unref(cbs); } -{{#isMultiListener}} void Java_{{jniPackage}}{{className}}Impl_removeListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { {{classCName}} *cptr = ({{classCName}}*)ptr; @@ -385,14 +405,16 @@ void Java_{{jniPackage}}{{className}}Impl_removeListener(JNIEnv* env, jobject th bctbx_list_t *it; for (it = (bctbx_list_t *)cbs_list; it != NULL; it = it->next) { {{classCName}}Cbs *cbs = ({{classCName}}Cbs *)it->data; - if ({{cPrefix}}_cbs_get_user_data(cbs) == jlistener) { + jobject listener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs); + if (env->IsSameObject(listener, jlistener)) { + {{cPrefix}}_cbs_set_user_data(cbs, NULL); {{cPrefix}}_remove_callbacks(cptr, cbs); + env->DeleteGlobalRef(listener); break; } } } {{/isMultiListener}} - {{/interfaces}} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////