// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/SecretChatsManager.h" #include "td/telegram/DhCache.h" #include "td/telegram/EncryptedFile.h" #include "td/telegram/FolderId.h" #include "td/telegram/Global.h" #include "td/telegram/logevent/SecretChatEvent.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/SecretChatDb.h" #include "td/telegram/SequenceDispatcher.h" #include "td/telegram/StateManager.h" #include "td/telegram/TdDb.h" #include "td/telegram/telegram_api.h" #include "td/telegram/UserManager.h" #include "td/mtproto/DhCallback.h" #include "td/db/binlog/BinlogEvent.h" #include "td/db/binlog/BinlogHelper.h" #include "td/db/binlog/BinlogInterface.h" #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/Random.h" #include "td/utils/SliceBuilder.h" #include "td/utils/Status.h" #include "td/utils/Time.h" #include namespace td { // seq_no // 1. // x_in = 0 if we initiated secret chat. // x_in = 1 if other client initiated secret chat // x_out = 1 - x_in // 2. Send: // in_seq_no = my_in_seq_no * 2 + x_in // out_seq_no = my_out_seq_no * 2 + x_out // my_out_seq_no++; // // 3. Receive // fail_if (in_seq_no % 2 != (1 - x_in)), in_seq_no /= 2. // fail_if (out_seq_no % 2 != x_out), out_seq_no /= 2. // drop_if (out_seq_no < my_in_seq_no) // handle_gap_if(out_seq_no > my_in_seq_no) // my_in_seq_no++; // // fail_if(in_seq_no < his_in_seq_no) // his_in_seq_no = in_seq_no // fail_if(my_out_seq_no < his_in_seq_no) // // 4. Preventing gaps. // All messages must be sent in order of out_seq_no // Messages of older layer have imaginary seq_no = -1 // a. TODO use invokeAfter. // b. Just don't send next message before server accepted previous one. // // 5. Handling gaps. // TODO // Just fail chat. SecretChatsManager::SecretChatsManager(ActorShared<> parent, bool use_secret_chats) : use_secret_chats_(use_secret_chats), parent_(std::move(parent)) { } void SecretChatsManager::start_up() { if (!use_secret_chats_) { return; } class StateCallback final : public StateManager::Callback { public: explicit StateCallback(ActorId parent) : parent_(std::move(parent)) { } bool on_online(bool online_flag) final { send_closure(parent_, &SecretChatsManager::on_online, online_flag); return parent_.is_alive(); } private: ActorId parent_; }; send_closure(G()->state_manager(), &StateManager::add_callback, make_unique(actor_id(this))); } void SecretChatsManager::create_chat(UserId user_id, int64 user_access_hash, Promise promise) { int32 random_id; ActorId actor; do { random_id = Random::secure_int32() & 0x7fffffff; actor = create_chat_actor(random_id); } while (actor.empty()); send_closure(actor, &SecretChatActor::create_chat, user_id, user_access_hash, random_id, std::move(promise)); } void SecretChatsManager::cancel_chat(SecretChatId secret_chat_id, bool delete_history, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Unit()); send_closure(actor, &SecretChatActor::cancel_chat, delete_history, false, std::move(safe_promise)); } void SecretChatsManager::send_message(SecretChatId secret_chat_id, tl_object_ptr message, tl_object_ptr file, Promise<> promise) { // message->message_ = Random::fast_bool() ? string(1, static_cast(0x80)) : "a"; auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Status::Error(400, "Can't find secret chat")); send_closure(actor, &SecretChatActor::send_message, std::move(message), std::move(file), std::move(safe_promise)); } void SecretChatsManager::send_message_action(SecretChatId secret_chat_id, tl_object_ptr action) { auto actor = get_chat_actor(secret_chat_id.get()); if (actor.empty()) { return; } send_closure(actor, &SecretChatActor::send_message_action, std::move(action)); } void SecretChatsManager::send_read_history(SecretChatId secret_chat_id, int32 date, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Status::Error(400, "Can't find secret chat")); send_closure(actor, &SecretChatActor::send_read_history, date, std::move(safe_promise)); } void SecretChatsManager::send_open_message(SecretChatId secret_chat_id, int64 random_id, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Status::Error(400, "Can't find secret chat")); send_closure(actor, &SecretChatActor::send_open_message, random_id, std::move(safe_promise)); } void SecretChatsManager::delete_messages(SecretChatId secret_chat_id, vector random_ids, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Unit()); send_closure(actor, &SecretChatActor::delete_messages, std::move(random_ids), std::move(safe_promise)); } void SecretChatsManager::delete_all_messages(SecretChatId secret_chat_id, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Unit()); send_closure(actor, &SecretChatActor::delete_all_messages, std::move(safe_promise)); } void SecretChatsManager::notify_screenshot_taken(SecretChatId secret_chat_id, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Status::Error(400, "Can't find secret chat")); send_closure(actor, &SecretChatActor::notify_screenshot_taken, std::move(safe_promise)); } void SecretChatsManager::send_set_ttl_message(SecretChatId secret_chat_id, int32 ttl, int64 random_id, Promise<> promise) { auto actor = get_chat_actor(secret_chat_id.get()); auto safe_promise = SafePromise<>(std::move(promise), Status::Error(400, "Can't find secret chat")); send_closure(actor, &SecretChatActor::send_set_ttl_message, ttl, random_id, std::move(safe_promise)); } void SecretChatsManager::on_update_chat(tl_object_ptr update) { if (!use_secret_chats_ || close_flag_) { return; } PendingChatUpdate pending_update; pending_update.online_process_time_ = Timestamp::now(); if (update->chat_->get_id() == telegram_api::encryptedChatRequested::ID) { #if TD_ANDROID || TD_DARWIN_IOS pending_update.offline_process_time_ = Timestamp::in(1.0); #else pending_update.online_process_time_ = Timestamp::in(2.0); pending_update.offline_process_time_ = Timestamp::in(3.0); #endif } pending_update.update_ = std::move(update); pending_chat_updates_.push_back(std::move(pending_update)); flush_pending_chat_updates(); } void SecretChatsManager::do_update_chat(tl_object_ptr update) { auto actor_id = [this, chat = update->chat_.get()] { switch (chat->get_id()) { case telegram_api::encryptedChatEmpty::ID: return create_chat_actor(static_cast(chat)->id_); case telegram_api::encryptedChatWaiting::ID: return create_chat_actor(static_cast(chat)->id_); case telegram_api::encryptedChatRequested::ID: return create_chat_actor(static_cast(chat)->id_); case telegram_api::encryptedChat::ID: return create_chat_actor(static_cast(chat)->id_); case telegram_api::encryptedChatDiscarded::ID: return get_chat_actor(static_cast(chat)->id_); default: UNREACHABLE(); return ActorId(); } }(); send_closure(actor_id, &SecretChatActor::update_chat, std::move(update->chat_)); } void SecretChatsManager::on_new_message(tl_object_ptr &&message_ptr, Promise &&promise) { if (!use_secret_chats_ || close_flag_) { return promise.set_value(Unit()); } CHECK(message_ptr != nullptr); auto event = make_unique(); event->promise = std::move(promise); switch (message_ptr->get_id()) { case telegram_api::encryptedMessage::ID: { auto message = telegram_api::move_object_as(message_ptr); event->chat_id = message->chat_id_; event->date = message->date_; event->encrypted_message = std::move(message->bytes_); event->file = EncryptedFile::get_encrypted_file(std::move(message->file_)); break; } case telegram_api::encryptedMessageService::ID: { auto message = telegram_api::move_object_as(message_ptr); event->chat_id = message->chat_id_; event->date = message->date_; event->encrypted_message = std::move(message->bytes_); break; } default: UNREACHABLE(); } add_inbound_message(std::move(event)); } void SecretChatsManager::replay_binlog_event(BinlogEvent &&binlog_event) { if (!use_secret_chats_) { binlog_erase(G()->td_db()->get_binlog(), binlog_event.id_); return; } auto r_message = log_event::SecretChatEvent::from_buffer_slice(binlog_event.data_as_buffer_slice()); LOG_IF(FATAL, r_message.is_error()) << "Failed to deserialize event: " << r_message.error(); auto message = r_message.move_as_ok(); message->set_log_event_id(binlog_event.id_); LOG(INFO) << "Process binlog event " << *message; switch (message->get_type()) { case log_event::SecretChatEvent::Type::InboundSecretMessage: return replay_inbound_message(unique_ptr( static_cast(message.release()))); case log_event::SecretChatEvent::Type::OutboundSecretMessage: return replay_outbound_message(unique_ptr( static_cast(message.release()))); case log_event::SecretChatEvent::Type::CloseSecretChat: return replay_close_chat( unique_ptr(static_cast(message.release()))); case log_event::SecretChatEvent::Type::CreateSecretChat: return replay_create_chat( unique_ptr(static_cast(message.release()))); default: LOG(FATAL) << "Unknown log event type " << tag("type", format::as_hex(static_cast(message->get_type()))); } } void SecretChatsManager::binlog_replay_finish() { binlog_replay_finish_flag_ = true; for (auto &it : id_to_actor_) { send_closure(it.second, &SecretChatActor::binlog_replay_finish); } } void SecretChatsManager::replay_inbound_message(unique_ptr message) { LOG(INFO) << "Replay inbound secret message in chat " << message->chat_id; auto actor = get_chat_actor(message->chat_id); send_closure_later(actor, &SecretChatActor::replay_inbound_message, std::move(message)); } void SecretChatsManager::add_inbound_message(unique_ptr message) { LOG(INFO) << "Process inbound secret message in chat " << message->chat_id; auto actor = get_chat_actor(message->chat_id); send_closure(actor, &SecretChatActor::add_inbound_message, std::move(message)); } void SecretChatsManager::replay_close_chat(unique_ptr message) { LOG(INFO) << "Replay close secret chat " << message->chat_id; auto actor = get_chat_actor(message->chat_id); send_closure_later(actor, &SecretChatActor::replay_close_chat, std::move(message)); } void SecretChatsManager::replay_create_chat(unique_ptr message) { LOG(INFO) << "Replay create secret chat " << message->random_id; auto actor = create_chat_actor(message->random_id); send_closure_later(actor, &SecretChatActor::replay_create_chat, std::move(message)); } void SecretChatsManager::replay_outbound_message(unique_ptr message) { LOG(INFO) << "Replay outbound secret message in chat " << message->chat_id; auto actor = get_chat_actor(message->chat_id); send_closure_later(actor, &SecretChatActor::replay_outbound_message, std::move(message)); } ActorId SecretChatsManager::get_chat_actor(int32 id) { return create_chat_actor_impl(id, false); } ActorId SecretChatsManager::create_chat_actor(int32 id) { return create_chat_actor_impl(id, true); } unique_ptr SecretChatsManager::make_secret_chat_context(int32 id) { class Context final : public SecretChatActor::Context { public: Context(int32 id, ActorShared parent, unique_ptr secret_chat_db) : secret_chat_id_(SecretChatId(id)), parent_(std::move(parent)), secret_chat_db_(std::move(secret_chat_db)) { sequence_dispatcher_ = create_actor("SecretChat SequenceDispatcher"); } Context(const Context &) = delete; Context &operator=(const Context &) = delete; Context(Context &&) = delete; Context &operator=(Context &&) = delete; ~Context() final { send_closure(std::move(sequence_dispatcher_), &SequenceDispatcher::close_silent); } mtproto::DhCallback *dh_callback() final { return DhCache::instance(); } NetQueryCreator &net_query_creator() final { return G()->net_query_creator(); } BinlogInterface *binlog() final { return G()->td_db()->get_binlog(); } SecretChatDb *secret_chat_db() final { return secret_chat_db_.get(); } std::shared_ptr dh_config() final { return G()->get_dh_config(); } void set_dh_config(std::shared_ptr dh_config) final { G()->set_dh_config(std::move(dh_config)); } void send_net_query(NetQueryPtr query, ActorShared callback, bool ordered) final { if (ordered) { send_closure(sequence_dispatcher_, &SequenceDispatcher::send_with_callback, std::move(query), std::move(callback)); } else { G()->net_query_dispatcher().dispatch_with_callback(std::move(query), std::move(callback)); } } bool get_config_option_boolean(const string &name) const final { return G()->get_option_boolean(name); } int32 unix_time() final { return G()->unix_time(); } bool close_flag() final { return G()->close_flag(); } void on_update_secret_chat(int64 access_hash, UserId user_id, SecretChatState state, bool is_outbound, int32 ttl, int32 date, string key_hash, int32 layer, FolderId initial_folder_id) final { send_closure(G()->user_manager(), &UserManager::on_update_secret_chat, secret_chat_id_, access_hash, user_id, state, is_outbound, ttl, date, key_hash, layer, initial_folder_id); } void on_inbound_message(UserId user_id, MessageId message_id, int32 date, unique_ptr file, tl_object_ptr message, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_get_secret_message, secret_chat_id_, user_id, message_id, date, std::move(file), std::move(message), std::move(promise)); } void on_send_message_error(int64 random_id, Status error, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_send_secret_message_error, random_id, std::move(error), std::move(promise)); } void on_send_message_ack(int64 random_id) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_send_message_get_quick_ack, random_id); } void on_send_message_ok(int64 random_id, MessageId message_id, int32 date, unique_ptr file, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_send_secret_message_success, random_id, message_id, date, std::move(file), std::move(promise)); } void on_delete_messages(std::vector random_ids, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::delete_secret_messages, secret_chat_id_, std::move(random_ids), std::move(promise)); } void on_flush_history(bool remove_from_dialog_list, MessageId message_id, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::delete_secret_chat_history, secret_chat_id_, remove_from_dialog_list, message_id, std::move(promise)); } void on_read_message(int64 random_id, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::open_secret_message, secret_chat_id_, random_id, std::move(promise)); } void on_screenshot_taken(UserId user_id, MessageId message_id, int32 date, int64 random_id, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_secret_chat_screenshot_taken, secret_chat_id_, user_id, message_id, date, random_id, std::move(promise)); } void on_set_ttl(UserId user_id, MessageId message_id, int32 date, int32 ttl, int64 random_id, Promise<> promise) final { send_closure_later(G()->messages_manager(), &MessagesManager::on_secret_chat_ttl_changed, secret_chat_id_, user_id, message_id, date, ttl, random_id, std::move(promise)); } private: SecretChatId secret_chat_id_; ActorOwn sequence_dispatcher_; ActorShared parent_; unique_ptr secret_chat_db_; }; return make_unique(id, actor_shared(this, id), td::make_unique(G()->td_db()->get_binlog_pmc_shared(), id)); } ActorId SecretChatsManager::create_chat_actor_impl(int32 id, bool can_be_empty) { if (id == 0) { return Auto(); } auto it_flag = id_to_actor_.emplace(id, ActorOwn()); if (it_flag.second) { LOG(INFO) << "Create SecretChatActor: " << tag("id", id); it_flag.first->second = create_actor(PSLICE() << "SecretChat " << id, id, make_secret_chat_context(id), can_be_empty); if (binlog_replay_finish_flag_) { send_closure(it_flag.first->second, &SecretChatActor::binlog_replay_finish); } } return it_flag.first->second.get(); } void SecretChatsManager::hangup() { close_flag_ = true; for (auto &it : id_to_actor_) { LOG(INFO) << "Ask to close SecretChatActor " << tag("id", it.first); it.second.reset(); } if (id_to_actor_.empty()) { stop(); } } void SecretChatsManager::hangup_shared() { CHECK(use_secret_chats_); auto token = get_link_token(); auto it = id_to_actor_.find(static_cast(token)); CHECK(it != id_to_actor_.end()); LOG(INFO) << "Close SecretChatActor " << tag("id", it->first); it->second.release(); id_to_actor_.erase(it); if (close_flag_ && id_to_actor_.empty()) { stop(); } } void SecretChatsManager::timeout_expired() { flush_pending_chat_updates(); } void SecretChatsManager::flush_pending_chat_updates() { if (close_flag_ || !use_secret_chats_) { return; } auto it = pending_chat_updates_.begin(); while (it != pending_chat_updates_.end() && (is_online_ ? it->online_process_time_.is_in_past() : it->offline_process_time_.is_in_past())) { do_update_chat(std::move(it->update_)); ++it; } if (it != pending_chat_updates_.end()) { set_timeout_at(is_online_ ? it->online_process_time_.at() : it->offline_process_time_.at()); } pending_chat_updates_.erase(pending_chat_updates_.begin(), it); } void SecretChatsManager::on_online(bool is_online) { if (is_online_ == is_online) { return; } is_online_ = is_online; flush_pending_chat_updates(); } } // namespace td