diff --git a/coreapi/chat.c b/coreapi/chat.c index 3674db2ff..807c1701b 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -36,14 +36,16 @@ #include "linphone/wrapper_utils.h" #include "c-wrapper/c-tools.h" -#include "chat/chat-room-p.h" -#include "chat/chat-room.h" +#include "chat/basic-chat-room.h" +#include "chat/real-time-text-chat-room.h" +#include "chat/real-time-text-chat-room-p.h" #include "utils/content-type.h" struct _LinphoneChatRoom{ belle_sip_object_t base; void *user_data; LinphonePrivate::ChatRoom *cr; + LinphoneAddress *peerAddressCache; }; BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneChatRoom); @@ -163,13 +165,20 @@ const bctbx_list_t *linphone_core_get_chat_rooms(LinphoneCore *lc) { } static bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from) { - return linphone_address_weak_equal(cr->cr->getPeerAddress(), from); + LinphoneAddress *addr = linphone_address_new(cr->cr->getPeerAddress().asString().c_str()); + bool_t result = linphone_address_weak_equal(addr, from); + linphone_address_unref(addr); + return result; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatRoom); static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) { delete cr->cr; + if (cr->peerAddressCache) { + linphone_address_unref(cr->peerAddressCache); + cr->peerAddressCache = nullptr; + } } BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatRoom, belle_sip_object_t, @@ -178,14 +187,17 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatRoom, belle_sip_object_t, NULL, // marshal FALSE); -LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, LinphoneAddress *addr){ +LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, const LinphoneAddress *addr) { LinphoneChatRoom *cr = belle_sip_object_new(LinphoneChatRoom); - cr->cr = new LinphonePrivate::ChatRoom(lc, addr); + if (linphone_core_realtime_text_enabled(lc)) + cr->cr = new LinphonePrivate::RealTimeTextChatRoom(lc, *L_GET_CPP_PTR_FROM_C_STRUCT(addr, Address).get()); + else + cr->cr = new LinphonePrivate::BasicChatRoom(lc, *L_GET_CPP_PTR_FROM_C_STRUCT(addr, Address).get()); L_GET_PRIVATE(cr->cr)->setCBackPointer(cr); return cr; } -static LinphoneChatRoom *_linphone_core_create_chat_room(LinphoneCore *lc, LinphoneAddress *addr) { +static LinphoneChatRoom *_linphone_core_create_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) { LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(lc, addr); lc->chatrooms = bctbx_list_append(lc->chatrooms, (void *)cr); return cr; @@ -238,7 +250,7 @@ static LinphoneChatRoom *_linphone_core_get_or_create_chat_room(LinphoneCore *lc LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) { LinphoneChatRoom *ret = _linphone_core_get_chat_room(lc, addr); if (!ret) { - ret = _linphone_core_create_chat_room(lc, linphone_address_clone(addr)); + ret = _linphone_core_create_chat_room(lc, addr); } return ret; } @@ -391,16 +403,20 @@ bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) { return cr->cr->isRemoteComposing(); } -LinphoneCore *linphone_chat_room_get_lc(LinphoneChatRoom *cr) { +LinphoneCore *linphone_chat_room_get_lc(const LinphoneChatRoom *cr) { return linphone_chat_room_get_core(cr); } -LinphoneCore *linphone_chat_room_get_core(LinphoneChatRoom *cr) { +LinphoneCore *linphone_chat_room_get_core(const LinphoneChatRoom *cr) { return cr->cr->getCore(); } const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) { - return cr->cr->getPeerAddress(); + if (cr->peerAddressCache) { + linphone_address_unref(cr->peerAddressCache); + } + cr->peerAddressCache = linphone_address_new(cr->cr->getPeerAddress().asString().c_str()); + return cr->peerAddressCache; } LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, const char *message) { @@ -599,46 +615,52 @@ void linphone_chat_message_send_display_notification(LinphoneChatMessage *cm) { } void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call) { - L_GET_PRIVATE(cr->cr)->realtimeTextReceived(character, call); + if (linphone_core_realtime_text_enabled(lc)) + L_GET_PRIVATE(static_cast(cr->cr))->realtimeTextReceived(character, call); } uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr) { - return cr->cr->getChar(); + if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(cr))) + return static_cast(cr->cr)->getChar(); + return 0; } LinphoneStatus linphone_chat_message_put_char(LinphoneChatMessage *msg, uint32_t character) { LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(msg); - LinphoneCall *call = cr->cr->getCall(); - LinphoneCore *lc = cr->cr->getCore(); - const uint32_t new_line = 0x2028; - const uint32_t crlf = 0x0D0A; - const uint32_t lf = 0x0A; + if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(cr))) { + LinphoneCall *call = static_cast(cr->cr)->getCall(); + LinphoneCore *lc = static_cast(cr->cr)->getCore(); + const uint32_t new_line = 0x2028; + const uint32_t crlf = 0x0D0A; + const uint32_t lf = 0x0A; - if (!call || !linphone_call_get_stream(call, LinphoneStreamTypeText)) { - return -1; - } - - if (character == new_line || character == crlf || character == lf) { - if (lc && lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) { - ms_debug("New line sent, forge a message with content %s", msg->message); - msg->time = ms_time(0); - msg->state = LinphoneChatMessageStateDisplayed; - msg->dir = LinphoneChatMessageOutgoing; - if (msg->from) linphone_address_unref(msg->from); - msg->from = linphone_address_new(linphone_core_get_identity(lc)); - msg->storage_id = linphone_chat_message_store(msg); - ms_free(msg->message); - msg->message = NULL; + if (!call || !linphone_call_get_stream(call, LinphoneStreamTypeText)) { + return -1; } - } else { - char *value = LinphonePrivate::Utils::utf8ToChar(character); - msg->message = ms_strcat_printf(msg->message, value); - ms_debug("Sent RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, msg->message); - delete value; - } - text_stream_putchar32(reinterpret_cast(linphone_call_get_stream(call, LinphoneStreamTypeText)), character); - return 0; + if (character == new_line || character == crlf || character == lf) { + if (lc && lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) { + ms_debug("New line sent, forge a message with content %s", msg->message); + msg->time = ms_time(0); + msg->state = LinphoneChatMessageStateDisplayed; + msg->dir = LinphoneChatMessageOutgoing; + if (msg->from) linphone_address_unref(msg->from); + msg->from = linphone_address_new(linphone_core_get_identity(lc)); + msg->storage_id = linphone_chat_message_store(msg); + ms_free(msg->message); + msg->message = NULL; + } + } else { + char *value = LinphonePrivate::Utils::utf8ToChar(character); + msg->message = ms_strcat_printf(msg->message, value); + ms_debug("Sent RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, msg->message); + delete value; + } + + text_stream_putchar32(reinterpret_cast(linphone_call_get_stream(call, LinphoneStreamTypeText)), character); + return 0; + } + return -1; } const char* linphone_chat_message_get_message_id(const LinphoneChatMessage *cm) { @@ -650,7 +672,9 @@ void linphone_chat_room_compose(LinphoneChatRoom *cr) { } LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *room) { - return room->cr->getCall(); + if (linphone_core_realtime_text_enabled(linphone_chat_room_get_core(room))) + return static_cast(room->cr)->getCall(); + return nullptr; } void linphone_chat_room_set_call(LinphoneChatRoom *cr, LinphoneCall *call) { @@ -773,7 +797,10 @@ const LinphoneAddress *linphone_chat_message_get_to_address(const LinphoneChatMe if (msg->to) return msg->to; if (msg->dir == LinphoneChatMessageOutgoing) { - return msg->chat_room->cr->getPeerAddress(); + if (msg->chat_room->peerAddressCache) + linphone_address_unref(msg->chat_room->peerAddressCache); + msg->chat_room->peerAddressCache = linphone_address_new(msg->chat_room->cr->getPeerAddress().asString().c_str()); + return msg->chat_room->peerAddressCache; } return NULL; } diff --git a/include/linphone/chat.h b/include/linphone/chat.h index c3d6ab006..e20a25ea7 100644 --- a/include/linphone/chat.h +++ b/include/linphone/chat.h @@ -207,12 +207,12 @@ LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoo * @deprecated use linphone_chat_room_get_core() * @donotwrap **/ -LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr); +LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCore* linphone_chat_room_get_lc(const LinphoneChatRoom *cr); /** * Returns back pointer to #LinphoneCore object. **/ -LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr); +LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_core(const LinphoneChatRoom *cr); /** * When realtime text is enabled #linphone_call_params_realtime_text_enabled, #LinphoneCoreIsComposingReceivedCb is call everytime a char is received from peer. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bff469b22..56243ca4f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,9 +28,13 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES call/call-listener.h call/call-p.h call/call.h + chat/basic-chat-room.h + chat/basic-chat-room-p.h chat/chat-message.h chat/chat-room-p.h chat/chat-room.h + chat/client-group-chat-room.h + chat/client-group-chat-room-p.h chat/cpim/cpim.h chat/cpim/header/cpim-core-headers.h chat/cpim/header/cpim-generic-header.h @@ -41,6 +45,8 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES chat/cpim/parser/cpim-parser.h chat/imdn.h chat/is-composing.h + chat/real-time-text-chat-room.h + chat/real-time-text-chat-room-p.h conference/conference-listener.h conference/conference-p.h conference/conference.h @@ -90,8 +96,10 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES c-wrapper/api/c-address.cpp c-wrapper/api/c-event-log.cpp call/call.cpp + chat/basic-chat-room.cpp chat/chat-message.cpp chat/chat-room.cpp + chat/client-group-chat-room.cpp chat/cpim/header/cpim-core-headers.cpp chat/cpim/header/cpim-generic-header.cpp chat/cpim/header/cpim-header.cpp @@ -100,6 +108,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES chat/cpim/parser/cpim-parser.cpp chat/imdn.cpp chat/is-composing.cpp + chat/real-time-text-chat-room.cpp conference/conference.cpp conference/local-conference.cpp conference/params/call-session-params.cpp diff --git a/src/chat/basic-chat-room-p.h b/src/chat/basic-chat-room-p.h new file mode 100644 index 000000000..54cd99f16 --- /dev/null +++ b/src/chat/basic-chat-room-p.h @@ -0,0 +1,45 @@ +/* + * basic-chat-room-p.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _BASIC_CHAT_ROOM_P_H_ +#define _BASIC_CHAT_ROOM_P_H_ + +// From coreapi. +#include "private.h" + +#include "basic-chat-room.h" +#include "chat/chat-room-p.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class BasicChatRoomPrivate : public ChatRoomPrivate { +public: + BasicChatRoomPrivate (LinphoneCore *core, const Address &peerAddress); + virtual ~BasicChatRoomPrivate () = default; + +private: + std::string dummyConferenceId; + + L_DECLARE_PUBLIC(BasicChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _BASIC_CHAT_ROOM_P_H_ diff --git a/src/chat/basic-chat-room.cpp b/src/chat/basic-chat-room.cpp new file mode 100644 index 000000000..e4768c988 --- /dev/null +++ b/src/chat/basic-chat-room.cpp @@ -0,0 +1,70 @@ +/* + * basic-chat-room.cpp + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "basic-chat-room-p.h" +#include "logger/logger.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +BasicChatRoomPrivate::BasicChatRoomPrivate (LinphoneCore *core, const Address &peerAddress) : ChatRoomPrivate(core, peerAddress) {} + +// ============================================================================= + +BasicChatRoom::BasicChatRoom (LinphoneCore *core, const Address &peerAddress) : ChatRoom(*new BasicChatRoomPrivate(core, peerAddress)) {} + +// ----------------------------------------------------------------------------- + +shared_ptr BasicChatRoom::addParticipant (const Address &addr, const shared_ptr params, bool hasMedia) { + lError() << "addParticipant() is not allowed on a BasicChatRoom"; + return nullptr; +} + +void BasicChatRoom::addParticipants (const list
&addresses, const shared_ptr params, bool hasMedia) { + lError() << "addParticipants() is not allowed on a BasicChatRoom"; +} + +const string& BasicChatRoom::getId () const { + L_D(const BasicChatRoom); + lError() << "a BasicChatRoom does not have a conference id"; + return d->dummyConferenceId; +} + +int BasicChatRoom::getNbParticipants () const { + return 1; +} + +list> BasicChatRoom::getParticipants () const { + L_D(const BasicChatRoom); + list> l; + l.push_back(make_shared(d->peerAddress)); + return l; +} + +void BasicChatRoom::removeParticipant (const shared_ptr participant) { + lError() << "removeParticipant() is not allowed on a BasicChatRoom"; +} + +void BasicChatRoom::removeParticipants (const list> participants) { + lError() << "removeParticipants() is not allowed on a BasicChatRoom"; +} + +LINPHONE_END_NAMESPACE diff --git a/src/chat/basic-chat-room.h b/src/chat/basic-chat-room.h new file mode 100644 index 000000000..f47341fc7 --- /dev/null +++ b/src/chat/basic-chat-room.h @@ -0,0 +1,58 @@ +/* + * basic-chat-room.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _BASIC_CHAT_ROOM_H_ +#define _BASIC_CHAT_ROOM_H_ + +// From coreapi +#include "private.h" + +#include "chat/chat-room.h" +#include "conference/conference-interface.h" + +#include "linphone/types.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class BasicChatRoomPrivate; + +class BasicChatRoom : public ChatRoom { +public: + BasicChatRoom (LinphoneCore *core, const Address &peerAddress); + virtual ~BasicChatRoom () = default; + + /* ConferenceInterface */ + std::shared_ptr addParticipant (const Address &addr, const std::shared_ptr params, bool hasMedia); + void addParticipants (const std::list
&addresses, const std::shared_ptr params, bool hasMedia); + const std::string& getId () const; + int getNbParticipants () const; + std::list> getParticipants () const; + void removeParticipant (const std::shared_ptr participant); + void removeParticipants (const std::list> participants); + +private: + L_DECLARE_PRIVATE(BasicChatRoom); + L_DISABLE_COPY(BasicChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _BASIC_CHAT_ROOM_H_ + diff --git a/src/chat/chat-room-p.h b/src/chat/chat-room-p.h index 4f7e492d9..6d480f77f 100644 --- a/src/chat/chat-room-p.h +++ b/src/chat/chat-room-p.h @@ -33,7 +33,7 @@ LINPHONE_BEGIN_NAMESPACE class ChatRoomPrivate : public ObjectPrivate, public IsComposingListener { public: - ChatRoomPrivate (LinphoneCore *core); + ChatRoomPrivate (LinphoneCore *core, const Address &peerAddress); virtual ~ChatRoomPrivate (); private: @@ -62,7 +62,7 @@ public: this->call = call; } -private: +protected: void sendIsComposingNotification (); int createChatMessageFromDb (int argc, char **argv, char **colName); @@ -78,7 +78,7 @@ public: LinphoneReason messageReceived (SalOp *op, const SalMessage *msg); void realtimeTextReceived (uint32_t character, LinphoneCall *call); -private: +protected: void chatMessageReceived (LinphoneChatMessage *msg); void imdnReceived (const std::string &text); void isComposingReceived (const std::string &text); @@ -92,8 +92,7 @@ public: LinphoneChatRoom *cBackPointer = nullptr; LinphoneCore *core = nullptr; LinphoneCall *call = nullptr; - LinphoneAddress *peerAddress = nullptr; - std::string peer; + Address peerAddress; int unreadCount = -1; bool isComposing = false; bool remoteIsComposing = false; diff --git a/src/chat/chat-room.cpp b/src/chat/chat-room.cpp index d120efba8..a3e7f4030 100644 --- a/src/chat/chat-room.cpp +++ b/src/chat/chat-room.cpp @@ -22,6 +22,7 @@ #include "chat-room-p.h" #include "imdn.h" +#include "c-wrapper/c-tools.h" #include "logger/logger.h" #include "utils/content-type.h" @@ -33,17 +34,13 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE -ChatRoomPrivate::ChatRoomPrivate (LinphoneCore *core) - : core(core), isComposingHandler(core, this) {} +ChatRoomPrivate::ChatRoomPrivate (LinphoneCore *core, const Address &peerAddress) + : core(core), peerAddress(peerAddress), isComposingHandler(core, this) {} ChatRoomPrivate::~ChatRoomPrivate () { for (auto it = transientMessages.begin(); it != transientMessages.end(); it++) { linphone_chat_message_release(*it); } - if (!receivedRttCharacters.empty()) { - for (auto it = receivedRttCharacters.begin(); it != receivedRttCharacters.end(); it++) - bctbx_free(*it); - } if (core) { if (bctbx_list_find(core->chatrooms, cBackPointer)) { lError() << "LinphoneChatRoom[" << cBackPointer << "] is destroyed while still being used by the LinphoneCore. " << @@ -52,7 +49,6 @@ ChatRoomPrivate::~ChatRoomPrivate () { core->chatrooms = bctbx_list_remove(core->chatrooms, cBackPointer); } } - linphone_address_unref(peerAddress); if (pendingMessage) linphone_chat_message_destroy(pendingMessage); } @@ -120,7 +116,8 @@ void ChatRoomPrivate::sendImdn (const string &content, LinphoneReason reason) { L_Q(ChatRoom); const char *identity = nullptr; - LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(core, peerAddress); + LinphoneAddress *peer = linphone_address_new(peerAddress.asString().c_str()); + LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(core, peer); if (proxy) identity = linphone_address_as_string(linphone_proxy_config_get_identity_address(proxy)); else @@ -128,11 +125,11 @@ void ChatRoomPrivate::sendImdn (const string &content, LinphoneReason reason) { /* Sending out of call */ SalOp *op = sal_op_new(core->sal); - linphone_configure_op(core, op, peerAddress, nullptr, lp_config_get_int(core->config, "sip", "chat_msg_with_contact", 0)); + linphone_configure_op(core, op, peer, nullptr, lp_config_get_int(core->config, "sip", "chat_msg_with_contact", 0)); LinphoneChatMessage *msg = q->createMessage(content); LinphoneAddress *fromAddr = linphone_address_new(identity); linphone_chat_message_set_from_address(msg, fromAddr); - LinphoneAddress *toAddr = linphone_address_new(peer.c_str()); + LinphoneAddress *toAddr = linphone_address_new(peerAddress.asString().c_str()); linphone_chat_message_set_to_address(msg, toAddr); linphone_chat_message_set_content_type(msg, "message/imdn+xml"); @@ -148,12 +145,13 @@ void ChatRoomPrivate::sendImdn (const string &content, LinphoneReason reason) { } if (retval <= 0) { - sal_message_send(op, identity, peer.c_str(), msg->content_type, msg->message, nullptr); + sal_message_send(op, identity, peerAddress.asString().c_str(), msg->content_type, msg->message, nullptr); } linphone_chat_message_unref(msg); linphone_address_unref(fromAddr); linphone_address_unref(toAddr); + linphone_address_unref(peer); sal_op_unref(op); } @@ -165,11 +163,11 @@ int ChatRoomPrivate::getMessagesCount (bool unreadOnly) { /* Optimization: do not read database if the count is already available in memory */ if (unreadOnly && unreadCount >= 0) return unreadCount; - char *peer = linphone_address_as_string_uri_only(peerAddress); + string peer = peerAddress.asStringUriOnly(); char *option = nullptr; if (unreadOnly) option = bctbx_strdup_printf("AND status!=%i AND direction=%i", LinphoneChatMessageStateDisplayed, LinphoneChatMessageIncoming); - char *buf = sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q %s;", peer, unreadOnly ? option : ""); + char *buf = sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q %s;", peer.c_str(), unreadOnly ? option : ""); sqlite3_stmt *selectStatement; int numrows = 0; int returnValue = sqlite3_prepare_v2(core->db, buf, -1, &selectStatement, nullptr); @@ -180,7 +178,6 @@ int ChatRoomPrivate::getMessagesCount (bool unreadOnly) { } sqlite3_finalize(selectStatement); sqlite3_free(buf); - ms_free(peer); /* No need to test the sign of unreadCount here because it has been tested above */ if (unreadOnly) { @@ -196,7 +193,8 @@ void ChatRoomPrivate::sendIsComposingNotification () { L_Q(ChatRoom); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(core); if (linphone_im_notif_policy_get_send_is_composing(policy)) { - LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(core, peerAddress); + LinphoneAddress *peer = linphone_address_new(peerAddress.asString().c_str()); + LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(core, peer); const char *identity = nullptr; if (proxy) @@ -206,14 +204,14 @@ void ChatRoomPrivate::sendIsComposingNotification () { /* Sending out of call */ SalOp *op = sal_op_new(core->sal); - linphone_configure_op(core, op, peerAddress, nullptr, lp_config_get_int(core->config, "sip", "chat_msg_with_contact", 0)); + linphone_configure_op(core, op, peer, nullptr, lp_config_get_int(core->config, "sip", "chat_msg_with_contact", 0)); string content = isComposingHandler.marshal(isComposing); if (!content.empty()) { int retval = -1; LinphoneAddress *fromAddr = linphone_address_new(identity); LinphoneChatMessage *msg = q->createMessage(content); linphone_chat_message_set_from_address(msg, fromAddr); - linphone_chat_message_set_to_address(msg, peerAddress); + linphone_chat_message_set_to_address(msg, peer); linphone_chat_message_set_content_type(msg, "application/im-iscomposing+xml"); LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(core); @@ -226,13 +224,14 @@ void ChatRoomPrivate::sendIsComposingNotification () { } if (retval <= 0) { - sal_message_send(op, identity, peer.c_str(), msg->content_type, msg->message, nullptr); + sal_message_send(op, identity, peerAddress.asString().c_str(), msg->content_type, msg->message, nullptr); } linphone_chat_message_unref(msg); linphone_address_unref(fromAddr); sal_op_unref(op); } + linphone_address_unref(peer); } } @@ -270,15 +269,17 @@ int ChatRoomPrivate::createChatMessageFromDb (int argc, char **argv, char **colN if (!newMessage) { newMessage = q->createMessage(argv[4] ? argv[4] : ""); + LinphoneAddress *peer = linphone_address_new(peerAddress.asString().c_str()); if (atoi(argv[3]) == LinphoneChatMessageIncoming) { newMessage->dir = LinphoneChatMessageIncoming; - linphone_chat_message_set_from(newMessage, peerAddress); + linphone_chat_message_set_from(newMessage, peer); newMessage->to = nullptr; /* Will be filled at the end */ } else { newMessage->dir = LinphoneChatMessageOutgoing; newMessage->from = nullptr; /* Will be filled at the end */ - linphone_chat_message_set_to(newMessage, peerAddress); + linphone_chat_message_set_to(newMessage, peer); } + linphone_address_unref(peer); newMessage->time = (time_t)atol(argv[9]); newMessage->is_read = atoi(argv[6]); @@ -358,12 +359,11 @@ void ChatRoomPrivate::sqlRequestMessage (sqlite3 *db, const string &stmt) { list ChatRoomPrivate::findMessages (const string &messageId) { if (!core->db) return list(); - char *peer = linphone_address_as_string_uri_only(peerAddress); - char *buf = sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q AND messageId = %Q", peer, messageId.c_str()); + string peer = peerAddress.asStringUriOnly(); + char *buf = sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q AND messageId = %Q", peer.c_str(), messageId.c_str()); messages.clear(); sqlRequestMessage(core->db, buf); sqlite3_free(buf); - ms_free(peer); list result = messages; messages.clear(); return result; @@ -401,7 +401,9 @@ LinphoneReason ChatRoomPrivate::messageReceived (SalOp *op, const SalMessage *sa msg = q->createMessage(salMsg->text ? salMsg->text : ""); linphone_chat_message_set_content_type(msg, salMsg->content_type); - linphone_chat_message_set_from(msg, peerAddress); + LinphoneAddress *peer = linphone_address_new(peerAddress.asString().c_str()); + linphone_chat_message_set_from(msg, peer); + linphone_address_unref(peer); LinphoneAddress *to = sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(core)); msg->to = to; @@ -494,59 +496,6 @@ end: return reason; } -void ChatRoomPrivate::realtimeTextReceived (uint32_t character, LinphoneCall *call) { - L_Q(ChatRoom); - const uint32_t new_line = 0x2028; - const uint32_t crlf = 0x0D0A; - const uint32_t lf = 0x0A; - - if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { - LinphoneChatMessageCharacter *cmc = bctbx_new0(LinphoneChatMessageCharacter, 1); - - if (!pendingMessage) - pendingMessage = q->createMessage(""); - - cmc->value = character; - cmc->has_been_read = FALSE; - receivedRttCharacters.push_back(cmc); - - remoteIsComposing = true; - linphone_core_notify_is_composing_received(core, cBackPointer); - - if ((character == new_line) || (character == crlf) || (character == lf)) { - /* End of message */ - lDebug() << "New line received, forge a message with content " << pendingMessage->message; - linphone_chat_message_set_from(pendingMessage, peerAddress); - if (pendingMessage->to) - linphone_address_unref(pendingMessage->to); - pendingMessage->to = linphone_call_get_dest_proxy(call) - ? linphone_address_clone(linphone_call_get_dest_proxy(call)->identity_address) - : linphone_address_new(linphone_core_get_identity(core)); - pendingMessage->time = ms_time(0); - pendingMessage->state = LinphoneChatMessageStateDelivered; - pendingMessage->dir = LinphoneChatMessageIncoming; - - if (lp_config_get_int(core->config, "misc", "store_rtt_messages", 1) == 1) - storeOrUpdateMessage(pendingMessage); - - if (unreadCount < 0) unreadCount = 1; - else unreadCount++; - - chatMessageReceived(pendingMessage); - linphone_chat_message_unref(pendingMessage); - pendingMessage = nullptr; - for (auto it = receivedRttCharacters.begin(); it != receivedRttCharacters.end(); it++) - ms_free(*it); - receivedRttCharacters.clear(); - } else { - char *value = Utils::utf8ToChar(character); - pendingMessage->message = ms_strcat_printf(pendingMessage->message, value); - lDebug() << "Received RTT character: " << value << " (" << character << "), pending text is " << pendingMessage->message; - delete value; - } - } -} - // ----------------------------------------------------------------------------- void ChatRoomPrivate::chatMessageReceived (LinphoneChatMessage *msg) { @@ -589,13 +538,9 @@ void ChatRoomPrivate::isComposingRefreshNeeded () { // ============================================================================= -ChatRoom::ChatRoom (LinphoneCore *core, LinphoneAddress *peerAddress) : Object(*new ChatRoomPrivate(core)) { - L_D(ChatRoom); - d->peerAddress = peerAddress; - char *peerStr = linphone_address_as_string(d->peerAddress); - d->peer = peerStr; - ms_free(peerStr); -} +ChatRoom::ChatRoom (LinphoneCore *core, const Address &peerAddress) : Object(*new ChatRoomPrivate(core, peerAddress)) {} + +ChatRoom::ChatRoom (ChatRoomPrivate &p) : Object(p) {} // ----------------------------------------------------------------------------- @@ -617,7 +562,9 @@ LinphoneChatMessage *ChatRoom::createFileTransferMessage (const LinphoneContent cm->message = nullptr; cm->file_transfer_information = linphone_content_copy(initialContent); cm->dir = LinphoneChatMessageOutgoing; - linphone_chat_message_set_to(cm, d->peerAddress); + LinphoneAddress *peer = linphone_address_new(d->peerAddress.asString().c_str()); + linphone_chat_message_set_to(cm, peer); + linphone_address_unref(peer); cm->from = linphone_address_new(linphone_core_get_identity(d->core)); /* This will be set to application/vnd.gsma.rcs-ft-http+xml when we will transfer the xml reply from server to the peers */ cm->content_type = nullptr; @@ -645,11 +592,10 @@ LinphoneChatMessage *ChatRoom::createMessage (const string &msg) { void ChatRoom::deleteHistory () { L_D(ChatRoom); if (!d->core->db) return; - char *peer = linphone_address_as_string_uri_only(d->peerAddress); - char *buf = sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;", peer); + string peer = d->peerAddress.asStringUriOnly(); + char *buf = sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;", peer.c_str()); d->sqlRequest(d->core->db, buf); sqlite3_free(buf); - ms_free(peer); if (d->unreadCount > 0) d->unreadCount = 0; } @@ -697,20 +643,6 @@ LinphoneChatMessage * ChatRoom::findMessageWithDirection (const string &messageI return ret; } -uint32_t ChatRoom::getChar () const { - L_D(const ChatRoom); - if (!d->receivedRttCharacters.empty()) { - for (auto it = d->receivedRttCharacters.begin(); it != d->receivedRttCharacters.end(); it++) { - LinphoneChatMessageCharacter *cmc = *it; - if (!cmc->has_been_read) { - cmc->has_been_read = TRUE; - return cmc->value; - } - } - } - return 0; -} - list ChatRoom::getHistory (int nbMessages) { return getHistoryRange(0, nbMessages - 1); } @@ -723,13 +655,13 @@ int ChatRoom::getHistorySize () { list ChatRoom::getHistoryRange (int startm, int endm) { L_D(ChatRoom); if (!d->core->db) return list(); - char *peer = linphone_address_as_string_uri_only(d->peerAddress); + string peer = d->peerAddress.asStringUriOnly(); d->messages.clear(); /* Since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf */ const int bufMaxSize = 512; char *buf = reinterpret_cast(ms_malloc(bufMaxSize)); - buf = sqlite3_snprintf(bufMaxSize - 1, buf, "SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC", peer); + buf = sqlite3_snprintf(bufMaxSize - 1, buf, "SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC", peer.c_str()); if (startm < 0) startm = 0; if (((endm > 0) && (endm >= startm)) || ((startm == 0) && (endm == 0))) { @@ -775,7 +707,6 @@ list ChatRoom::getHistoryRange (int startm, int endm) { list result = d->messages; d->messages.clear(); - ms_free(peer); return result; } @@ -797,8 +728,8 @@ void ChatRoom::markAsRead () { /* Optimization: do not modify the database if no message is marked as unread */ if (getUnreadMessagesCount() == 0) return; - char *peer = linphone_address_as_string_uri_only(d->peerAddress); - char *buf = sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q AND direction = %i AND status != %i", peer, LinphoneChatMessageIncoming, LinphoneChatMessageStateDisplayed); + string peer = d->peerAddress.asStringUriOnly(); + char *buf = sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q AND direction = %i AND status != %i", peer.c_str(), LinphoneChatMessageIncoming, LinphoneChatMessageStateDisplayed); d->sqlRequestMessage(d->core->db, buf); sqlite3_free(buf); for (auto it = d->messages.begin(); it != d->messages.end(); it++) { @@ -806,10 +737,9 @@ void ChatRoom::markAsRead () { linphone_chat_message_unref(*it); } d->messages.clear(); - buf = sqlite3_mprintf("UPDATE history SET status=%i WHERE remoteContact=%Q AND direction=%i;", LinphoneChatMessageStateDisplayed, peer, LinphoneChatMessageIncoming); + buf = sqlite3_mprintf("UPDATE history SET status=%i WHERE remoteContact=%Q AND direction=%i;", LinphoneChatMessageStateDisplayed, peer.c_str(), LinphoneChatMessageIncoming); d->sqlRequest(d->core->db, buf); sqlite3_free(buf); - ms_free(peer); if (d->pendingMessage) { linphone_chat_message_set_state(d->pendingMessage, LinphoneChatMessageStateDisplayed); @@ -822,14 +752,6 @@ void ChatRoom::markAsRead () { void ChatRoom::sendMessage (LinphoneChatMessage *msg) { L_D(ChatRoom); - /* Stubed rtt */ - if (d->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(d->call))) { - uint32_t new_line = 0x2028; - linphone_chat_message_put_char(msg, new_line); - linphone_chat_message_unref(msg); - return; - } - msg->dir = LinphoneChatMessageOutgoing; /* Check if we shall upload a file to a server */ @@ -847,9 +769,10 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { } else { SalOp *op = msg->op; LinphoneCall *call = nullptr; - const char *identity = nullptr; + string identity; char *clearTextMessage = nullptr; char *clearTextContentType = nullptr; + LinphoneAddress *peer = linphone_address_new(d->peerAddress.asString().c_str()); if (msg->message) { clearTextMessage = ms_strdup(msg->message); @@ -862,7 +785,7 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { d->addTransientMessage(msg); msg->time = ms_time(0); if (lp_config_get_int(d->core->config, "sip", "chat_use_call_dialogs", 0) != 0) { - call = linphone_core_get_call_by_remote_address(d->core, d->peer.c_str()); + call = linphone_core_get_call_by_remote_address(d->core, d->peerAddress.asString().c_str()); if (call) { if (linphone_call_get_state(call) == LinphoneCallConnected || linphone_call_get_state(call) == LinphoneCallStreamsRunning || linphone_call_get_state(call) == LinphoneCallPaused || linphone_call_get_state(call) == LinphoneCallPausing || @@ -874,10 +797,10 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { } } - if (!identity) { - LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(d->core, d->peerAddress); + if (identity.empty()) { + LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(d->core, peer); if (proxy) { - identity = linphone_address_as_string(linphone_proxy_config_get_identity_address(proxy)); + identity = L_GET_CPP_PTR_FROM_C_STRUCT(linphone_proxy_config_get_identity_address(proxy), Address)->asString(); } else { identity = linphone_core_get_primary_contact(d->core); } @@ -886,7 +809,7 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { /* BUG: the file transfer message constructor sets the from, but doesn't do it as well as here */ linphone_address_unref(msg->from); } - msg->from = linphone_address_new(identity); + msg->from = linphone_address_new(identity.c_str()); int retval = -1; LinphoneImEncryptionEngine *imee = d->core->im_encryption_engine; @@ -904,7 +827,7 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { if (!op) { /* Sending out of call */ msg->op = op = sal_op_new(d->core->sal); - linphone_configure_op(d->core, op, d->peerAddress, msg->custom_headers, + linphone_configure_op(d->core, op, peer, msg->custom_headers, lp_config_get_int(d->core->config, "sip", "chat_msg_with_contact", 0)); sal_op_set_user_pointer(op, msg); /* If out of call, directly store msg */ } @@ -914,21 +837,20 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { d->storeOrUpdateMessage(msg); linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered); linphone_chat_message_unref(msg); + linphone_address_unref(peer); return; } if (msg->external_body_url) { char *content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", msg->external_body_url); - sal_message_send(op, identity, d->peer.c_str(), content_type, nullptr, nullptr); + sal_message_send(op, identity.c_str(), d->peerAddress.asString().c_str(), content_type, nullptr, nullptr); ms_free(content_type); } else { - char *peerUri = linphone_address_as_string_uri_only(d->peerAddress); if (msg->content_type) { - sal_message_send(op, identity, d->peer.c_str(), msg->content_type, msg->message, peerUri); + sal_message_send(op, identity.c_str(), d->peerAddress.asString().c_str(), msg->content_type, msg->message, d->peerAddress.asStringUriOnly().c_str()); } else { - sal_text_send(op, identity, d->peer.c_str(), msg->message); + sal_text_send(op, identity.c_str(), d->peerAddress.asString().c_str(), msg->message); } - ms_free(peerUri); } if (msg->message && clearTextMessage && strcmp(msg->message, clearTextMessage) != 0) { @@ -955,6 +877,7 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { if (clearTextContentType) { ms_free(clearTextContentType); } + linphone_address_unref(peer); if (call && linphone_call_get_op(call) == op) { /* In this case, chat delivery status is not notified, so unrefing chat message right now */ @@ -973,11 +896,6 @@ void ChatRoom::sendMessage (LinphoneChatMessage *msg) { // ----------------------------------------------------------------------------- -LinphoneCall *ChatRoom::getCall () const { - L_D(const ChatRoom); - return d->call; -} - LinphoneCore *ChatRoom::getCore () const { L_D(const ChatRoom); return d->core; @@ -985,7 +903,7 @@ LinphoneCore *ChatRoom::getCore () const { // ----------------------------------------------------------------------------- -const LinphoneAddress *ChatRoom::getPeerAddress () const { +const Address& ChatRoom::getPeerAddress () const { L_D(const ChatRoom); return d->peerAddress; } diff --git a/src/chat/chat-room.h b/src/chat/chat-room.h index d3052d711..d5663d416 100644 --- a/src/chat/chat-room.h +++ b/src/chat/chat-room.h @@ -24,7 +24,9 @@ #include +#include "address/address.h" #include "object/object.h" +#include "conference/conference-interface.h" #include "linphone/types.h" @@ -34,9 +36,9 @@ LINPHONE_BEGIN_NAMESPACE class ChatRoomPrivate; -class ChatRoom : public Object { +class ChatRoom : public Object, public ConferenceInterface { public: - ChatRoom (LinphoneCore *core, LinphoneAddress *peerAddress); + ChatRoom (LinphoneCore *core, const Address &peerAddress); virtual ~ChatRoom () = default; void compose (); @@ -46,19 +48,20 @@ public: void deleteMessage (LinphoneChatMessage *msg); LinphoneChatMessage * findMessage (const std::string& messageId); LinphoneChatMessage * findMessageWithDirection (const std::string &messageId, LinphoneChatMessageDir direction); - uint32_t getChar () const; std::list getHistory (int nbMessages); int getHistorySize (); std::list getHistoryRange (int startm, int endm); int getUnreadMessagesCount (); bool isRemoteComposing () const; void markAsRead (); - void sendMessage (LinphoneChatMessage *msg); + virtual void sendMessage (LinphoneChatMessage *msg); - LinphoneCall *getCall () const; LinphoneCore *getCore () const; - const LinphoneAddress *getPeerAddress () const; + const Address& getPeerAddress () const; + +protected: + explicit ChatRoom (ChatRoomPrivate &p); private: L_DECLARE_PRIVATE(ChatRoom); diff --git a/src/chat/client-group-chat-room-p.h b/src/chat/client-group-chat-room-p.h new file mode 100644 index 000000000..3e6fe2d1f --- /dev/null +++ b/src/chat/client-group-chat-room-p.h @@ -0,0 +1,43 @@ +/* + * client-group-chat-room-p.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CLIENT_GROUP_CHAT_ROOM_P_H_ +#define _CLIENT_GROUP_CHAT_ROOM_P_H_ + +// From coreapi. +#include "private.h" + +#include "client-group-chat-room.h" +#include "chat/chat-room-p.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ClientGroupChatRoomPrivate : public ChatRoomPrivate { +public: + ClientGroupChatRoomPrivate (LinphoneCore *core, const Address &peerAddress); + virtual ~ClientGroupChatRoomPrivate () = default; + +private: + L_DECLARE_PUBLIC(ClientGroupChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _CLIENT_GROUP_CHAT_ROOM_P_H_ diff --git a/src/chat/client-group-chat-room.cpp b/src/chat/client-group-chat-room.cpp new file mode 100644 index 000000000..9f607a71a --- /dev/null +++ b/src/chat/client-group-chat-room.cpp @@ -0,0 +1,30 @@ +/* + * client-group-chat-room.cpp + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "client-group-chat-room-p.h" +#include "logger/logger.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +ClientGroupChatRoomPrivate::ClientGroupChatRoomPrivate (LinphoneCore *core, const Address &peerAddress) : ChatRoomPrivate(core, peerAddress) {} + +LINPHONE_END_NAMESPACE diff --git a/src/chat/client-group-chat-room.h b/src/chat/client-group-chat-room.h new file mode 100644 index 000000000..6c70ab363 --- /dev/null +++ b/src/chat/client-group-chat-room.h @@ -0,0 +1,57 @@ +/* + * client-group-chat-room.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CLIENT_GROUP_CHAT_ROOM_H_ +#define _CLIENT_GROUP_CHAT_ROOM_H_ + +// From coreapi +#include "private.h" + +#include "chat/chat-room.h" + +#include "linphone/types.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class ClientGroupChatRoomPrivate; + +class ClientGroupChatRoom : public ChatRoom { +public: + ClientGroupChatRoom (LinphoneCore *core, const Address &peerAddress); + virtual ~ClientGroupChatRoom () = default; + + /* ConferenceInterface */ + std::shared_ptr addParticipant (const Address &addr, const std::shared_ptr params, bool hasMedia); + void addParticipants (const std::list
&addresses, const std::shared_ptr params, bool hasMedia); + const std::string& getId () const; + int getNbParticipants () const; + std::list> getParticipants () const; + void removeParticipant (const std::shared_ptr participant); + void removeParticipants (const std::list> participants); + +private: + L_DECLARE_PRIVATE(ClientGroupChatRoom); + L_DISABLE_COPY(ClientGroupChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _CLIENT_GROUP_CHAT_ROOM_H_ + diff --git a/src/chat/real-time-text-chat-room-p.h b/src/chat/real-time-text-chat-room-p.h new file mode 100644 index 000000000..50ca2db1c --- /dev/null +++ b/src/chat/real-time-text-chat-room-p.h @@ -0,0 +1,56 @@ +/* + * real-time-text-chat-room-p.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _REAL_TIME_TEXT_CHAT_ROOM_P_H_ +#define _REAL_TIME_TEXT_CHAT_ROOM_P_H_ + +// From coreapi. +#include "private.h" + +#include "real-time-text-chat-room.h" +#include "chat/chat-room-p.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class RealTimeTextChatRoomPrivate : public ChatRoomPrivate { +public: + RealTimeTextChatRoomPrivate (LinphoneCore *core, const Address &peerAddress); + virtual ~RealTimeTextChatRoomPrivate (); + +public: + void setCall (LinphoneCall *call) { this->call = call; } + +public: + void realtimeTextReceived (uint32_t character, LinphoneCall *call); + +public: + LinphoneCall *call = nullptr; + std::list receivedRttCharacters; + LinphoneChatMessage *pendingMessage = nullptr; + +private: + std::string dummyConferenceId; + + L_DECLARE_PUBLIC(RealTimeTextChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _REAL_TIME_TEXT_CHAT_ROOM_P_H_ diff --git a/src/chat/real-time-text-chat-room.cpp b/src/chat/real-time-text-chat-room.cpp new file mode 100644 index 000000000..680e5d05c --- /dev/null +++ b/src/chat/real-time-text-chat-room.cpp @@ -0,0 +1,175 @@ +/* + * chat-room.cpp + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "linphone/utils/utils.h" + +#include "real-time-text-chat-room-p.h" +#include "logger/logger.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +RealTimeTextChatRoomPrivate::RealTimeTextChatRoomPrivate (LinphoneCore *core, const Address &peerAddress) + : ChatRoomPrivate(core, peerAddress) {} + +RealTimeTextChatRoomPrivate::~RealTimeTextChatRoomPrivate () { + if (!receivedRttCharacters.empty()) { + for (auto it = receivedRttCharacters.begin(); it != receivedRttCharacters.end(); it++) + bctbx_free(*it); + } + if (pendingMessage) + linphone_chat_message_destroy(pendingMessage); +} + +// ----------------------------------------------------------------------------- + +void RealTimeTextChatRoomPrivate::realtimeTextReceived (uint32_t character, LinphoneCall *call) { + L_Q(ChatRoom); + const uint32_t new_line = 0x2028; + const uint32_t crlf = 0x0D0A; + const uint32_t lf = 0x0A; + + if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) { + LinphoneChatMessageCharacter *cmc = bctbx_new0(LinphoneChatMessageCharacter, 1); + + if (!pendingMessage) + pendingMessage = q->createMessage(""); + + cmc->value = character; + cmc->has_been_read = FALSE; + receivedRttCharacters.push_back(cmc); + + remoteIsComposing = true; + linphone_core_notify_is_composing_received(core, cBackPointer); + + if ((character == new_line) || (character == crlf) || (character == lf)) { + /* End of message */ + lDebug() << "New line received, forge a message with content " << pendingMessage->message; + LinphoneAddress *peer = linphone_address_new(peerAddress.asString().c_str()); + linphone_chat_message_set_from(pendingMessage, peer); + linphone_address_unref(peer); + if (pendingMessage->to) + linphone_address_unref(pendingMessage->to); + pendingMessage->to = linphone_call_get_dest_proxy(call) + ? linphone_address_clone(linphone_call_get_dest_proxy(call)->identity_address) + : linphone_address_new(linphone_core_get_identity(core)); + pendingMessage->time = ms_time(0); + pendingMessage->state = LinphoneChatMessageStateDelivered; + pendingMessage->dir = LinphoneChatMessageIncoming; + + if (lp_config_get_int(core->config, "misc", "store_rtt_messages", 1) == 1) + storeOrUpdateMessage(pendingMessage); + + if (unreadCount < 0) unreadCount = 1; + else unreadCount++; + + chatMessageReceived(pendingMessage); + linphone_chat_message_unref(pendingMessage); + pendingMessage = nullptr; + for (auto it = receivedRttCharacters.begin(); it != receivedRttCharacters.end(); it++) + ms_free(*it); + receivedRttCharacters.clear(); + } else { + char *value = Utils::utf8ToChar(character); + pendingMessage->message = ms_strcat_printf(pendingMessage->message, value); + lDebug() << "Received RTT character: " << value << " (" << character << "), pending text is " << pendingMessage->message; + delete value; + } + } +} + +// ============================================================================= + +RealTimeTextChatRoom::RealTimeTextChatRoom (LinphoneCore *core, const Address &peerAddress) : ChatRoom(*new RealTimeTextChatRoomPrivate(core, peerAddress)) {} + +// ----------------------------------------------------------------------------- + +void RealTimeTextChatRoom::sendMessage (LinphoneChatMessage *msg) { + L_D(ChatRoom); + if (d->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(d->call))) { + uint32_t new_line = 0x2028; + linphone_chat_message_put_char(msg, new_line); + linphone_chat_message_unref(msg); + } +} + +// ----------------------------------------------------------------------------- + +uint32_t RealTimeTextChatRoom::getChar () const { + L_D(const ChatRoom); + if (!d->receivedRttCharacters.empty()) { + for (auto it = d->receivedRttCharacters.begin(); it != d->receivedRttCharacters.end(); it++) { + LinphoneChatMessageCharacter *cmc = *it; + if (!cmc->has_been_read) { + cmc->has_been_read = TRUE; + return cmc->value; + } + } + } + return 0; +} + +// ----------------------------------------------------------------------------- + +LinphoneCall *RealTimeTextChatRoom::getCall () const { + L_D(const ChatRoom); + return d->call; +} + +// ----------------------------------------------------------------------------- + +shared_ptr RealTimeTextChatRoom::addParticipant (const Address &addr, const shared_ptr params, bool hasMedia) { + lError() << "addParticipant() is not allowed on a RealTimeTextChatRoom"; + return nullptr; +} + +void RealTimeTextChatRoom::addParticipants (const list
&addresses, const shared_ptr params, bool hasMedia) { + lError() << "addParticipants() is not allowed on a RealTimeTextChatRoom"; +} + +const string& RealTimeTextChatRoom::getId () const { + L_D(const RealTimeTextChatRoom); + lError() << "a RealTimeTextChatRoom does not have a conference id"; + return d->dummyConferenceId; +} + +int RealTimeTextChatRoom::getNbParticipants () const { + return 1; +} + +list> RealTimeTextChatRoom::getParticipants () const { + L_D(const RealTimeTextChatRoom); + list> l; + l.push_back(make_shared(d->peerAddress)); + return l; +} + +void RealTimeTextChatRoom::removeParticipant (const shared_ptr participant) { + lError() << "removeParticipant() is not allowed on a RealTimeTextChatRoom"; +} + +void RealTimeTextChatRoom::removeParticipants (const list> participants) { + lError() << "removeParticipants() is not allowed on a RealTimeTextChatRoom"; +} + +LINPHONE_END_NAMESPACE diff --git a/src/chat/real-time-text-chat-room.h b/src/chat/real-time-text-chat-room.h new file mode 100644 index 000000000..3987a8a9b --- /dev/null +++ b/src/chat/real-time-text-chat-room.h @@ -0,0 +1,62 @@ +/* + * real-time-text-chat-room.h + * Copyright (C) 2017 Belledonne Communications SARL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _REAL_TIME_TEXT_CHAT_ROOM_H_ +#define _REAL_TIME_TEXT_CHAT_ROOM_H_ + +// From coreapi +#include "private.h" + +#include "chat/chat-room.h" + +#include "linphone/types.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class RealTimeTextChatRoomPrivate; + +class RealTimeTextChatRoom : public ChatRoom { +public: + RealTimeTextChatRoom (LinphoneCore *core, const Address &peerAddress); + virtual ~RealTimeTextChatRoom () = default; + + void sendMessage (LinphoneChatMessage *msg); + + uint32_t getChar () const; + LinphoneCall *getCall () const; + + /* ConferenceInterface */ + std::shared_ptr addParticipant (const Address &addr, const std::shared_ptr params, bool hasMedia); + void addParticipants (const std::list
&addresses, const std::shared_ptr params, bool hasMedia); + const std::string& getId () const; + int getNbParticipants () const; + std::list> getParticipants () const; + void removeParticipant (const std::shared_ptr participant); + void removeParticipants (const std::list> participants); + +private: + L_DECLARE_PRIVATE(RealTimeTextChatRoom); + L_DISABLE_COPY(RealTimeTextChatRoom); +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _REAL_TIME_TEXT_CHAT_ROOM_H_ + diff --git a/src/conference/conference-interface.h b/src/conference/conference-interface.h index 181052912..6c0090e05 100644 --- a/src/conference/conference-interface.h +++ b/src/conference/conference-interface.h @@ -33,7 +33,7 @@ LINPHONE_BEGIN_NAMESPACE class ConferenceInterface { public: virtual std::shared_ptr addParticipant (const Address &addr, const std::shared_ptr params, bool hasMedia) = 0; - virtual void addParticipants (const std::list &addresses, const std::shared_ptr params, bool hasMedia) = 0; + virtual void addParticipants (const std::list
&addresses, const std::shared_ptr params, bool hasMedia) = 0; virtual const std::string& getId () const = 0; virtual int getNbParticipants () const = 0; virtual std::list> getParticipants () const = 0; diff --git a/src/conference/conference.cpp b/src/conference/conference.cpp index 5175c43fe..affe724ec 100644 --- a/src/conference/conference.cpp +++ b/src/conference/conference.cpp @@ -109,7 +109,7 @@ shared_ptr Conference::addParticipant (const Address &addr, const s return d->activeParticipant; } -void Conference::addParticipants (const list &addresses, const shared_ptr params, bool hasMedia) { +void Conference::addParticipants (const list
&addresses, const shared_ptr params, bool hasMedia) { // TODO } diff --git a/src/conference/conference.h b/src/conference/conference.h index 04d69279e..c5c9619b7 100644 --- a/src/conference/conference.h +++ b/src/conference/conference.h @@ -46,7 +46,7 @@ public: /* ConferenceInterface */ virtual std::shared_ptr addParticipant (const Address &addr, const std::shared_ptr params, bool hasMedia); - virtual void addParticipants (const std::list &addresses, const std::shared_ptr params, bool hasMedia); + virtual void addParticipants (const std::list
&addresses, const std::shared_ptr params, bool hasMedia); virtual const std::string& getId () const; virtual int getNbParticipants () const; virtual std::list> getParticipants () const;