diff --git a/src/chat/distributedchat.cc b/src/chat/distributedchat.cc index e42995f7eb..7701b9fe9c 100644 --- a/src/chat/distributedchat.cc +++ b/src/chat/distributedchat.cc @@ -66,6 +66,7 @@ DistributedChatService::DistributedChatService(uint32_t serv_type,p3ServiceContr { _time_shift_average = 0.0f ; _should_reset_lobby_counts = false ; + _allow_history_sharing = false ; last_visible_lobby_info_request_time = 0 ; } @@ -443,6 +444,10 @@ bool DistributedChatService::handleRecvItem(RsChatItem *item) case RS_PKT_SUBTYPE_CHAT_LOBBY_UNSUBSCRIBE: handleFriendUnsubscribeLobby (dynamic_cast(item)) ; break ; case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_REQUEST: handleRecvChatLobbyListRequest (dynamic_cast(item)) ; break ; case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST: handleRecvChatLobbyList (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE: handleRecvLobbyHistoryProbe (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE_RESP: handleRecvLobbyHistoryProbeResponse(dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_REQUEST: handleRecvLobbyHistoryRequest (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_DATA: handleRecvLobbyHistoryData (dynamic_cast(item)) ; break ; default: return false ; } return true ; @@ -2145,6 +2150,15 @@ void DistributedChatService::addToSaveList(std::list& list) const list.push_back(vitem); } + /* Save Allow History Sharing */ + { + RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; + RsTlvKeyValue kv; + kv.key = "ALLOW_HISTORY_SHARING"; + kv.value = _allow_history_sharing ? "1" : "0"; + vitem->tlvkvs.pairs.push_back(kv); + list.push_back(vitem); + } } bool DistributedChatService::processLoadListItem(const RsItem *item) @@ -2170,6 +2184,12 @@ bool DistributedChatService::processLoadListItem(const RsItem *item) return true; } + if( kit->key == "ALLOW_HISTORY_SHARING" ) + { + _allow_history_sharing = (kit->value == "1") ; + return true; + } + if( kit->key.compare(0, strldID.length(), strldID) == 0) { #ifdef DEBUG_CHAT_LOBBIES @@ -2269,3 +2289,258 @@ bool DistributedChatService::processLoadListItem(const RsItem *item) return false ; } +/***************** Lobby History Retrieval Protocol *****************/ + +#include "chat/rschatitems.h" +#include "pqi/p3historymgr.h" + +bool DistributedChatService::requestLobbyHistory(const ChatLobbyId& lobby_id) +{ + RsStackMutex stack(mDistributedChatMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it = _chat_lobbys.find(lobby_id) ; + + if(it == _chat_lobbys.end()) + { + std::cerr << "(EE) requestLobbyHistory(): lobby " << std::hex << lobby_id << std::dec << " not found." << std::endl; + return false ; + } + + // Send a probe to every direct friend participating in this lobby + + for(std::set::const_iterator fit(it->second.participating_friends.begin()); fit != it->second.participating_friends.end(); ++fit) + { + if(mServControl->isPeerConnected(mServType, *fit)) + { + RsChatLobbyHistoryProbeItem *item = new RsChatLobbyHistoryProbeItem ; + item->lobby_id = lobby_id ; + item->PeerId(*fit) ; + + sendChatItem(item) ; + + std::cerr << "requestLobbyHistory(): sent probe to peer " << *fit << " for lobby " << std::hex << lobby_id << std::dec << std::endl; + } + } + + return true ; +} + +void DistributedChatService::handleRecvLobbyHistoryProbe(RsChatLobbyHistoryProbeItem *item) +{ + if(!item) return ; + + // Check we are subscribed to this lobby + { + RsStackMutex stack(mDistributedChatMtx); /********** STACK LOCKED MTX ******/ + + if(_chat_lobbys.find(item->lobby_id) == _chat_lobbys.end()) + { + std::cerr << "(WW) handleRecvLobbyHistoryProbe(): lobby " << std::hex << item->lobby_id << std::dec << " not found. Ignoring." << std::endl; + return ; + } + } + + // Check if we allow sharing history + if(!_allow_history_sharing) + { + std::cerr << "handleRecvLobbyHistoryProbe(): history sharing is disabled. Ignoring." << std::endl; + return ; + } + + // Retrieve our local history for this lobby + std::list msgs ; + mHistMgr->getMessages(ChatId(item->lobby_id), msgs, 0) ; // 0 = get all available + + uint32_t available_count = (uint32_t)msgs.size() ; + uint32_t oldest_ts = 0 ; + + if(!msgs.empty()) + oldest_ts = msgs.front().sendTime ; + + // Send response back to the requesting peer + RsChatLobbyHistoryProbeResponseItem *response = new RsChatLobbyHistoryProbeResponseItem ; + response->lobby_id = item->lobby_id ; + response->available_count = available_count ; + response->oldest_timestamp = oldest_ts ; + response->PeerId(item->PeerId()) ; + + sendChatItem(response) ; + + std::cerr << "handleRecvLobbyHistoryProbe(): responded to " << item->PeerId() << " — " << available_count << " msgs available, oldest TS=" << oldest_ts << std::endl; +} + +void DistributedChatService::handleRecvLobbyHistoryProbeResponse(RsChatLobbyHistoryProbeResponseItem *item) +{ + if(!item) return ; + + std::cerr << "handleRecvLobbyHistoryProbeResponse(): peer " << item->PeerId() + << " has " << item->available_count << " messages for lobby " << std::hex << item->lobby_id << std::dec + << ", oldest TS=" << item->oldest_timestamp << std::endl; + + auto ev = std::make_shared(); + ev->mEventCode = RsChatLobbyEventCode::CHAT_LOBBY_EVENT_HISTORY_PROBE_RESPONSE; + ev->mPeerId = item->PeerId() ; + ev->mLobbyId = item->lobby_id ; + ev->mGenericCount = item->available_count ; + ev->mTimeShift = item->oldest_timestamp ; // using mTimeShift to store the timestamp + rsEvents->postEvent(ev); +} + +bool DistributedChatService::requestLobbyHistoryFromPeer(const ChatLobbyId& lobby_id, const RsPeerId& peer_id, uint32_t max_count, uint32_t oldest_ts) +{ + RsStackMutex stack(mDistributedChatMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it = _chat_lobbys.find(lobby_id) ; + + if(it == _chat_lobbys.end()) + { + std::cerr << "(EE) requestLobbyHistoryFromPeer(): lobby " << std::hex << lobby_id << std::dec << " not found." << std::endl; + return false ; + } + + if(!mServControl->isPeerConnected(mServType, peer_id)) + { + std::cerr << "(EE) requestLobbyHistoryFromPeer(): peer " << peer_id << " is not connected." << std::endl; + return false ; + } + + RsChatLobbyHistoryRequestItem *request = new RsChatLobbyHistoryRequestItem ; + request->lobby_id = lobby_id ; + request->max_count = max_count ; + request->oldest_timestamp = oldest_ts ; + request->PeerId(peer_id) ; + + sendChatItem(request) ; + + std::cerr << "requestLobbyHistoryFromPeer(): sent request to peer " << peer_id << " for lobby " << std::hex << lobby_id << std::dec << " (max " << max_count << " msgs, oldest TS=" << oldest_ts << ")" << std::endl; + return true ; +} + +void DistributedChatService::handleRecvLobbyHistoryRequest(RsChatLobbyHistoryRequestItem *item) +{ + if(!item) return ; + + // Check we are subscribed to this lobby + { + RsStackMutex stack(mDistributedChatMtx); /********** STACK LOCKED MTX ******/ + + if(_chat_lobbys.find(item->lobby_id) == _chat_lobbys.end()) + { + std::cerr << "(WW) handleRecvLobbyHistoryRequest(): lobby " << std::hex << item->lobby_id << std::dec << " not found. Ignoring." << std::endl; + return ; + } + } + + // Check if we allow sharing history + if(!_allow_history_sharing) + { + std::cerr << "handleRecvLobbyHistoryRequest(): history sharing is disabled. Ignoring." << std::endl; + return ; + } + + // Retrieve local history + std::list msgs ; + mHistMgr->getMessages(ChatId(item->lobby_id), msgs, item->max_count) ; + + RsChatLobbyHistoryDataItem *data_item = new RsChatLobbyHistoryDataItem ; + data_item->lobby_id = item->lobby_id ; + data_item->PeerId(item->PeerId()) ; + size_t count = 0; + size_t current_chunk_size = 0; + const size_t MAX_CHUNK_SIZE = 120 * 1024; // ~120 KB to stay safely under 128KB limit + + for(std::list::const_iterator it(msgs.begin()); it != msgs.end(); ++it) + { + if(it->sendTime >= item->oldest_timestamp) + { + LobbyHistoryMsgEntry entry ; + // HistoryMsg only stores the author's nick (peerName) and peerId, not the GXS ID. + entry.author_id = RsGxsId() ; + entry.nick = it->peerName ; + entry.send_time = it->sendTime ; + entry.message = it->message ; + entry.incoming = it->incoming ; + + // Calculate approximate size of this entry + // author_id (std::string approx 32 bytes) + nick + timestamp (4 bytes) + message + incoming (1 byte) + size_t entry_size = 32 + entry.nick.size() + 4 + entry.message.size() + 1 + 50; // +50 for serialization overhead + + // If adding this message exceeds the chunk size (and the chunk isn't empty), send the current chunk first + if (current_chunk_size + entry_size > MAX_CHUNK_SIZE && !data_item->msgs.empty()) { + std::cerr << "handleRecvLobbyHistoryRequest(): sending chunk of " << data_item->msgs.size() << " msgs (" << current_chunk_size << " bytes) to " << item->PeerId() << std::endl; + sendChatItem(data_item) ; + + // Create a new data item for the next chunk + data_item = new RsChatLobbyHistoryDataItem ; + data_item->lobby_id = item->lobby_id ; + data_item->PeerId(item->PeerId()) ; + current_chunk_size = 0; + } + + data_item->msgs.push_back(entry) ; + current_chunk_size += entry_size; + count++; + } + } + + if (!data_item->msgs.empty()) { + std::cerr << "handleRecvLobbyHistoryRequest(): sending final chunk of " << data_item->msgs.size() << " msgs to " << item->PeerId() << std::endl; + sendChatItem(data_item) ; + } else { + // Clean up the unused item if we sent everything cleanly in batches, or if there were 0 messages. + delete data_item; + } + + std::cerr << "handleRecvLobbyHistoryRequest(): finished. Sent a total of " << count << " messages." << std::endl; +} + +void DistributedChatService::handleRecvLobbyHistoryData(RsChatLobbyHistoryDataItem *item) +{ + if(!item) return ; + + std::cerr << "handleRecvLobbyHistoryData(): received " << item->msgs.size() << " messages from peer " << item->PeerId() + << " for lobby " << std::hex << item->lobby_id << std::dec << std::endl; + + // Deduplicate and save to local history database + std::list existingMsgs; + mHistMgr->getMessages(ChatId(item->lobby_id), existingMsgs, 0); + + std::set > existingSet; + for (std::list::const_iterator it = existingMsgs.begin(); it != existingMsgs.end(); ++it) { + existingSet.insert(std::make_pair(it->sendTime, it->message)); + } + + int addedCount = 0; + for (std::vector::const_iterator it = item->msgs.begin(); it != item->msgs.end(); ++it) { + if (existingSet.find(std::make_pair(it->send_time, it->message)) == existingSet.end()) { + // Not a duplicate + ChatMessage cm; + cm.chat_id = ChatId(item->lobby_id); + cm.lobby_peer_gxs_id = it->author_id; + cm.peer_alternate_nickname = it->nick; + cm.chatflags = 0; + cm.sendTime = it->send_time; + cm.recvTime = it->send_time; // Keep the original send_time for chronological sorting + cm.msg = it->message; + cm.incoming = it->incoming; + cm.online = true; + + mHistMgr->addMessage(cm); + addedCount++; + + // Add to our set just in case the received chunk contains duplicates within itself + existingSet.insert(std::make_pair(it->send_time, it->message)); + } + } + + std::cerr << "handleRecvLobbyHistoryData(): merged " << addedCount << " new messages into local history." << std::endl; + + auto ev = std::make_shared(); + ev->mEventCode = RsChatLobbyEventCode::CHAT_LOBBY_EVENT_HISTORY_DATA; + ev->mPeerId = item->PeerId() ; + ev->mLobbyId = item->lobby_id ; + ev->mHistoryMsgs = item->msgs ; // Copy the received messages to the event + rsEvents->postEvent(ev); +} + + diff --git a/src/chat/distributedchat.h b/src/chat/distributedchat.h index 411da81c12..ca0fa4b499 100644 --- a/src/chat/distributedchat.h +++ b/src/chat/distributedchat.h @@ -46,6 +46,11 @@ class RsChatItem ; class RsChatMsgItem ; class RsGixs ; +class RsChatLobbyHistoryProbeItem ; +class RsChatLobbyHistoryProbeResponseItem ; +class RsChatLobbyHistoryRequestItem ; +class RsChatLobbyHistoryDataItem ; + class DistributedChatService { public: @@ -79,6 +84,12 @@ class DistributedChatService void getListOfNearbyChatLobbies(std::vector& public_lobbies) ; bool joinVisibleChatLobby(const ChatLobbyId& id, const RsGxsId &gxs_id) ; + // Lobby history retrieval protocol + bool requestLobbyHistory(const ChatLobbyId& lobby_id) ; + bool requestLobbyHistoryFromPeer(const ChatLobbyId& lobby_id, const RsPeerId& peer_id, uint32_t max_count, uint32_t oldest_ts) ; + void allowHistorySharing(bool allow) { _allow_history_sharing = allow; triggerConfigSave(); } + bool isHistorySharingAllowed() const { return _allow_history_sharing; } + protected: bool handleRecvItem(RsChatItem *) ; @@ -125,6 +136,12 @@ class DistributedChatService void sendLobbyStatusNewPeer(const ChatLobbyId& lobby_id) ; void sendLobbyStatusKeepAlive(const ChatLobbyId&) ; + // Lobby history retrieval handlers + void handleRecvLobbyHistoryProbe(RsChatLobbyHistoryProbeItem *item) ; + void handleRecvLobbyHistoryProbeResponse(RsChatLobbyHistoryProbeResponseItem *item) ; + void handleRecvLobbyHistoryRequest(RsChatLobbyHistoryRequestItem *item) ; + void handleRecvLobbyHistoryData(RsChatLobbyHistoryDataItem *item) ; + bool locked_initLobbyBouncableObject(const ChatLobbyId& id,RsChatLobbyBouncingObject&) ; void locked_printDebugInfo() const ; RsGxsId locked_getDefaultIdentity(); @@ -156,6 +173,7 @@ class DistributedChatService rstime_t last_lobby_challenge_time ; // prevents bruteforce attack rstime_t last_visible_lobby_info_request_time ; // allows to ask for updates bool _should_reset_lobby_counts ; + bool _allow_history_sharing ; RsGxsId _default_identity; std::map _lobby_default_identity; diff --git a/src/chat/p3chatservice.cc b/src/chat/p3chatservice.cc index e585327402..d741278722 100644 --- a/src/chat/p3chatservice.cc +++ b/src/chat/p3chatservice.cc @@ -554,6 +554,27 @@ ChatLobbyId p3ChatService::createChatLobby(const std::string& lobby_name,const R return DistributedChatService::createChatLobby(lobby_name,lobby_identity,lobby_topic,invited_friends,privacy_type) ; } +bool p3ChatService::requestLobbyHistory(const ChatLobbyId& lobby_id) +{ + return DistributedChatService::requestLobbyHistory(lobby_id) ; +} + +bool p3ChatService::requestLobbyHistoryFromPeer(const ChatLobbyId& lobby_id, const RsPeerId& peer_id, uint32_t max_count, uint32_t oldest_ts) +{ + return DistributedChatService::requestLobbyHistoryFromPeer(lobby_id, peer_id, max_count, oldest_ts) ; +} + +bool p3ChatService::allowHistorySharing(bool allow) +{ + DistributedChatService::allowHistorySharing(allow) ; + return true ; +} + +bool p3ChatService::isHistorySharingAllowed() const +{ + return DistributedChatService::isHistorySharingAllowed() ; +} + void p3ChatService::sendChatItem(RsChatItem *item) { diff --git a/src/chat/p3chatservice.h b/src/chat/p3chatservice.h index ac0be8f50f..002ec35857 100644 --- a/src/chat/p3chatservice.h +++ b/src/chat/p3chatservice.h @@ -138,6 +138,11 @@ class p3ChatService : virtual void getDefaultIdentityForChatLobby(RsGxsId& nick_name) override; virtual void setLobbyAutoSubscribe(const ChatLobbyId& lobby_id, const bool autoSubscribe) override; virtual bool getLobbyAutoSubscribe(const ChatLobbyId& lobby_id) override; + virtual bool requestLobbyHistory(const ChatLobbyId& lobby_id) override; + virtual bool requestLobbyHistoryFromPeer(const ChatLobbyId& lobby_id, const RsPeerId& peer_id, uint32_t max_count, uint32_t oldest_ts) override; + + virtual bool allowHistorySharing(bool allow) override; + virtual bool isHistorySharingAllowed() const override; /** methods that will call the DistantChatService parent */ diff --git a/src/chat/rschatitems.cc b/src/chat/rschatitems.cc index b0e570215e..badbb12bce 100644 --- a/src/chat/rschatitems.cc +++ b/src/chat/rschatitems.cc @@ -56,6 +56,10 @@ RsItem *RsChatSerialiser::create_item(uint16_t service_id,uint8_t item_sub_id) c case RS_PKT_SUBTYPE_CHAT_AVATAR_INFO: return new RsChatAvatarInfoItem(); case RS_PKT_SUBTYPE_CHAT_AVATAR_CONFIG: return new RsChatAvatarConfigItem(); case RS_PKT_SUBTYPE_OUTGOING_MAP: return new PrivateOugoingMapItem(); + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE: return new RsChatLobbyHistoryProbeItem(); + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE_RESP: return new RsChatLobbyHistoryProbeResponseItem(); + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_REQUEST: return new RsChatLobbyHistoryRequestItem(); + case RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_DATA: return new RsChatLobbyHistoryDataItem(); default: std::cerr << "Unknown packet type in chat!" << std::endl; return NULL; @@ -220,3 +224,32 @@ void PrivateOugoingMapItem::serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) { RS_SERIAL_PROCESS(store); } + +/***************** Lobby History Retrieval Protocol *****************/ + + + +void RsChatLobbyHistoryProbeItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) +{ + RsTypeSerializer::serial_process(j,ctx,lobby_id,"lobby_id") ; +} + +void RsChatLobbyHistoryProbeResponseItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) +{ + RsTypeSerializer::serial_process(j,ctx,lobby_id, "lobby_id") ; + RsTypeSerializer::serial_process(j,ctx,available_count, "available_count") ; + RsTypeSerializer::serial_process(j,ctx,oldest_timestamp, "oldest_timestamp") ; +} + +void RsChatLobbyHistoryRequestItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) +{ + RsTypeSerializer::serial_process(j,ctx,lobby_id, "lobby_id") ; + RsTypeSerializer::serial_process(j,ctx,max_count, "max_count") ; + RsTypeSerializer::serial_process(j,ctx,oldest_timestamp, "oldest_timestamp") ; +} + +void RsChatLobbyHistoryDataItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) +{ + RsTypeSerializer::serial_process(j,ctx,lobby_id,"lobby_id") ; + RsTypeSerializer::serial_process (j,ctx,msgs, "msgs") ; +} diff --git a/src/chat/rschatitems.h b/src/chat/rschatitems.h index 04dcaf8e50..8958262936 100644 --- a/src/chat/rschatitems.h +++ b/src/chat/rschatitems.h @@ -87,6 +87,11 @@ const uint8_t RS_PKT_SUBTYPE_SUBSCRIBED_CHAT_LOBBY_CONFIG = 0x1D ; const uint8_t RS_PKT_SUBTYPE_CHAT_AVATAR_INFO = 0x1E ; const uint8_t RS_PKT_SUBTYPE_CHAT_AVATAR_CONFIG = 0x1F ; +const uint8_t RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE = 0x20 ; +const uint8_t RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE_RESP = 0x21 ; +const uint8_t RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_REQUEST = 0x22 ; +const uint8_t RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_DATA = 0x23 ; + typedef uint64_t ChatLobbyId ; typedef uint64_t ChatLobbyMsgId ; typedef std::string ChatLobbyNickName ; @@ -397,6 +402,61 @@ class RsChatAvatarConfigItem: public RsChatItem unsigned char* image_data ; /// image data }; +// ----- Lobby History Retrieval Protocol ----- + +/// Step 1: Probe sent to all direct friends in the lobby: "do you have history?" +class RsChatLobbyHistoryProbeItem: public RsChatItem +{ +public: + RsChatLobbyHistoryProbeItem() : RsChatItem(RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE) {} + virtual ~RsChatLobbyHistoryProbeItem() {} + + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx); + + uint64_t lobby_id; +}; + +/// Step 2: Response to probe: "I have N messages, oldest is TS" +class RsChatLobbyHistoryProbeResponseItem: public RsChatItem +{ +public: + RsChatLobbyHistoryProbeResponseItem() : RsChatItem(RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_PROBE_RESP) {} + virtual ~RsChatLobbyHistoryProbeResponseItem() {} + + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx); + + uint64_t lobby_id; + uint32_t available_count; + uint32_t oldest_timestamp; +}; + +/// Step 3: Request sent to chosen friend: "send me max N messages, no older than TS" +class RsChatLobbyHistoryRequestItem: public RsChatItem +{ +public: + RsChatLobbyHistoryRequestItem() : RsChatItem(RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_REQUEST) {} + virtual ~RsChatLobbyHistoryRequestItem() {} + + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx); + + uint64_t lobby_id; + uint32_t max_count; + uint32_t oldest_timestamp; +}; + +/// Step 4: Actual history data sent back +class RsChatLobbyHistoryDataItem: public RsChatItem +{ +public: + RsChatLobbyHistoryDataItem() : RsChatItem(RS_PKT_SUBTYPE_CHAT_LOBBY_HISTORY_DATA) {} + virtual ~RsChatLobbyHistoryDataItem() {} + + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx); + + uint64_t lobby_id; + std::vector msgs; +}; + struct PrivateOugoingMapItem : RsChatItem { diff --git a/src/pqi/p3historymgr.cc b/src/pqi/p3historymgr.cc index 15f9e91da3..f1e0c8c871 100644 --- a/src/pqi/p3historymgr.cc +++ b/src/pqi/p3historymgr.cc @@ -89,6 +89,8 @@ void p3HistoryMgr::addMessage(const ChatMessage& cm) RsIdentityDetails details; if (rsIdentity->getIdDetails(cm.lobby_peer_gxs_id, details)) peerName = details.mNickname; + else if (cm.lobby_peer_gxs_id.isNull() && !cm.peer_alternate_nickname.empty()) + peerName = cm.peer_alternate_nickname; else peerName = cm.lobby_peer_gxs_id.toStdString(); } diff --git a/src/retroshare/rschats.h b/src/retroshare/rschats.h index 74a06ed95a..ae45f68005 100644 --- a/src/retroshare/rschats.h +++ b/src/retroshare/rschats.h @@ -94,6 +94,25 @@ struct DistantChatPeerInfo : RsSerializable } }; +/// A single message entry transmitted as part of lobby history +struct LobbyHistoryMsgEntry : RsSerializable +{ + RsGxsId author_id; // GXS identity of the message author + std::string nick; // nickname of the author at time of sending + uint32_t send_time; // original send timestamp + std::string message; // message text content + bool incoming; // true if the message was received (not sent by us) + + void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx) override + { + RS_SERIAL_PROCESS(author_id); + RS_SERIAL_PROCESS(nick); + RS_SERIAL_PROCESS(send_time); + RS_SERIAL_PROCESS(message); + RS_SERIAL_PROCESS(incoming); + } +}; + enum class RsChatHistoryChangeFlags: uint8_t { SAME = 0x00, @@ -240,6 +259,8 @@ enum class RsChatLobbyEventCode: uint8_t CHAT_LOBBY_EVENT_PEER_JOINED = 0x07, // RS_CHAT_LOBBY_EVENT_PEER_JOINED CHAT_LOBBY_EVENT_PEER_CHANGE_NICKNAME = 0x08, // RS_CHAT_LOBBY_EVENT_PEER_CHANGE_NICKNAME CHAT_LOBBY_EVENT_KEEP_ALIVE = 0x09, // RS_CHAT_LOBBY_EVENT_KEEP_ALIVE + CHAT_LOBBY_EVENT_HISTORY_PROBE_RESPONSE = 0x0a, + CHAT_LOBBY_EVENT_HISTORY_DATA = 0x0b, }; enum class RsDistantChatEventCode: uint8_t @@ -253,16 +274,20 @@ enum class RsDistantChatEventCode: uint8_t struct RsChatLobbyEvent : RsEvent // This event handles events internal to the distributed chat system { - RsChatLobbyEvent() : RsEvent(RsEventType::CHAT_SERVICE),mEventCode(RsChatLobbyEventCode::UNKNOWN),mLobbyId(0),mTimeShift(0) {} + RsChatLobbyEvent() : RsEvent(RsEventType::CHAT_SERVICE),mEventCode(RsChatLobbyEventCode::UNKNOWN),mLobbyId(0),mTimeShift(0),mGenericCount(0) {} virtual ~RsChatLobbyEvent() override = default; RsChatLobbyEventCode mEventCode; uint64_t mLobbyId; RsGxsId mGxsId; + RsPeerId mPeerId; std::string mStr; ChatMessage mMsg; int mTimeShift; + uint32_t mGenericCount; // Used for returning probe counts/timestamps + + std::vector mHistoryMsgs; // Passing history messages void serial_process(RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext &ctx) override { @@ -271,9 +296,12 @@ struct RsChatLobbyEvent : RsEvent // This event handles events internal to the d RS_SERIAL_PROCESS(mEventCode); RS_SERIAL_PROCESS(mLobbyId); RS_SERIAL_PROCESS(mGxsId); + RS_SERIAL_PROCESS(mPeerId); RS_SERIAL_PROCESS(mStr); RS_SERIAL_PROCESS(mMsg); RS_SERIAL_PROCESS(mTimeShift); + RS_SERIAL_PROCESS(mGenericCount); + RS_SERIAL_PROCESS(mHistoryMsgs); } }; @@ -585,13 +613,39 @@ class RsChats */ virtual void setLobbyAutoSubscribe(const ChatLobbyId &lobby_id, const bool autoSubscribe) = 0 ; + virtual bool getLobbyAutoSubscribe(const ChatLobbyId &lobby_id) = 0 ; + /** - * @brief getLobbyAutoSubscribe get current value of auto subscribe + * @brief requestLobbyHistory send a probe to all participating friends of a lobby to check available chat history * @jsonapi{development} - * @param[in] lobby_id lobby to get value from - * @return wether lobby has auto subscribe enabled or disabled + * @param[in] lobby_id lobby to check + * @return true if probe was sent */ - virtual bool getLobbyAutoSubscribe(const ChatLobbyId &lobby_id) = 0 ; + virtual bool requestLobbyHistory(const ChatLobbyId& lobby_id) = 0 ; + + /** + * @brief requestLobbyHistoryFromPeer request chat history from a specific peer in a lobby + * @jsonapi{development} + * @param[in] lobby_id lobby for which to request history + * @param[in] peer_id peer to request history from + * @param[in] max_count maximum number of messages to fetch + * @param[in] oldest_ts maximum age (timestamp) of the oldest message + * @return true if request was sent + */ + virtual bool requestLobbyHistoryFromPeer(const ChatLobbyId& lobby_id, const RsPeerId& peer_id, uint32_t max_count, uint32_t oldest_ts) = 0 ; + + /** + * @brief allowHistorySharing enable or disable sharing of chat history with friends + * @param[in] allow set to true to allow sharing, false to disallow + * @return true on success + */ + virtual bool allowHistorySharing(bool allow) = 0 ; + + /** + * @brief isHistorySharingAllowed check if history sharing is allowed + * @return true if sharing is allowed, false otherwise + */ + virtual bool isHistorySharingAllowed() const = 0 ; /** * @brief createChatLobby create a new chat lobby