diff --git a/build.html b/build.html index b9af48310..e16968de2 100644 --- a/build.html +++ b/build.html @@ -734,7 +734,7 @@ function onOptionsChanged() { pre_text.push('Install Git, ' + compiler + ', make, CMake >= 3.0.2, OpenSSL-dev, zlib-dev, gperf, PHP' + jdk + ' using your package manager.'); } if (os_linux && os.includes('Node.js')) { - pre_text.push('Note that for Node.js ≤ 9.11.2 you must build TDLight with OpenSSL 1.0.* and for Node.js ≥ 10 with OpenSSL 1.1.* instead, so you may need to modify the following commands to install a proper OpenSSL version.'); + pre_text.push('Note that for Node.js ≥ 17 you must build TDLib with OpenSSL 3.0.*, for Node.js ≥ 10 with OpenSSL 1.1.*, and for Node.js < 10 with OpenSSL 1.0.*, so you may need to modify the following commands to install a proper OpenSSL version.'); } if (os_freebsd) { pre_text.push('Note that the following instruction is for FreeBSD 11.'); diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 9bacc744c..29acb7de8 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2353,8 +2353,8 @@ callStateReady protocol:callProtocol servers:vector config:string en //@description The call is hanging up after discardCall has been called callStateHangingUp = CallState; -//@description The call has ended successfully @reason The reason, why the call has ended @need_rating True, if the call rating must be sent to the server @need_debug_information True, if the call debug information must be sent to the server -callStateDiscarded reason:CallDiscardReason need_rating:Bool need_debug_information:Bool = CallState; +//@description The call has ended successfully @reason The reason, why the call has ended @need_rating True, if the call rating must be sent to the server @need_debug_information True, if the call debug information must be sent to the server @need_log True, if the call log must be sent to the server +callStateDiscarded reason:CallDiscardReason need_rating:Bool need_debug_information:Bool need_log:Bool = CallState; //@description The call has ended with an error @error Error. An error with the code 4005000 will be returned if an outgoing call is missed because of an expired timeout callStateError error:error = CallState; @@ -3295,18 +3295,73 @@ userPrivacySettingAllowFindingByPhoneNumber = UserPrivacySetting; accountTtl days:int32 = AccountTtl; +//@class SessionType @description Represents the type of a session + +//@description The session is running on an Android device +sessionTypeAndroid = SessionType; + +//@description The session is running on a generic Apple device +sessionTypeApple = SessionType; + +//@description The session is running on the Brave browser +sessionTypeBrave = SessionType; + +//@description The session is running on the Chrome browser +sessionTypeChrome = SessionType; + +//@description The session is running on the Edge browser +sessionTypeEdge = SessionType; + +//@description The session is running on the Firefox browser +sessionTypeFirefox = SessionType; + +//@description The session is running on an iPad device +sessionTypeIpad = SessionType; + +//@description The session is running on an iPhone device +sessionTypeIphone = SessionType; + +//@description The session is running on a Linux device +sessionTypeLinux = SessionType; + +//@description The session is running on a Mac device +sessionTypeMac = SessionType; + +//@description The session is running on the Opera browser +sessionTypeOpera = SessionType; + +//@description The session is running on the Safari browser +sessionTypeSafari = SessionType; + +//@description The session is running on an Ubuntu device +sessionTypeUbuntu = SessionType; + +//@description The session is running on an unknown type of device +sessionTypeUnknown = SessionType; + +//@description The session is running on the Vivaldi browser +sessionTypeVivaldi = SessionType; + +//@description The session is running on a Windows device +sessionTypeWindows = SessionType; + +//@description The session is running on an Xbox console +sessionTypeXbox = SessionType; + + //@description Contains information about one session in a Telegram application used by the current user. Sessions must be shown to the user in the returned order //@id Session identifier @is_current True, if this session is the current session //@is_password_pending True, if a password is needed to complete authorization of the session //@can_accept_secret_chats True, if incoming secret chats can be accepted by the session //@can_accept_calls True, if incoming calls can be accepted by the session +//@type Session type based on the system and application version, which can be used to display a corresponding icon //@api_id Telegram API identifier, as provided by the application @application_name Name of the application, as provided by the application //@application_version The version of the application, as provided by the application @is_official_application True, if the application is an official application or uses the api_id of an official application //@device_model Model of the device the application has been run or is running on, as provided by the application @platform Operating system the application has been run or is running on, as provided by the application //@system_version Version of the operating system the application has been run or is running on, as provided by the application @log_in_date Point in time (Unix timestamp) when the user has logged in //@last_active_date Point in time (Unix timestamp) when the session was last used @ip IP address from which the session was created, in human-readable format //@country A two-letter country code for the country from which the session was created, based on the IP address @region Region code from which the session was created, based on the IP address -session id:int64 is_current:Bool is_password_pending:Bool can_accept_secret_chats:Bool can_accept_calls:Bool api_id:int32 application_name:string application_version:string is_official_application:Bool device_model:string platform:string system_version:string log_in_date:int32 last_active_date:int32 ip:string country:string region:string = Session; +session id:int64 is_current:Bool is_password_pending:Bool can_accept_secret_chats:Bool can_accept_calls:Bool type:SessionType api_id:int32 application_name:string application_version:string is_official_application:Bool device_model:string platform:string system_version:string log_in_date:int32 last_active_date:int32 ip:string country:string region:string = Session; //@description Contains a list of sessions @sessions List of sessions @inactive_session_ttl_days Number of days of inactivity before sessions will automatically be terminated; 1-366 days sessions sessions:vector inactive_session_ttl_days:int32 = Sessions; @@ -5442,9 +5497,12 @@ discardCall call_id:int32 is_disconnected:Bool duration:int32 is_video:Bool conn //@description Sends a call rating @call_id Call identifier @rating Call rating; 1-5 @comment An optional user comment if the rating is less than 5 @problems List of the exact types of problems with the call, specified by the user sendCallRating call_id:int32 rating:int32 comment:string problems:vector = Ok; -//@description Sends debug information for a call @call_id Call identifier @debug_information Debug information in application-specific format +//@description Sends debug information for a call to Telegram servers @call_id Call identifier @debug_information Debug information in application-specific format sendCallDebugInformation call_id:int32 debug_information:string = Ok; +//@description Sends log file for a call to Telegram servers @call_id Call identifier @log_file Call log file. Only inputFileLocal and inputFileGenerated are supported +sendCallLog call_id:int32 log_file:InputFile = Ok; + //@description Returns list of participant identifiers, on whose behalf a video chat in the chat can be joined @chat_id Chat identifier getVideoChatAvailableParticipants chat_id:int53 = MessageSenders; @@ -5610,7 +5668,7 @@ changeImportedContacts contacts:vector = ImportedContacts; clearImportedContacts = Ok; -//@description Searches a user by their phone number @phone_number Phone number to search for +//@description Searches a user by their phone number. Returns a 404 error if the user can't be found @phone_number Phone number to search for searchUserByPhoneNumber phone_number:string = User; //@description Shares the phone number of the current user with a mutual contact. Supposed to be called when the user clicks on chatActionBarSharePhoneNumber @user_id Identifier of the user with whom to share the phone number. The user must be a mutual contact diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 6ecfd1bfd..5d6f3d2b1 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -1797,6 +1797,7 @@ phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = U phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates; phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels; phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl; +phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; diff --git a/td/telegram/Account.cpp b/td/telegram/Account.cpp index 24d657ecf..2ccccb973 100644 --- a/td/telegram/Account.cpp +++ b/td/telegram/Account.cpp @@ -28,15 +28,85 @@ namespace td { +static td_api::object_ptr get_session_type_object( + const tl_object_ptr &authorization) { + auto contains = [](const string &str, const char *substr) { + return str.find(substr) != string::npos; + }; + + const string &app_name = authorization->app_name_; + auto device_model = to_lower(authorization->device_model_); + auto platform = to_lower(authorization->platform_); + auto system_version = to_lower(authorization->system_version_); + + if (device_model.find("xbox") != string::npos) { + return td_api::make_object(); + } + + bool is_web = [&] { + CSlice web_name("Web"); + auto pos = app_name.find(web_name.c_str()); + if (pos == string::npos) { + return false; + } + + auto next_character = app_name[pos + web_name.size()]; + return !('a' <= next_character && next_character <= 'z'); + }(); + + if (is_web) { + if (contains(device_model, "brave")) { + return td_api::make_object(); + } else if (contains(device_model, "vivaldi")) { + return td_api::make_object(); + } else if (contains(device_model, "opera") || contains(device_model, "opr")) { + return td_api::make_object(); + } else if (contains(device_model, "edg")) { + return td_api::make_object(); + } else if (contains(device_model, "chrome")) { + return td_api::make_object(); + } else if (contains(device_model, "firefox") || contains(device_model, "fxios")) { + return td_api::make_object(); + } else if (contains(device_model, "safari")) { + return td_api::make_object(); + } + } + + if (begins_with(platform, "android") || contains(system_version, "android")) { + return td_api::make_object(); + } else if (begins_with(platform, "windows") || contains(system_version, "windows")) { + return td_api::make_object(); + } else if (begins_with(platform, "ubuntu") || contains(system_version, "ubuntu")) { + return td_api::make_object(); + } else if (begins_with(platform, "linux") || contains(system_version, "linux")) { + return td_api::make_object(); + } + + auto is_ios = begins_with(platform, "ios") || contains(system_version, "ios"); + auto is_macos = begins_with(platform, "macos") || contains(system_version, "macos"); + if (is_ios && contains(device_model, "iphone")) { + return td_api::make_object(); + } else if (is_ios && contains(device_model, "ipad")) { + return td_api::make_object(); + } else if (is_macos && contains(device_model, "mac")) { + return td_api::make_object(); + } else if (is_ios || is_macos) { + return td_api::make_object(); + } + + return td_api::make_object(); +} + static td_api::object_ptr convert_authorization_object( tl_object_ptr &&authorization) { CHECK(authorization != nullptr); return td_api::make_object( authorization->hash_, authorization->current_, authorization->password_pending_, - !authorization->encrypted_requests_disabled_, !authorization->call_requests_disabled_, authorization->api_id_, - authorization->app_name_, authorization->app_version_, authorization->official_app_, authorization->device_model_, - authorization->platform_, authorization->system_version_, authorization->date_created_, - authorization->date_active_, authorization->ip_, authorization->country_, authorization->region_); + !authorization->encrypted_requests_disabled_, !authorization->call_requests_disabled_, + get_session_type_object(authorization), authorization->api_id_, authorization->app_name_, + authorization->app_version_, authorization->official_app_, authorization->device_model_, authorization->platform_, + authorization->system_version_, authorization->date_created_, authorization->date_active_, authorization->ip_, + authorization->country_, authorization->region_); } class SetAccountTtlQuery final : public Td::ResultHandler { diff --git a/td/telegram/AttachMenuManager.cpp b/td/telegram/AttachMenuManager.cpp index 17a243076..c5c13f04f 100644 --- a/td/telegram/AttachMenuManager.cpp +++ b/td/telegram/AttachMenuManager.cpp @@ -598,7 +598,9 @@ Result AttachMenuManager::get_attach_menu_bot( auto parsed_document = td_->documents_manager_->on_get_document(move_tl_object_as(icon->icon_), DialogId()); if (parsed_document.type != expected_document_type) { - LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id; + if (user_id != UserId(5000860301) || !G()->is_test_dc() || name != "macos_animated") { + LOG(ERROR) << "Receive wrong attachment menu bot icon \"" << name << "\" for " << user_id; + } continue; } bool expect_colors = false; diff --git a/td/telegram/CallActor.cpp b/td/telegram/CallActor.cpp index e43aeb96e..7e23512dc 100644 --- a/td/telegram/CallActor.cpp +++ b/td/telegram/CallActor.cpp @@ -10,6 +10,7 @@ #include "td/telegram/ContactsManager.h" #include "td/telegram/DhCache.h" #include "td/telegram/DialogId.h" +#include "td/telegram/files/FileManager.h" #include "td/telegram/Global.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQueryCreator.h" @@ -128,7 +129,7 @@ tl_object_ptr CallState::get_call_state_object() const { return make_tl_object(); case Type::Discarded: return make_tl_object(get_call_discard_reason_object(discard_reason), need_rating, - need_debug_information); + need_debug_information, need_log); case Type::Error: CHECK(error.is_error()); return make_tl_object(make_tl_object(error.code(), error.message().str())); @@ -159,7 +160,7 @@ void CallActor::create_call(UserId user_id, tl_object_ptr promise) { +void CallActor::accept_call(CallProtocol &&protocol, Promise promise) { if (state_ != State::SendAcceptQuery) { return promise.set_error(Status::Error(400, "Unexpected acceptCall")); } @@ -180,7 +181,7 @@ void CallActor::update_call_signaling_data(string data) { send_closure(G()->td(), &Td::send_update, std::move(update)); } -void CallActor::send_call_signaling_data(string &&data, Promise<> promise) { +void CallActor::send_call_signaling_data(string &&data, Promise promise) { if (call_state_.type != CallState::Type::Ready) { return promise.set_error(Status::Error(400, "Call is not active")); } @@ -199,7 +200,7 @@ void CallActor::send_call_signaling_data(string &&data, Promise<> promise) { } void CallActor::discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, - Promise<> promise) { + Promise promise) { promise.set_value(Unit()); if (state_ == State::Discarded || state_ == State::WaitDiscardResult || state_ == State::SendDiscardQuery) { return; @@ -244,7 +245,7 @@ void CallActor::discard_call(bool is_disconnected, int32 duration, bool is_video } void CallActor::rate_call(int32 rating, string comment, vector> &&problems, - Promise<> promise) { + Promise promise) { if (!call_state_.need_rating) { return promise.set_error(Status::Error(400, "Unexpected sendCallRating")); } @@ -309,11 +310,15 @@ void CallActor::on_set_rating_query_result(Result r_net_query) { if (res.is_error()) { return on_error(res.move_as_error()); } - call_state_.need_rating = false; + if (call_state_.need_rating) { + call_state_.need_rating = false; + call_state_need_flush_ = true; + loop(); + } send_closure(G()->updates_manager(), &UpdatesManager::on_get_updates, res.move_as_ok(), Promise()); } -void CallActor::send_call_debug_information(string data, Promise<> promise) { +void CallActor::send_call_debug_information(string data, Promise promise) { if (!call_state_.need_debug_information) { return promise.set_error(Status::Error(400, "Unexpected sendCallDebugInformation")); } @@ -323,17 +328,148 @@ void CallActor::send_call_debug_information(string data, Promise<> promise) { auto query = G()->net_query_creator().create(tl_query); send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this)](Result r_net_query) { - send_closure(actor_id, &CallActor::on_set_debug_query_result, std::move(r_net_query)); + send_closure(actor_id, &CallActor::on_save_debug_query_result, std::move(r_net_query)); })); loop(); } -void CallActor::on_set_debug_query_result(Result r_net_query) { +void CallActor::on_save_debug_query_result(Result r_net_query) { auto res = fetch_result(std::move(r_net_query)); if (res.is_error()) { return on_error(res.move_as_error()); } - call_state_.need_debug_information = false; + if (!res.ok() && !call_state_.need_log) { + call_state_.need_log = true; + call_state_need_flush_ = true; + } + if (call_state_.need_debug_information) { + call_state_.need_debug_information = false; + call_state_need_flush_ = true; + } + loop(); +} + +void CallActor::send_call_log(td_api::object_ptr log_file, Promise promise) { + if (!call_state_.need_log) { + return promise.set_error(Status::Error(400, "Unexpected sendCallLog")); + } + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); + auto r_file_id = file_manager->get_input_file_id(FileType::CallLog, log_file, DialogId(), false, false); + if (r_file_id.is_error()) { + return promise.set_error(Status::Error(400, r_file_id.error().message())); + } + auto file_id = r_file_id.move_as_ok(); + + FileView file_view = file_manager->get_file_view(file_id); + if (file_view.is_encrypted()) { + return promise.set_error(Status::Error(400, "Can't use encrypted file")); + } + if (!file_view.has_local_location() && !file_view.has_generate_location()) { + return promise.set_error(Status::Error(400, "Need local or generate location to upload call log")); + } + + upload_log_file(file_id, std::move(promise)); +} + +void CallActor::upload_log_file(FileId file_id, Promise &&promise) { + auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); + auto upload_file_id = file_manager->dup_file_id(file_id); + LOG(INFO) << "Ask to upload call log file " << upload_file_id; + + class UploadLogFileCallback final : public FileManager::UploadCallback { + ActorId actor_id_; + FileId file_id_; + Promise promise_; + + public: + UploadLogFileCallback(ActorId actor_id, FileId file_id, Promise &&promise) + : actor_id_(actor_id), file_id_(file_id), promise_(std::move(promise)) { + } + + void on_upload_ok(FileId file_id, tl_object_ptr input_file) final { + CHECK(file_id == file_id_); + send_closure_later(actor_id_, &CallActor::on_upload_log_file, file_id, std::move(promise_), + std::move(input_file)); + } + + void on_upload_encrypted_ok(FileId file_id, tl_object_ptr input_file) final { + UNREACHABLE(); + } + + void on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) final { + UNREACHABLE(); + } + + void on_upload_error(FileId file_id, Status error) final { + CHECK(file_id == file_id_); + send_closure_later(actor_id_, &CallActor::on_upload_log_file_error, file_id, std::move(promise_), + std::move(error)); + } + }; + + file_manager->upload(upload_file_id, + std::make_shared(actor_id(this), upload_file_id, std::move(promise)), 1, + 0); +} + +void CallActor::on_upload_log_file(FileId file_id, Promise &&promise, + tl_object_ptr input_file) { + LOG(INFO) << "Log file " << file_id << " has been uploaded"; + TRY_STATUS_PROMISE(promise, G()->close_status()); + + do_upload_log_file(file_id, std::move(input_file), std::move(promise)); +} + +void CallActor::on_upload_log_file_error(FileId file_id, Promise &&promise, Status status) { + LOG(WARNING) << "Log file " << file_id << " has upload error " << status; + CHECK(status.is_error()); + + TRY_STATUS_PROMISE(promise, G()->close_status()); + + promise.set_error(Status::Error(status.code() > 0 ? status.code() : 500, + status.message())); // TODO CHECK that status has always a code +} + +void CallActor::do_upload_log_file(FileId file_id, tl_object_ptr &&input_file, + Promise &&promise) { + if (input_file == nullptr) { + return promise.set_error(Status::Error(500, "Failed to reupload call log")); + } + + auto tl_query = telegram_api::phone_saveCallLog(get_input_phone_call("do_upload_log_file"), std::move(input_file)); + send_with_promise(G()->net_query_creator().create(tl_query), + PromiseCreator::lambda([actor_id = actor_id(this), file_id, + promise = std::move(promise)](Result r_net_query) mutable { + send_closure(actor_id, &CallActor::on_save_log_query_result, file_id, std::move(promise), + std::move(r_net_query)); + })); + loop(); +} + +void CallActor::on_save_log_query_result(FileId file_id, Promise promise, Result r_net_query) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + + auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); + file_manager->delete_partial_remote_location(file_id); + file_manager->cancel_upload(file_id); + + auto res = fetch_result(std::move(r_net_query)); + if (res.is_error()) { + auto error = res.move_as_error(); + if (begins_with(error.message(), "FILE_PART_") && ends_with(error.message(), "_MISSING")) { + // TODO on_upload_log_file_part_missing(file_id, to_integer(error.message().substr(10))); + // return; + } + return promise.set_error(std::move(error)); + } + if (call_state_.need_log) { + call_state_.need_log = false; + call_state_need_flush_ = true; + } + loop(); + promise.set_value(Unit()); } // Requests @@ -810,7 +946,7 @@ void CallActor::loop() { break; case State::Discarded: { if (call_state_.type == CallState::Type::Discarded && - (call_state_.need_rating || call_state_.need_debug_information)) { + (call_state_.need_rating || call_state_.need_debug_information || call_state_.need_log)) { break; } LOG(INFO) << "Close " << local_call_id_; diff --git a/td/telegram/CallActor.h b/td/telegram/CallActor.h index e2f70b844..0c936099b 100644 --- a/td/telegram/CallActor.h +++ b/td/telegram/CallActor.h @@ -9,6 +9,7 @@ #include "td/telegram/CallDiscardReason.h" #include "td/telegram/CallId.h" #include "td/telegram/DhConfig.h" +#include "td/telegram/files/FileId.h" #include "td/telegram/net/NetQuery.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" @@ -76,6 +77,7 @@ struct CallState { bool is_received{false}; bool need_debug_information{false}; bool need_rating{false}; + bool need_log{false}; int64 key_fingerprint{0}; string key; @@ -94,13 +96,14 @@ class CallActor final : public NetQueryCallback { void create_call(UserId user_id, tl_object_ptr &&input_user, CallProtocol &&protocol, bool is_video, Promise &&promise); - void accept_call(CallProtocol &&protocol, Promise<> promise); + void accept_call(CallProtocol &&protocol, Promise promise); void update_call_signaling_data(string data); - void send_call_signaling_data(string &&data, Promise<> promise); - void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise<> promise); + void send_call_signaling_data(string &&data, Promise promise); + void discard_call(bool is_disconnected, int32 duration, bool is_video, int64 connection_id, Promise promise); void rate_call(int32 rating, string comment, vector> &&problems, - Promise<> promise); - void send_call_debug_information(string data, Promise<> promise); + Promise promise); + void send_call_debug_information(string data, Promise promise); + void send_call_log(td_api::object_ptr log_file, Promise promise); void update_call(tl_object_ptr call); @@ -182,7 +185,18 @@ class CallActor final : public NetQueryCallback { void on_call_discarded(CallDiscardReason reason, bool need_rating, bool need_debug, bool is_video); void on_set_rating_query_result(Result r_net_query); - void on_set_debug_query_result(Result r_net_query); + + void on_save_debug_query_result(Result r_net_query); + + void upload_log_file(FileId file_id, Promise &&promise); + + void on_upload_log_file(FileId file_id, Promise &&promise, tl_object_ptr input_file); + + void on_upload_log_file_error(FileId file_id, Promise &&promise, Status status); + + void do_upload_log_file(FileId file_id, tl_object_ptr &&input_file, Promise &&promise); + + void on_save_log_query_result(FileId file_id, Promise promise, Result r_net_query); void on_get_call_config_result(Result r_net_query); diff --git a/td/telegram/CallManager.cpp b/td/telegram/CallManager.cpp index 92c3abe8e..702655775 100644 --- a/td/telegram/CallManager.cpp +++ b/td/telegram/CallManager.cpp @@ -70,7 +70,7 @@ void CallManager::create_call(UserId user_id, tl_object_ptr promise) { +void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise promise) { auto actor = get_call_actor(call_id); if (actor.empty()) { return promise.set_error(Status::Error(400, "Call not found")); @@ -78,7 +78,7 @@ void CallManager::accept_call(CallId call_id, CallProtocol &&protocol, Promise<> send_closure(actor, &CallActor::accept_call, std::move(protocol), std::move(promise)); } -void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promise<> promise) { +void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promise promise) { auto actor = get_call_actor(call_id); if (actor.empty()) { return promise.set_error(Status::Error(400, "Call not found")); @@ -87,7 +87,7 @@ void CallManager::send_call_signaling_data(CallId call_id, string &&data, Promis } void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id, - Promise<> promise) { + Promise promise) { auto actor = get_call_actor(call_id); if (actor.empty()) { return promise.set_error(Status::Error(400, "Call not found")); @@ -96,7 +96,7 @@ void CallManager::discard_call(CallId call_id, bool is_disconnected, int32 durat } void CallManager::rate_call(CallId call_id, int32 rating, string comment, - vector> &&problems, Promise<> promise) { + vector> &&problems, Promise promise) { auto actor = get_call_actor(call_id); if (actor.empty()) { return promise.set_error(Status::Error(400, "Call not found")); @@ -104,7 +104,7 @@ void CallManager::rate_call(CallId call_id, int32 rating, string comment, send_closure(actor, &CallActor::rate_call, rating, std::move(comment), std::move(problems), std::move(promise)); } -void CallManager::send_call_debug_information(CallId call_id, string data, Promise<> promise) { +void CallManager::send_call_debug_information(CallId call_id, string data, Promise promise) { auto actor = get_call_actor(call_id); if (actor.empty()) { return promise.set_error(Status::Error(400, "Call not found")); @@ -112,6 +112,14 @@ void CallManager::send_call_debug_information(CallId call_id, string data, Promi send_closure(actor, &CallActor::send_call_debug_information, std::move(data), std::move(promise)); } +void CallManager::send_call_log(CallId call_id, td_api::object_ptr log_file, Promise promise) { + auto actor = get_call_actor(call_id); + if (actor.empty()) { + return promise.set_error(Status::Error(400, "Call not found")); + } + send_closure(actor, &CallActor::send_call_log, std::move(log_file), std::move(promise)); +} + CallId CallManager::create_call_actor() { if (next_call_id_ == std::numeric_limits::max()) { next_call_id_ = 1; diff --git a/td/telegram/CallManager.h b/td/telegram/CallManager.h index 98923f3c6..2430fdc65 100644 --- a/td/telegram/CallManager.h +++ b/td/telegram/CallManager.h @@ -29,13 +29,20 @@ class CallManager final : public Actor { void create_call(UserId user_id, tl_object_ptr &&input_user, CallProtocol &&protocol, bool is_video, Promise promise); - void accept_call(CallId call_id, CallProtocol &&protocol, Promise<> promise); - void send_call_signaling_data(CallId call_id, string &&data, Promise<> promise); + + void accept_call(CallId call_id, CallProtocol &&protocol, Promise promise); + + void send_call_signaling_data(CallId call_id, string &&data, Promise promise); + void discard_call(CallId call_id, bool is_disconnected, int32 duration, bool is_video, int64 connection_id, - Promise<> promise); + Promise promise); + void rate_call(CallId call_id, int32 rating, string comment, - vector> &&problems, Promise<> promise); - void send_call_debug_information(CallId call_id, string data, Promise<> promise); + vector> &&problems, Promise promise); + + void send_call_debug_information(CallId call_id, string data, Promise promise); + + void send_call_log(CallId call_id, td_api::object_ptr log_file, Promise promise); private: bool close_flag_ = false; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index de7647377..f74c8180c 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -3318,6 +3318,11 @@ ContactsManager::~ContactsManager() = default; void ContactsManager::tear_down() { parent_.reset(); + + LOG(DEBUG) << "Have " << users_.size() << " users, " << chats_.size() << " basic groups, " << channels_.size() + << " supergroups and " << secret_chats_.size() << " secret chats to free"; + LOG(DEBUG) << "Have " << users_full_.size() << " full users, " << chats_full_.size() << " full basic groups and " + << channels_full_.size() << " full supergroups to free"; } UserId ContactsManager::load_my_id() { @@ -4657,20 +4662,26 @@ tl_object_ptr ContactsManager::get_input_encry return make_tl_object(secret_chat_id.get(), sc->access_hash); } +void ContactsManager::apply_pending_user_photo(User *u, UserId user_id) { + if (u == nullptr || u->is_photo_inited) { + return; + } + + auto it = pending_user_photos_.find(user_id); + if (it != pending_user_photos_.end()) { + do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo"); + pending_user_photos_.erase(it); + update_user(u, user_id); + } +} + const DialogPhoto *ContactsManager::get_user_dialog_photo(UserId user_id) { auto u = get_user(user_id); if (u == nullptr) { return nullptr; } - if (!u->is_photo_inited) { - auto it = pending_user_photos_.find(user_id); - if (it != pending_user_photos_.end()) { - do_update_user_photo(u, user_id, std::move(it->second), "get_user_dialog_photo"); - pending_user_photos_.erase(it); - update_user(u, user_id); - } - } + apply_pending_user_photo(u, user_id); return &u->photo; } @@ -5287,7 +5298,7 @@ std::pair, vector> ContactsManager::import_contacts(const do { random_id = Random::secure_int64(); - } while (random_id == 0 || imported_contacts_.find(random_id) != imported_contacts_.end()); + } while (random_id == 0 || imported_contacts_.count(random_id) > 0); imported_contacts_[random_id]; // reserve place for result do_import_contacts(contacts, random_id, std::move(promise)); @@ -8378,6 +8389,14 @@ ChannelId ContactsManager::get_channel_id(const tl_object_ptr &chat) { + auto channel_id = get_channel_id(chat); + if (channel_id.is_valid()) { + return DialogId(channel_id); + } + return DialogId(get_chat_id(chat)); +} + void ContactsManager::on_get_user(tl_object_ptr &&user_ptr, const char *source, bool is_me, bool expect_support) { LOG(DEBUG) << "Receive from " << source << ' ' << to_string(user_ptr); @@ -9808,18 +9827,18 @@ void ContactsManager::on_load_chat_full_from_database(ChatId chat_id, string val } } - if (td_->file_manager_->get_file_view(c->photo.small_file_id).get_unique_file_id() != - td_->file_manager_->get_file_view(as_fake_dialog_photo(chat_full->photo, DialogId(chat_id)).small_file_id) - .get_unique_file_id()) { + if (!is_same_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), chat_full->photo, c->photo)) { chat_full->photo = Photo(); if (c->photo.small_file_id.is_valid()) { reload_chat_full(chat_id, Auto()); } } - td_->group_call_manager_->on_update_dialog_about(DialogId(chat_id), chat_full->description, false); + auto photo = std::move(chat_full->photo); + chat_full->photo = Photo(); + on_update_chat_full_photo(chat_full, chat_id, std::move(photo)); - on_update_chat_full_photo(chat_full, chat_id, std::move(chat_full->photo)); + td_->group_call_manager_->on_update_dialog_about(DialogId(chat_id), chat_full->description, false); chat_full->is_update_chat_full_sent = true; update_chat_full(chat_full, chat_id, "on_load_chat_full_from_database", true); @@ -9919,15 +9938,14 @@ void ContactsManager::on_load_channel_full_from_database(ChannelId channel_id, s } } - if (td_->file_manager_->get_file_view(c->photo.small_file_id).get_unique_file_id() != - td_->file_manager_->get_file_view(as_fake_dialog_photo(channel_full->photo, DialogId(channel_id)).small_file_id) - .get_unique_file_id()) { + if (!is_same_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), channel_full->photo, c->photo)) { channel_full->photo = Photo(); if (c->photo.small_file_id.is_valid()) { channel_full->expires_at = 0.0; } } auto photo = std::move(channel_full->photo); + channel_full->photo = Photo(); on_update_channel_full_photo(channel_full, channel_id, std::move(photo)); if (channel_full->participant_count < channel_full->administrator_count) { @@ -10113,10 +10131,20 @@ void ContactsManager::update_user(User *u, UserId user_id, bool from_binlog, boo void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, bool from_database) { CHECK(c != nullptr); + bool need_update_chat_full = false; if (c->is_photo_changed) { td_->messages_manager_->on_dialog_photo_updated(DialogId(chat_id)); - drop_chat_photos(chat_id, !c->photo.small_file_id.is_valid(), true, "update_chat"); c->is_photo_changed = false; + + auto chat_full = get_chat_full(chat_id); // must not load ChatFull + if (chat_full != nullptr && + !is_same_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), chat_full->photo, c->photo)) { + on_update_chat_full_photo(chat_full, chat_id, Photo()); + need_update_chat_full = true; + if (c->photo.small_file_id.is_valid()) { + reload_chat_full(chat_id, Auto()); + } + } } if (c->is_title_changed) { td_->messages_manager_->on_dialog_title_updated(DialogId(chat_id)); @@ -10168,14 +10196,34 @@ void ContactsManager::update_chat(Chat *c, ChatId chat_id, bool from_binlog, boo LOG(INFO) << "Repairing cache of " << chat_id; reload_chat(chat_id, Promise()); } + + if (need_update_chat_full) { + auto chat_full = get_chat_full(chat_id); + CHECK(chat_full != nullptr); + update_chat_full(chat_full, chat_id, "drop_chat_photos"); + } } void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from_binlog, bool from_database) { CHECK(c != nullptr); + bool need_update_channel_full = false; if (c->is_photo_changed) { td_->messages_manager_->on_dialog_photo_updated(DialogId(channel_id)); - drop_channel_photos(channel_id, !c->photo.small_file_id.is_valid(), true, "update_channel"); c->is_photo_changed = false; + + auto channel_full = get_channel_full(channel_id, true, "update_channel"); + if (channel_full != nullptr && + !is_same_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), channel_full->photo, c->photo)) { + on_update_channel_full_photo(channel_full, channel_id, Photo()); + need_update_channel_full = true; + if (c->photo.small_file_id.is_valid()) { + if (channel_full->expires_at > 0.0) { + channel_full->expires_at = 0.0; + channel_full->need_save_to_database = true; + } + send_get_channel_full_query(channel_full, channel_id, Auto(), "update_channel"); + } + } } if (c->is_title_changed) { td_->messages_manager_->on_dialog_title_updated(DialogId(channel_id)); @@ -10283,6 +10331,12 @@ void ContactsManager::update_channel(Channel *c, ChannelId channel_id, bool from LOG(INFO) << "Repairing cache of " << channel_id; reload_channel(channel_id, Promise()); } + + if (need_update_channel_full) { + auto channel_full = get_channel_full(channel_id, true, "update_channel"); + CHECK(channel_full != nullptr); + update_channel_full(channel_full, channel_id, "update_channel"); + } } void ContactsManager::update_secret_chat(SecretChat *c, SecretChatId secret_chat_id, bool from_binlog, @@ -10494,6 +10548,8 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u return; } + apply_pending_user_photo(u, user_id); + td_->messages_manager_->on_update_dialog_notify_settings(DialogId(user_id), std::move(user->notify_settings_), "on_get_user_full"); @@ -10753,16 +10809,17 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c return promise.set_value(Unit()); } + Chat *c = get_chat(chat_id); + if (c == nullptr) { + LOG(ERROR) << "Can't find " << chat_id; + return promise.set_value(Unit()); + } { MessageId pinned_message_id; if ((chat->flags_ & CHAT_FULL_FLAG_HAS_PINNED_MESSAGE) != 0) { pinned_message_id = MessageId(ServerMessageId(chat->pinned_msg_id_)); } - Chat *c = get_chat(chat_id); - if (c == nullptr) { - LOG(ERROR) << "Can't find " << chat_id; - return promise.set_value(Unit()); - } else if (c->version >= c->pinned_message_version) { + if (c->version >= c->pinned_message_version) { LOG(INFO) << "Receive pinned " << pinned_message_id << " in " << chat_id << " with version " << c->version << ". Current version is " << c->pinned_message_version; td_->messages_manager_->on_update_dialog_last_pinned_message_id(DialogId(chat_id), pinned_message_id); @@ -10809,8 +10866,10 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c ChatFull *chat_full = add_chat_full(chat_id); on_update_chat_full_invite_link(chat_full, std::move(chat->exported_invite_)); - on_update_chat_full_photo(chat_full, chat_id, - get_photo(td_->file_manager_.get(), std::move(chat->chat_photo_), DialogId(chat_id))); + auto photo = get_photo(td_->file_manager_.get(), std::move(chat->chat_photo_), DialogId(chat_id)); + // on_update_chat_photo should be a no-op if server sent consistent data + on_update_chat_photo(c, as_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, photo)); + on_update_chat_full_photo(chat_full, chat_id, std::move(photo)); if (chat_full->description != chat->about_) { chat_full->description = std::move(chat->about_); chat_full->is_changed = true; @@ -10840,6 +10899,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c } chat_full->is_update_chat_full_sent = true; + update_chat(c, chat_id); update_chat_full(chat_full, chat_id, "on_get_chat_full"); } else { CHECK(chat_full_ptr->get_id() == telegram_api::channelFull::ID); @@ -10975,9 +11035,10 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c channel_full->need_save_to_database = true; } - on_update_channel_full_photo( - channel_full, channel_id, - get_photo(td_->file_manager_.get(), std::move(channel->chat_photo_), DialogId(channel_id))); + auto photo = get_photo(td_->file_manager_.get(), std::move(channel->chat_photo_), DialogId(channel_id)); + // on_update_channel_photo should be a no-op if server sent consistent data + on_update_channel_photo(c, as_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, photo)); + on_update_channel_full_photo(channel_full, channel_id, std::move(photo)); td_->messages_manager_->on_read_channel_outbox(channel_id, MessageId(ServerMessageId(channel->read_outbox_max_id_))); @@ -11108,6 +11169,7 @@ void ContactsManager::on_get_chat_full(tl_object_ptr &&c } channel_full->is_update_channel_full_sent = true; + update_channel(c, channel_id); update_channel_full(channel_full, channel_id, "on_get_channel_full"); if (linked_channel_id.is_valid()) { @@ -11296,7 +11358,7 @@ void ContactsManager::do_update_user_photo(User *u, UserId user_id, void ContactsManager::do_update_user_photo(User *u, UserId user_id, ProfilePhoto &&new_photo, bool invalidate_photo_cache, const char *source) { u->is_photo_inited = true; - if (new_photo != u->photo) { + if (need_update_profile_photo(u->photo, new_photo)) { LOG_IF(ERROR, u->access_hash == -1 && new_photo.small_file_id.is_valid()) << "Update profile photo of " << user_id << " without access hash from " << source; u->photo = new_photo; @@ -12579,26 +12641,6 @@ void ContactsManager::speculative_add_channel_user(ChannelId channel_id, UserId update_channel_full(channel_full, channel_id, "speculative_add_channel_user"); } -void ContactsManager::drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo, - const char *source) { - if (drop_channel_full_photo) { - auto channel_full = get_channel_full(channel_id, true, "drop_channel_photos"); // must not load ChannelFull - if (channel_full == nullptr) { - return; - } - - on_update_channel_full_photo(channel_full, channel_id, Photo()); - if (!is_empty) { - if (channel_full->expires_at > 0.0) { - channel_full->expires_at = 0.0; - channel_full->need_save_to_database = true; - } - send_get_channel_full_query(channel_full, channel_id, Auto(), "drop_channel_photos"); - } - update_channel_full(channel_full, channel_id, "drop_channel_photos"); - } -} - void ContactsManager::invalidate_channel_full(ChannelId channel_id, bool need_drop_slow_mode_delay) { LOG(INFO) << "Invalidate supergroup full for " << channel_id; auto channel_full = get_channel_full(channel_id, true, "invalidate_channel_full"); // must not load ChannelFull @@ -12632,9 +12674,6 @@ void ContactsManager::on_update_chat_full_photo(ChatFull *chat_full, ChatId chat chat_full->photo = std::move(photo); chat_full->is_changed = true; } - if (chat_full->photo.is_empty()) { - drop_chat_photos(chat_id, true, false, "on_update_chat_full_photo"); - } auto photo_file_ids = photo_get_file_ids(chat_full->photo); if (chat_full->registered_photo_file_ids == photo_file_ids) { @@ -12669,9 +12708,6 @@ void ContactsManager::on_update_channel_full_photo(ChannelFull *channel_full, Ch channel_full->photo = std::move(photo); channel_full->is_changed = true; } - if (channel_full->photo.is_empty()) { - drop_channel_photos(channel_id, true, false, "on_update_channel_full_photo"); - } auto photo_file_ids = photo_get_file_ids(channel_full->photo); if (channel_full->registered_photo_file_ids == photo_file_ids) { @@ -12950,7 +12986,7 @@ void ContactsManager::on_get_dialog_invite_link_info(const string &invite_link, LOG(ERROR) << "Receive invalid " << channel_id; channel_id = ChannelId(); } - if (!channel_id.is_valid() || accessible_before < 0) { + if (accessible_before != 0 && (!channel_id.is_valid() || accessible_before < 0)) { LOG(ERROR) << "Receive expires = " << accessible_before << " for invite link " << invite_link << " to " << to_string(chat); accessible_before = 0; @@ -13449,14 +13485,16 @@ void ContactsManager::on_update_chat_participant_count(Chat *c, ChatId chat_id, void ContactsManager::on_update_chat_photo(Chat *c, ChatId chat_id, tl_object_ptr &&chat_photo_ptr) { - DialogPhoto new_chat_photo = - get_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, std::move(chat_photo_ptr)); + on_update_chat_photo(c, get_dialog_photo(td_->file_manager_.get(), DialogId(chat_id), 0, std::move(chat_photo_ptr))); +} + +void ContactsManager::on_update_chat_photo(Chat *c, DialogPhoto &&photo) { if (td_->auth_manager_->is_bot()) { - new_chat_photo.minithumbnail.clear(); + photo.minithumbnail.clear(); } - if (new_chat_photo != c->photo) { - c->photo = new_chat_photo; + if (need_update_dialog_photo(c->photo, photo)) { + c->photo = std::move(photo); c->is_photo_changed = true; c->need_save_to_database = true; } @@ -13555,25 +13593,9 @@ void ContactsManager::on_update_chat_full_participants(ChatFull *chat_full, Chat update_chat_online_member_count(chat_full, chat_id, true); } -void ContactsManager::drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_chat_full_photo, const char *source) { - if (drop_chat_full_photo) { - auto chat_full = get_chat_full(chat_id); // must not load ChatFull - if (chat_full == nullptr) { - return; - } - - on_update_chat_full_photo(chat_full, chat_id, Photo()); - if (!is_empty) { - reload_chat_full(chat_id, Auto()); - } - update_chat_full(chat_full, chat_id, "drop_chat_photos"); - } -} - void ContactsManager::drop_chat_full(ChatId chat_id) { ChatFull *chat_full = get_chat_full_force(chat_id, "drop_chat_full"); if (chat_full == nullptr) { - drop_chat_photos(chat_id, false, false, "drop_chat_full"); return; } @@ -13591,14 +13613,17 @@ void ContactsManager::drop_chat_full(ChatId chat_id) { void ContactsManager::on_update_channel_photo(Channel *c, ChannelId channel_id, tl_object_ptr &&chat_photo_ptr) { - DialogPhoto new_chat_photo = - get_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, std::move(chat_photo_ptr)); + on_update_channel_photo( + c, get_dialog_photo(td_->file_manager_.get(), DialogId(channel_id), c->access_hash, std::move(chat_photo_ptr))); +} + +void ContactsManager::on_update_channel_photo(Channel *c, DialogPhoto &&photo) { if (td_->auth_manager_->is_bot()) { - new_chat_photo.minithumbnail.clear(); + photo.minithumbnail.clear(); } - if (new_chat_photo != c->photo) { - c->photo = new_chat_photo; + if (need_update_dialog_photo(c->photo, photo)) { + c->photo = std::move(photo); c->is_photo_changed = true; c->need_save_to_database = true; } @@ -14358,7 +14383,7 @@ std::pair> ContactsManager::get_user_profile_photos return result; } - get_user_dialog_photo(user_id); // apply pending user photo + apply_pending_user_photo(get_user(user_id), user_id); auto user_photos = &user_photos_[user_id]; if (user_photos->getting_now) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 906d64c04..bf4d7d893 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -80,6 +80,7 @@ class ContactsManager final : public Actor { static UserId get_user_id(const tl_object_ptr &user); static ChatId get_chat_id(const tl_object_ptr &chat); static ChannelId get_channel_id(const tl_object_ptr &chat); + static DialogId get_dialog_id(const tl_object_ptr &chat); Result> get_input_user(UserId user_id) const; @@ -1229,6 +1230,7 @@ class ContactsManager final : public Actor { const char *source); void do_update_user_photo(User *u, UserId user_id, ProfilePhoto &&new_photo, bool invalidate_photo_cache, const char *source); + void apply_pending_user_photo(User *u, UserId user_id); void upload_profile_photo(FileId file_id, bool is_animation, double main_frame_timestamp, Promise &&promise, int reupload_count = 0, vector bad_parts = {}); @@ -1258,6 +1260,7 @@ class ContactsManager final : public Actor { void on_update_chat_participant_count(Chat *c, ChatId chat_id, int32 participant_count, int32 version, const string &debug_str); void on_update_chat_photo(Chat *c, ChatId chat_id, tl_object_ptr &&chat_photo_ptr); + void on_update_chat_photo(Chat *c, DialogPhoto &&photo); static void on_update_chat_title(Chat *c, ChatId chat_id, string &&title); static void on_update_chat_active(Chat *c, ChatId chat_id, bool is_active); static void on_update_chat_migrated_to_channel_id(Chat *c, ChatId chat_id, ChannelId migrated_to_channel_id); @@ -1272,6 +1275,7 @@ class ContactsManager final : public Actor { void on_update_channel_photo(Channel *c, ChannelId channel_id, tl_object_ptr &&chat_photo_ptr); + void on_update_channel_photo(Channel *c, DialogPhoto &&photo); static void on_update_channel_title(Channel *c, ChannelId channel_id, string &&title); void on_update_channel_username(Channel *c, ChannelId channel_id, string &&username); void on_update_channel_status(Channel *c, ChannelId channel_id, DialogParticipantStatus &&status); @@ -1310,11 +1314,8 @@ class ContactsManager final : public Actor { void speculative_add_channel_user(ChannelId channel_id, UserId user_id, const DialogParticipantStatus &new_status, const DialogParticipantStatus &old_status); - void drop_chat_photos(ChatId chat_id, bool is_empty, bool drop_chat_full_photo, const char *source); void drop_chat_full(ChatId chat_id); - void drop_channel_photos(ChannelId channel_id, bool is_empty, bool drop_channel_full_photo, const char *source); - void do_invalidate_channel_full(ChannelFull *channel_full, ChannelId channel_id, bool need_drop_slow_mode_delay); void update_user_online_member_count(User *u); diff --git a/td/telegram/DialogFilter.hpp b/td/telegram/DialogFilter.hpp index 0921e88c7..2becce043 100644 --- a/td/telegram/DialogFilter.hpp +++ b/td/telegram/DialogFilter.hpp @@ -50,9 +50,9 @@ void DialogFilter::store(StorerT &storer) const { template void DialogFilter::parse(ParserT &parser) { using td::parse; - bool has_pinned_dialog_ids = !pinned_dialog_ids.empty(); - bool has_included_dialog_ids = !included_dialog_ids.empty(); - bool has_excluded_dialog_ids = !excluded_dialog_ids.empty(); + bool has_pinned_dialog_ids; + bool has_included_dialog_ids; + bool has_excluded_dialog_ids; BEGIN_PARSE_FLAGS(); PARSE_FLAG(exclude_muted); PARSE_FLAG(exclude_read); diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index dec9049e9..f04690ec1 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -2435,36 +2435,30 @@ tl_object_ptr get_fake_input_media(Td *td, tl_object_p FileId file_id) { FileView file_view = td->file_manager_->get_file_view(file_id); auto file_type = file_view.get_type(); - switch (file_type) { - case FileType::Animation: - case FileType::Audio: - case FileType::Document: - case FileType::Sticker: - case FileType::Video: - case FileType::VoiceNote: { - vector> attributes; - auto file_path = file_view.suggested_path(); - const PathView path_view(file_path); - Slice file_name = path_view.file_name(); - if (!file_name.empty()) { - attributes.push_back(make_tl_object(file_name.str())); - } - string mime_type = MimeType::from_extension(path_view.extension()); - int32 flags = 0; - if (file_type == FileType::Video) { - flags |= telegram_api::inputMediaUploadedDocument::NOSOUND_VIDEO_MASK; - } - return make_tl_object( - flags, false /*ignored*/, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes), - vector>(), 0); + if (is_document_file_type(file_type)) { + vector> attributes; + auto file_path = file_view.suggested_path(); + const PathView path_view(file_path); + Slice file_name = path_view.file_name(); + if (!file_name.empty()) { + attributes.push_back(make_tl_object(file_name.str())); } - case FileType::Photo: - return make_tl_object( - 0, std::move(input_file), vector>(), 0); - default: - UNREACHABLE(); + string mime_type = MimeType::from_extension(path_view.extension()); + int32 flags = 0; + if (file_type == FileType::Video) { + flags |= telegram_api::inputMediaUploadedDocument::NOSOUND_VIDEO_MASK; + } + if (file_type == FileType::DocumentAsFile) { + flags |= telegram_api::inputMediaUploadedDocument::FORCE_FILE_MASK; + } + return make_tl_object( + flags, false /*ignored*/, false /*ignored*/, std::move(input_file), nullptr, mime_type, std::move(attributes), + vector>(), 0); + } else { + CHECK(file_type == FileType::Photo); + return make_tl_object( + 0, std::move(input_file), vector>(), 0); } - return nullptr; } void delete_message_content_thumbnail(MessageContent *content, Td *td) { @@ -5769,10 +5763,6 @@ bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogActi return !td->stickers_manager_->is_sent_animated_emoji_click(dialog_id, remove_emoji_modifiers(emoji)); } -bool is_active_reaction(Td *td, const string &reaction) { - return td->stickers_manager_->is_active_reaction(reaction); -} - void init_stickers_manager(Td *td) { td->stickers_manager_->init(); } diff --git a/td/telegram/MessageContent.h b/td/telegram/MessageContent.h index edb52d201..1d996453d 100644 --- a/td/telegram/MessageContent.h +++ b/td/telegram/MessageContent.h @@ -248,8 +248,6 @@ void on_sent_message_content(Td *td, const MessageContent *content); bool is_unsent_animated_emoji_click(Td *td, DialogId dialog_id, const DialogAction &action); -bool is_active_reaction(Td *td, const string &reaction); - void init_stickers_manager(Td *td); void on_dialog_used(TopDialogCategory category, DialogId dialog_id, int32 date); diff --git a/td/telegram/MessageReaction.cpp b/td/telegram/MessageReaction.cpp index feade1105..95b6f1e1e 100644 --- a/td/telegram/MessageReaction.cpp +++ b/td/telegram/MessageReaction.cpp @@ -217,6 +217,17 @@ class GetMessageReactionsListQuery final : public Td::ResultHandler { } }; +void MessageReaction::add_recent_chooser_dialog_id(DialogId dialog_id) { + recent_chooser_dialog_ids_.insert(recent_chooser_dialog_ids_.begin(), dialog_id); + if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS) { + recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS); + } +} + +bool MessageReaction::remove_recent_chooser_dialog_id(DialogId dialog_id) { + return td::remove(recent_chooser_dialog_ids_, dialog_id); +} + void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, bool can_get_added_reactions) { if (is_chosen_ == is_chosen) { return; @@ -227,12 +238,9 @@ void MessageReaction::set_is_chosen(bool is_chosen, DialogId chooser_dialog_id, if (chooser_dialog_id.is_valid()) { choose_count_ += is_chosen_ ? 1 : -1; if (can_get_added_reactions) { - td::remove(recent_chooser_dialog_ids_, chooser_dialog_id); + remove_recent_chooser_dialog_id(chooser_dialog_id); if (is_chosen_) { - recent_chooser_dialog_ids_.insert(recent_chooser_dialog_ids_.begin(), chooser_dialog_id); - if (recent_chooser_dialog_ids_.size() > MAX_RECENT_CHOOSERS) { - recent_chooser_dialog_ids_.resize(MAX_RECENT_CHOOSERS); - } + add_recent_chooser_dialog_id(chooser_dialog_id); } } } @@ -410,6 +418,24 @@ void MessageReactions::sort_reactions(const FlatHashMap &active_ }); } +void MessageReactions::fix_chosen_reaction(DialogId my_dialog_id) { + bool need_fix = false; + for (auto &reaction : reactions_) { + if (!reaction.is_chosen() && reaction.remove_recent_chooser_dialog_id(my_dialog_id)) { + LOG(WARNING) << "Fix recent chosen reaction in " << *this; + need_fix = true; + } + } + if (!need_fix) { + return; + } + for (auto &reaction : reactions_) { + if (reaction.is_chosen() && !td::contains(reaction.get_recent_chooser_dialog_ids(), my_dialog_id)) { + reaction.add_recent_chooser_dialog_id(my_dialog_id); + } + } +} + bool MessageReactions::need_update_message_reactions(const MessageReactions *old_reactions, const MessageReactions *new_reactions) { if (old_reactions == nullptr) { diff --git a/td/telegram/MessageReaction.h b/td/telegram/MessageReaction.h index 2b59a6479..a2ce9e918 100644 --- a/td/telegram/MessageReaction.h +++ b/td/telegram/MessageReaction.h @@ -78,6 +78,10 @@ class MessageReaction { return recent_chooser_min_channels_; } + void add_recent_chooser_dialog_id(DialogId dialog_id); + + bool remove_recent_chooser_dialog_id(DialogId dialog_id); + td_api::object_ptr get_message_reaction_object(Td *td) const; template @@ -149,6 +153,8 @@ struct MessageReactions { void sort_reactions(const FlatHashMap &active_reaction_pos); + void fix_chosen_reaction(DialogId my_dialog_id); + static bool need_update_message_reactions(const MessageReactions *old_reactions, const MessageReactions *new_reactions); diff --git a/td/telegram/MessagesManager.cpp b/td/telegram/MessagesManager.cpp index adf6a71a6..b725bb666 100644 --- a/td/telegram/MessagesManager.cpp +++ b/td/telegram/MessagesManager.cpp @@ -6455,7 +6455,7 @@ void MessagesManager::skip_old_pending_pts_update(tl_object_ptrget_id() == telegram_api::updateNewMessage::ID) { auto update_new_message = static_cast(update.get()); auto full_message_id = get_full_message_id(update_new_message->message_, false); - if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) { + if (update_message_ids_.count(full_message_id) > 0) { if (new_pts == old_pts) { // otherwise message can be already deleted // apply sent message anyway on_get_message(std::move(update_new_message->message_), true, false, false, true, true, @@ -6716,41 +6716,28 @@ void MessagesManager::on_update_message_reactions(FullMessageId full_message_id, Promise &&promise) { TRY_STATUS_PROMISE(promise, G()->close_status()); + auto new_reactions = MessageReactions::get_message_reactions(td_, std::move(reactions), td_->auth_manager_->is_bot()); if (!have_message_force(full_message_id, "on_update_message_reactions")) { auto dialog_id = full_message_id.get_dialog_id(); if (!have_input_peer(dialog_id, AccessRights::Read)) { LOG(INFO) << "Ignore updateMessageReaction in inaccessible " << full_message_id; return; } - - switch (dialog_id.get_type()) { - case DialogType::User: - case DialogType::Chat: { - const Dialog *d = get_dialog(dialog_id); - if (d == nullptr) { - LOG(INFO) << "Ignore updateMessageReaction in unknown " << dialog_id; - return; - } - if (d->last_new_message_id != MessageId() && full_message_id.get_message_id() > d->last_new_message_id) { - LOG(INFO) << "Ignore updateMessageReaction about too new " << full_message_id << ", last known is " - << d->last_new_message_id; - return; - } - break; - } - case DialogType::Channel: - // the message will be added after get_channel_difference_if_needed - break; - case DialogType::SecretChat: - default: - UNREACHABLE(); - break; + const Dialog *d = get_dialog(dialog_id); + if (d == nullptr) { + LOG(INFO) << "Ignore updateMessageReaction in unknown " << dialog_id; + return; } - LOG(INFO) << "Need to load " << full_message_id << " to process updateMessageReaction"; - return get_message_from_server(full_message_id, std::move(promise), "on_update_message_reactions"); + + // there is no message, so the update can be ignored + if ((new_reactions != nullptr && !new_reactions->unread_reactions_.empty()) || d->unread_reaction_count > 0) { + // but if there are unread reactions or the chat has unread reactions, + // then number of unread reactions could have been changed, so reload the number of unread reactions + send_get_dialog_query(dialog_id, std::move(promise), 0, "on_update_message_reactions"); + } + return; } - auto new_reactions = MessageReactions::get_message_reactions(td_, std::move(reactions), td_->auth_manager_->is_bot()); update_message_interaction_info(full_message_id, -1, -1, false, nullptr, true, std::move(new_reactions)); promise.set_value(Unit()); } @@ -7063,6 +7050,11 @@ bool MessagesManager::update_message_interaction_info(Dialog *d, Message *m, int reactions->update_from(*m->reactions); } reactions->sort_reactions(active_reaction_pos_); + reactions->fix_chosen_reaction(get_my_dialog_id()); + if (d->default_send_message_as_dialog_id.is_valid()) { + // the reaction could be set by previous owner of the broadcast + // reactions->fix_chosen_reaction(d->default_send_message_as_dialog_id); + } } bool need_update_reactions = has_reactions && MessageReactions::need_update_message_reactions(m->reactions.get(), reactions.get()); @@ -7679,7 +7671,7 @@ void MessagesManager::add_pending_channel_update(DialogId dialog_id, tl_object_p auto update_new_channel_message = static_cast(update.get()); auto message_id = get_message_id(update_new_channel_message->message_, false); FullMessageId full_message_id(dialog_id, message_id); - if (update_message_ids_.find(full_message_id) != update_message_ids_.end()) { + if (update_message_ids_.count(full_message_id) > 0) { // apply sent channel message on_get_message(std::move(update_new_channel_message->message_), true, true, false, true, true, "updateNewChannelMessage with an awaited message"); @@ -7755,79 +7747,78 @@ bool MessagesManager::is_old_channel_update(DialogId dialog_id, int32 new_pts) { return new_pts <= (d == nullptr ? load_channel_pts(dialog_id) : d->pts); } -void MessagesManager::process_pts_update(tl_object_ptr &&update) { - switch (update->get_id()) { +void MessagesManager::process_pts_update(tl_object_ptr &&update_ptr) { + switch (update_ptr->get_id()) { case dummyUpdate::ID: LOG(INFO) << "Process dummyUpdate"; break; case telegram_api::updateNewMessage::ID: { - auto update_new_message = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateNewMessage"; - on_get_message(std::move(update_new_message->message_), true, false, false, true, true, "updateNewMessage"); + on_get_message(std::move(update->message_), true, false, false, true, true, "updateNewMessage"); break; } case updateSentMessage::ID: { - auto update_sent_message = move_tl_object_as(update); - LOG(INFO) << "Process updateSentMessage " << update_sent_message->random_id_; - on_send_message_success(update_sent_message->random_id_, update_sent_message->message_id_, - update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + auto update = move_tl_object_as(update_ptr); + LOG(INFO) << "Process updateSentMessage " << update->random_id_; + on_send_message_success(update->random_id_, update->message_id_, update->date_, update->ttl_period_, FileId(), "process updateSentMessage"); break; } case telegram_api::updateReadMessagesContents::ID: { - auto read_contents_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateReadMessageContents"; - for (auto &message_id : read_contents_update->messages_) { + for (auto &message_id : update->messages_) { read_message_content_from_updates(MessageId(ServerMessageId(message_id))); } break; } case telegram_api::updateEditMessage::ID: { - auto update_edit_message = move_tl_object_as(update); - auto full_message_id = on_get_message(std::move(update_edit_message->message_), false, false, false, false, false, - "updateEditMessage"); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateEditMessage"; - on_message_edited(full_message_id, update_edit_message->pts_); + bool had_message = have_message_force(get_full_message_id(update->message_, false), "updateEditMessage"); + auto full_message_id = + on_get_message(std::move(update->message_), false, false, false, false, false, "updateEditMessage"); + on_message_edited(full_message_id, update->pts_, had_message); break; } case telegram_api::updateDeleteMessages::ID: { - auto delete_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateDeleteMessages"; vector message_ids; - for (auto message : delete_update->messages_) { + for (auto message : update->messages_) { message_ids.push_back(MessageId(ServerMessageId(message))); } delete_messages_from_updates(message_ids); break; } case telegram_api::updateReadHistoryInbox::ID: { - auto read_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateReadHistoryInbox"; - DialogId dialog_id(read_update->peer_); + DialogId dialog_id(update->peer_); FolderId folder_id; - if ((read_update->flags_ & telegram_api::updateReadHistoryInbox::FOLDER_ID_MASK) != 0) { - folder_id = FolderId(read_update->folder_id_); + if ((update->flags_ & telegram_api::updateReadHistoryInbox::FOLDER_ID_MASK) != 0) { + folder_id = FolderId(update->folder_id_); } on_update_dialog_folder_id(dialog_id, folder_id); - read_history_inbox(dialog_id, MessageId(ServerMessageId(read_update->max_id_)), - -1 /*read_update->still_unread_count*/, "updateReadHistoryInbox"); + read_history_inbox(dialog_id, MessageId(ServerMessageId(update->max_id_)), -1 /*update->still_unread_count*/, + "updateReadHistoryInbox"); break; } case telegram_api::updateReadHistoryOutbox::ID: { - auto read_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateReadHistoryOutbox"; - read_history_outbox(DialogId(read_update->peer_), MessageId(ServerMessageId(read_update->max_id_))); + read_history_outbox(DialogId(update->peer_), MessageId(ServerMessageId(update->max_id_))); break; } case telegram_api::updatePinnedMessages::ID: { - auto pinned_messages_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updatePinnedMessages"; vector message_ids; - for (auto message : pinned_messages_update->messages_) { + for (auto message : update->messages_) { message_ids.push_back(MessageId(ServerMessageId(message))); } - update_dialog_pinned_messages_from_updates(DialogId(pinned_messages_update->peer_), message_ids, - pinned_messages_update->pinned_); + update_dialog_pinned_messages_from_updates(DialogId(update->peer_), message_ids, update->pinned_); break; } default: @@ -7836,37 +7827,35 @@ void MessagesManager::process_pts_update(tl_object_ptr &&u CHECK(!td_->updates_manager_->running_get_difference()); } -void MessagesManager::process_channel_update(tl_object_ptr &&update) { - switch (update->get_id()) { +void MessagesManager::process_channel_update(tl_object_ptr &&update_ptr) { + switch (update_ptr->get_id()) { case dummyUpdate::ID: LOG(INFO) << "Process dummyUpdate"; break; case updateSentMessage::ID: { - auto update_sent_message = move_tl_object_as(update); - LOG(INFO) << "Process updateSentMessage " << update_sent_message->random_id_; - on_send_message_success(update_sent_message->random_id_, update_sent_message->message_id_, - update_sent_message->date_, update_sent_message->ttl_period_, FileId(), + auto update = move_tl_object_as(update_ptr); + LOG(INFO) << "Process updateSentMessage " << update->random_id_; + on_send_message_success(update->random_id_, update->message_id_, update->date_, update->ttl_period_, FileId(), "process updateSentChannelMessage"); break; } case telegram_api::updateNewChannelMessage::ID: { - auto update_new_channel_message = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateNewChannelMessage"; - on_get_message(std::move(update_new_channel_message->message_), true, true, false, true, true, - "updateNewChannelMessage"); + on_get_message(std::move(update->message_), true, true, false, true, true, "updateNewChannelMessage"); break; } case telegram_api::updateDeleteChannelMessages::ID: { - auto delete_channel_messages_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateDeleteChannelMessages"; - ChannelId channel_id(delete_channel_messages_update->channel_id_); + ChannelId channel_id(update->channel_id_); if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; break; } vector message_ids; - for (auto &message : delete_channel_messages_update->messages_) { + for (auto &message : update->messages_) { message_ids.push_back(MessageId(ServerMessageId(message))); } @@ -7875,29 +7864,29 @@ void MessagesManager::process_channel_update(tl_object_ptr break; } case telegram_api::updateEditChannelMessage::ID: { - auto update_edit_channel_message = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updateEditChannelMessage"; - auto full_message_id = on_get_message(std::move(update_edit_channel_message->message_), false, true, false, false, - false, "updateEditChannelMessage"); - on_message_edited(full_message_id, update_edit_channel_message->pts_); + bool had_message = have_message_force(get_full_message_id(update->message_, false), "updateEditChannelMessage"); + auto full_message_id = + on_get_message(std::move(update->message_), false, true, false, false, false, "updateEditChannelMessage"); + on_message_edited(full_message_id, update->pts_, had_message); break; } case telegram_api::updatePinnedChannelMessages::ID: { - auto pinned_channel_messages_update = move_tl_object_as(update); + auto update = move_tl_object_as(update_ptr); LOG(INFO) << "Process updatePinnedChannelMessages"; - ChannelId channel_id(pinned_channel_messages_update->channel_id_); + ChannelId channel_id(update->channel_id_); if (!channel_id.is_valid()) { LOG(ERROR) << "Receive invalid " << channel_id; break; } vector message_ids; - for (auto &message : pinned_channel_messages_update->messages_) { + for (auto &message : update->messages_) { message_ids.push_back(MessageId(ServerMessageId(message))); } - update_dialog_pinned_messages_from_updates(DialogId(channel_id), message_ids, - pinned_channel_messages_update->pinned_); + update_dialog_pinned_messages_from_updates(DialogId(channel_id), message_ids, update->pinned_); break; } default: @@ -7905,7 +7894,7 @@ void MessagesManager::process_channel_update(tl_object_ptr } } -void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts) { +void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts, bool had_message) { if (full_message_id == FullMessageId()) { return; } @@ -7920,6 +7909,13 @@ void MessagesManager::on_message_edited(FullMessageId full_message_id, int32 pts send_update_message_edited(dialog_id, m); } update_used_hashtags(dialog_id, m); + + if (!had_message && + ((m->reactions != nullptr && !m->reactions->unread_reactions_.empty()) || d->unread_reaction_count > 0)) { + // if new message with unread reactions was added or the chat has unread reactions, + // then number of unread reactions could have been changed, so reload the number of unread reactions + send_get_dialog_query(dialog_id, Promise(), 0, "on_message_edited"); + } } bool MessagesManager::update_dialog_notification_settings(DialogId dialog_id, @@ -10214,7 +10210,7 @@ void MessagesManager::on_get_dialog_messages_search_result( if (filter == MessageSearchFilter::UnreadReaction) { d->unread_reaction_count = old_message_count; // update_dialog_mention_notification_count(d); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "on_get_dialog_messages_search_result"); } update_dialog = true; } @@ -11084,6 +11080,52 @@ void MessagesManager::on_failed_scheduled_message_deletion(DialogId dialog_id, c load_dialog_scheduled_messages(dialog_id, false, 0, Promise()); } +MessagesManager::CanDeleteDialog MessagesManager::can_delete_dialog(const Dialog *d) const { + auto chat_source = sponsored_dialog_source_.get_chat_source_object(); + if (chat_source != nullptr) { + switch (chat_source->get_id()) { + case td_api::chatSourcePublicServiceAnnouncement::ID: + // can delete for self (but only while removing from dialog list) + return {true, false}; + default: + return {false, false}; + } + } + if (td_->auth_manager_->is_bot() || !have_input_peer(d->dialog_id, AccessRights::Read)) { + return {false, false}; + } + + switch (d->dialog_id.get_type()) { + case DialogType::User: + if (d->dialog_id == get_my_dialog_id() || td_->contacts_manager_->is_user_deleted(d->dialog_id.get_user_id()) || + td_->contacts_manager_->is_user_bot(d->dialog_id.get_user_id())) { + return {true, false}; + } + return {true, G()->shared_config().get_option_boolean("revoke_pm_inbox", true)}; + case DialogType::Chat: + // chats can be deleted only for self and can be deleted for everyone by their creator + return {true, td_->contacts_manager_->get_chat_status(d->dialog_id.get_chat_id()).is_creator()}; + case DialogType::Channel: + // private supergroups can be deleted for self + return {!is_broadcast_channel(d->dialog_id) && + !td_->contacts_manager_->is_channel_public(d->dialog_id.get_channel_id()), + td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())}; + case DialogType::SecretChat: + if (td_->contacts_manager_->get_secret_chat_state(d->dialog_id.get_secret_chat_id()) == SecretChatState::Closed) { + // in a closed secret chats there is no way to delete messages for both users + return {true, false}; + } else { + // active secret chats can be deleted only for both users + return {false, true}; + } + break; + case DialogType::None: + default: + UNREACHABLE(); + return {false, false}; + } +} + void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from_dialog_list, bool revoke, Promise &&promise) { LOG(INFO) << "Receive deleteChatHistory request to delete all messages in " << dialog_id @@ -11104,8 +11146,7 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from return promise.set_error(Status::Error(400, "Can't delete the chat")); } if (!remove_from_dialog_list) { - return promise.set_error( - Status::Error(400, "Can't delete only chat history without removing the chat from the chat list")); + return promise.set_error(Status::Error(400, "Can't delete chat history without removing the chat")); } removed_sponsored_dialog_id_ = dialog_id; @@ -11116,37 +11157,25 @@ void MessagesManager::delete_dialog_history(DialogId dialog_id, bool remove_from return; } - auto dialog_type = dialog_id.get_type(); - switch (dialog_type) { - case DialogType::User: - case DialogType::Chat: - // ok - break; - case DialogType::Channel: - if (revoke) { - if (!td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())) { - return promise.set_error(Status::Error(400, "Can't delete chat history for all chat members")); - } - } else { - if (is_broadcast_channel(dialog_id)) { - return promise.set_error(Status::Error(400, "Can't delete chat history in a channel")); - } - if (td_->contacts_manager_->is_channel_public(dialog_id.get_channel_id())) { - return promise.set_error(Status::Error(400, "Can't delete chat history in a public supergroup")); - } + auto can_delete = can_delete_dialog(d); + if (revoke) { + if (!can_delete.for_all_users_) { + if (!can_delete.for_self_) { + return promise.set_error(Status::Error(400, "Chat history can't be deleted")); } - break; - case DialogType::SecretChat: - // ok - break; - case DialogType::None: - default: - UNREACHABLE(); - break; + + LOG(INFO) << "Can't delete history of " << dialog_id << " for everyone; delete it only for self"; + revoke = false; + } + } else { + if (!can_delete.for_self_) { + return promise.set_error( + Status::Error(400, PSLICE() << "Can't delete history of " << dialog_id << " only for self")); + } } auto last_new_message_id = d->last_new_message_id; - if (dialog_type != DialogType::SecretChat && last_new_message_id == MessageId()) { + if (dialog_id.get_type() != DialogType::SecretChat && last_new_message_id == MessageId()) { // TODO get dialog from the server and delete history from last message identifier } @@ -11697,7 +11726,7 @@ void MessagesManager::delete_all_dialog_messages(Dialog *d, bool remove_from_dia } if (d->unread_reaction_count > 0) { set_dialog_unread_reaction_count(d, 0); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "delete_all_dialog_messages"); } bool has_last_message_id = d->last_message_id != MessageId(); @@ -11919,7 +11948,7 @@ void MessagesManager::read_all_dialog_reactions(DialogId dialog_id, Promiseunread_reaction_count != 0) { set_dialog_unread_reaction_count(d, 0); if (!is_update_sent) { - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "read_all_dialog_reactions"); } else { LOG(INFO) << "Update unread reaction message count in " << dialog_id << " to " << d->unread_reaction_count; on_dialog_updated(dialog_id, "read_all_dialog_reactions"); @@ -13117,6 +13146,8 @@ class MessagesManager::DialogFiltersLogEvent { void MessagesManager::tear_down() { parent_.reset(); + + LOG(DEBUG) << "Have " << dialogs_.size() << " chats with " << added_message_count_ << " messages to free"; } void MessagesManager::hangup() { @@ -14853,7 +14884,7 @@ void MessagesManager::set_dialog_is_empty(Dialog *d, const char *source) { } if (d->unread_reaction_count > 0) { set_dialog_unread_reaction_count(d, 0); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "set_dialog_is_empty"); } if (d->reply_markup_message_id != MessageId()) { set_dialog_reply_markup(d, MessageId()); @@ -15569,7 +15600,7 @@ void MessagesManager::on_get_dialogs(FolderId folder_id, vectorunread_reaction_count != dialog->unread_reactions_count_) { set_dialog_unread_reaction_count(d, dialog->unread_reactions_count_); // update_dialog_mention_notification_count(d); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "on_get_dialogs"); } } @@ -16183,7 +16214,7 @@ unique_ptr MessagesManager::do_delete_message(Dialog * } } else { set_dialog_unread_reaction_count(d, d->unread_reaction_count - 1); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "do_delete_message"); } } @@ -16251,6 +16282,8 @@ void MessagesManager::on_message_deleted(Dialog *d, Message *m, bool is_permanen if (m->notification_id.is_valid()) { delete_notification_id_to_message_id_correspondence(d, m->notification_id, m->message_id); } + + added_message_count_--; } unique_ptr MessagesManager::do_delete_scheduled_message(Dialog *d, MessageId message_id, @@ -17558,62 +17591,11 @@ void MessagesManager::on_get_common_dialogs(UserId user_id, int64 offset_chat_id } bool is_last = chats.empty() && offset_chat_id == 0; for (auto &chat : chats) { - DialogId dialog_id; - switch (chat->get_id()) { - case telegram_api::chatEmpty::ID: { - auto c = static_cast(chat.get()); - ChatId chat_id(c->id_); - if (!chat_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << chat_id; - continue; - } - dialog_id = DialogId(chat_id); - break; - } - case telegram_api::chat::ID: { - auto c = static_cast(chat.get()); - ChatId chat_id(c->id_); - if (!chat_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << chat_id; - continue; - } - dialog_id = DialogId(chat_id); - break; - } - case telegram_api::chatForbidden::ID: { - auto c = static_cast(chat.get()); - ChatId chat_id(c->id_); - if (!chat_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << chat_id; - continue; - } - dialog_id = DialogId(chat_id); - break; - } - case telegram_api::channel::ID: { - auto c = static_cast(chat.get()); - ChannelId channel_id(c->id_); - if (!channel_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << channel_id; - continue; - } - dialog_id = DialogId(channel_id); - break; - } - case telegram_api::channelForbidden::ID: { - auto c = static_cast(chat.get()); - ChannelId channel_id(c->id_); - if (!channel_id.is_valid()) { - LOG(ERROR) << "Receive invalid " << channel_id; - continue; - } - dialog_id = DialogId(channel_id); - break; - } - default: - UNREACHABLE(); + auto dialog_id = ContactsManager::get_dialog_id(chat); + if (!dialog_id.is_valid()) { + LOG(ERROR) << "Receive invalid " << to_string(chat); + continue; } - CHECK(dialog_id.is_valid()); td_->contacts_manager_->on_get_chat(std::move(chat), "on_get_common_dialogs"); if (!td::contains(result, dialog_id)) { @@ -19256,7 +19238,7 @@ void MessagesManager::add_dialog_filter(unique_ptr dialog_filter, } auto dialog_list_id = DialogListId(dialog_filter_id); - CHECK(dialog_lists_.find(dialog_list_id) == dialog_lists_.end()); + CHECK(dialog_lists_.count(dialog_list_id) == 0); auto &list = add_dialog_list(dialog_list_id); auto folder_ids = get_dialog_list_folder_ids(list); @@ -20407,7 +20389,7 @@ DialogId MessagesManager::create_new_group_chat(const vector &user_ids, do { random_id = Random::secure_int64(); - } while (random_id == 0 || created_dialogs_.find(random_id) != created_dialogs_.end()); + } while (random_id == 0 || created_dialogs_.count(random_id) > 0); created_dialogs_[random_id]; // reserve place for result td_->create_handler(std::move(promise))->send(std::move(input_users), new_title, random_id); @@ -20445,7 +20427,7 @@ DialogId MessagesManager::create_new_channel_chat(const string &title, bool is_m do { random_id = Random::secure_int64(); - } while (random_id == 0 || created_dialogs_.find(random_id) != created_dialogs_.end()); + } while (random_id == 0 || created_dialogs_.count(random_id) > 0); created_dialogs_[random_id]; // reserve place for result td_->create_handler(std::move(promise)) @@ -21156,65 +21138,9 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * CHECK(d != nullptr); auto chat_source = is_dialog_sponsored(d) ? sponsored_dialog_source_.get_chat_source_object() : nullptr; - - bool can_delete_for_self = false; - bool can_delete_for_all_users = false; - if (chat_source != nullptr) { - switch (chat_source->get_id()) { - case td_api::chatSourcePublicServiceAnnouncement::ID: - // can delete for self (but only while removing from dialog list) - can_delete_for_self = true; - break; - default: - // can't delete - break; - } - } else if (!td_->auth_manager_->is_bot() && have_input_peer(d->dialog_id, AccessRights::Read)) { - switch (d->dialog_id.get_type()) { - case DialogType::User: - can_delete_for_self = true; - can_delete_for_all_users = G()->shared_config().get_option_boolean("revoke_pm_inbox", true); - if (d->dialog_id == get_my_dialog_id() || td_->contacts_manager_->is_user_deleted(d->dialog_id.get_user_id()) || - td_->contacts_manager_->is_user_bot(d->dialog_id.get_user_id())) { - can_delete_for_all_users = false; - } - break; - case DialogType::Chat: - // chats can be deleted only for self with deleteChatHistory and for everyone by their creator - can_delete_for_self = true; - can_delete_for_all_users = td_->contacts_manager_->get_chat_status(d->dialog_id.get_chat_id()).is_creator(); - break; - case DialogType::Channel: - if (is_broadcast_channel(d->dialog_id) || - td_->contacts_manager_->is_channel_public(d->dialog_id.get_channel_id())) { - // deleteChatHistory can't be used in channels and public supergroups to delete messages for self - } else { - // private supergroups can be deleted for self - can_delete_for_self = true; - } - if (td_->contacts_manager_->get_channel_can_be_deleted(d->dialog_id.get_channel_id())) { - can_delete_for_all_users = true; - } - break; - case DialogType::SecretChat: - if (td_->contacts_manager_->get_secret_chat_state(d->dialog_id.get_secret_chat_id()) == - SecretChatState::Closed) { - // in a closed secret chats there is no way to delete messages for both users - can_delete_for_self = true; - } else { - // active secret chats can be deleted only for both users - can_delete_for_all_users = true; - } - break; - case DialogType::None: - default: - UNREACHABLE(); - } - } - + auto can_delete = can_delete_dialog(d); // TODO hide/show draft message when can_send_message(dialog_id) changes auto draft_message = can_send_message(d->dialog_id).is_ok() ? get_draft_message_object(d->draft_message) : nullptr; - return make_tl_object( d->dialog_id.get(), get_chat_type_object(d->dialog_id), get_dialog_title(d->dialog_id), get_chat_photo_info_object(td_->file_manager_.get(), get_dialog_photo(d->dialog_id)), @@ -21222,7 +21148,7 @@ td_api::object_ptr MessagesManager::get_chat_object(const Dialog * get_message_object(d->dialog_id, get_message(d, d->last_message_id), "get_chat_object"), get_chat_positions_object(d), get_default_message_sender_object(d), get_dialog_has_protected_content(d->dialog_id), d->is_marked_as_unread, d->is_blocked, - get_dialog_has_scheduled_messages(d), can_delete_for_self, can_delete_for_all_users, + get_dialog_has_scheduled_messages(d), can_delete.for_self_, can_delete.for_all_users_, can_report_dialog(d->dialog_id), d->notification_settings.silent_send_message, d->server_unread_count + d->local_unread_count, d->last_read_inbox_message_id.get(), d->last_read_outbox_message_id.get(), d->unread_mention_count, d->unread_reaction_count, @@ -22054,7 +21980,7 @@ std::pair> MessagesManager::get_message_thread_histo do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_dialog_messages_.find(random_id) != found_dialog_messages_.end()); + } while (random_id == 0 || found_dialog_messages_.count(random_id) > 0); found_dialog_messages_[random_id]; // reserve place for result td_->create_handler(std::move(promise)) @@ -22103,7 +22029,7 @@ td_api::object_ptr MessagesManager::get_dialog_message_ do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_dialog_message_calendars_.find(random_id) != found_dialog_message_calendars_.end()); + } while (random_id == 0 || found_dialog_message_calendars_.count(random_id) > 0); found_dialog_message_calendars_[random_id]; // reserve place for result CHECK(filter != MessageSearchFilter::Call && filter != MessageSearchFilter::MissedCall); @@ -22317,7 +22243,7 @@ std::pair> MessagesManager::search_dialog_messages( do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_dialog_messages_.find(random_id) != found_dialog_messages_.end()); + } while (random_id == 0 || found_dialog_messages_.count(random_id) > 0); found_dialog_messages_[random_id]; // reserve place for result if (filter == MessageSearchFilter::UnreadMention || filter == MessageSearchFilter::UnreadReaction) { @@ -22437,7 +22363,7 @@ std::pair> MessagesManager::search_call_messages(Me do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_call_messages_.find(random_id) != found_call_messages_.end()); + } while (random_id == 0 || found_call_messages_.count(random_id) > 0); found_call_messages_[random_id]; // reserve place for result auto filter = only_missed ? MessageSearchFilter::MissedCall : MessageSearchFilter::Call; @@ -22947,7 +22873,7 @@ void MessagesManager::on_search_dialog_messages_db_result(int64 random_id, Dialo if (filter == MessageSearchFilter::UnreadReaction) { d->unread_reaction_count = message_count; // update_dialog_mention_notification_count(d); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "on_search_dialog_messages_db_result"); } on_dialog_updated(dialog_id, "on_search_dialog_messages_db_result"); } @@ -23030,7 +22956,7 @@ MessagesManager::FoundMessages MessagesManager::offline_search_messages(DialogId do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_fts_messages_.find(random_id) != found_fts_messages_.end()); + } while (random_id == 0 || found_fts_messages_.count(random_id) > 0); found_fts_messages_[random_id]; // reserve place for result G()->td_db()->get_messages_db_async()->get_messages_fts( @@ -23163,7 +23089,7 @@ std::pair> MessagesManager::search_messages( do { random_id = Random::secure_int64(); - } while (random_id == 0 || found_messages_.find(random_id) != found_messages_.end()); + } while (random_id == 0 || found_messages_.count(random_id) > 0); found_messages_[random_id]; // reserve place for result LOG(DEBUG) << "Search all messages filtered by " << filter << " with query = \"" << query << "\" from date " @@ -23194,8 +23120,7 @@ int64 MessagesManager::get_dialog_message_by_date(DialogId dialog_id, int32 date int64 random_id = 0; do { random_id = Random::secure_int64(); - } while (random_id == 0 || - get_dialog_message_by_date_results_.find(random_id) != get_dialog_message_by_date_results_.end()); + } while (random_id == 0 || get_dialog_message_by_date_results_.count(random_id) > 0); get_dialog_message_by_date_results_[random_id]; // reserve place for result auto message_id = find_message_by_date(d->messages.get(), date); @@ -23511,9 +23436,14 @@ void MessagesManager::get_dialog_message_count(DialogId dialog_id, MessageSearch return promise.set_value(std::move(message_count)); } + get_dialog_message_count_from_server(dialog_id, filter, std::move(promise)); +} + +void MessagesManager::get_dialog_message_count_from_server(DialogId dialog_id, MessageSearchFilter filter, + Promise &&promise) { LOG(INFO) << "Get number of messages in " << dialog_id << " filtered by " << filter << " from the server"; - switch (dialog_type) { + switch (dialog_id.get_type()) { case DialogType::User: case DialogType::Chat: case DialogType::Channel: @@ -24238,7 +24168,8 @@ void MessagesManager::set_message_reaction(FullMessageId full_message_id, string return promise.set_error(Status::Error(400, "The reaction isn't available for the message")); } - bool can_get_added_reactions = !is_broadcast_channel(dialog_id) && dialog_id.get_type() != DialogType::User; + bool can_get_added_reactions = !is_broadcast_channel(dialog_id) && dialog_id.get_type() != DialogType::User && + !is_discussion_message(dialog_id, m); if (m->reactions == nullptr) { if (reaction.empty()) { return promise.set_value(Unit()); @@ -24638,7 +24569,7 @@ int64 MessagesManager::generate_new_random_id() { int64 random_id; do { random_id = Random::secure_int64(); - } while (random_id == 0 || being_sent_messages_.find(random_id) != being_sent_messages_.end()); + } while (random_id == 0 || being_sent_messages_.count(random_id) > 0); return random_id; } @@ -27849,6 +27780,7 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d } auto schedule_date = get_message_schedule_date(messages[0]); + auto as_input_peer = get_send_message_as_input_peer(messages[0]); int32 flags = 0; if (messages[0]->disable_notification) { @@ -27863,7 +27795,7 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d if (schedule_date != 0) { flags |= SEND_MESSAGE_FLAG_HAS_SCHEDULE_DATE; } - if (messages[0]->has_explicit_sender) { + if (as_input_peer != nullptr) { flags |= SEND_MESSAGE_FLAG_HAS_SEND_AS; } if (messages[0]->noforwards) { @@ -27873,8 +27805,8 @@ void MessagesManager::do_forward_messages(DialogId to_dialog_id, DialogId from_d vector random_ids = transform(messages, [this, to_dialog_id](const Message *m) { return begin_send_message(to_dialog_id, m); }); td_->create_handler(get_erase_log_event_promise(log_event_id)) - ->send(flags, to_dialog_id, from_dialog_id, get_send_message_as_input_peer(messages[0]), message_ids, - std::move(random_ids), schedule_date); + ->send(flags, to_dialog_id, from_dialog_id, std::move(as_input_peer), message_ids, std::move(random_ids), + schedule_date); } Result> MessagesManager::forward_message( @@ -28737,7 +28669,7 @@ void MessagesManager::start_import_messages(DialogId dialog_id, int64 import_id, int64 random_id; do { random_id = Random::secure_int64(); - } while (random_id == 0 || pending_message_imports_.find(random_id) != pending_message_imports_.end()); + } while (random_id == 0 || pending_message_imports_.count(random_id) > 0); pending_message_imports_[random_id] = std::move(pending_message_import); multipromise.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), random_id](Result result) { @@ -30494,17 +30426,20 @@ void MessagesManager::send_update_chat_unread_mention_count(const Dialog *d) { make_tl_object(d->dialog_id.get(), d->unread_mention_count)); } -void MessagesManager::send_update_chat_unread_reaction_count(const Dialog *d) { +void MessagesManager::send_update_chat_unread_reaction_count(const Dialog *d, const char *source) { if (td_->auth_manager_->is_bot()) { return; } CHECK(d != nullptr); - LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id << " in send_update_chat_unread_reaction_count"; - LOG(INFO) << "Update unread reaction message count in " << d->dialog_id << " to " << d->unread_reaction_count; + LOG_CHECK(d->is_update_new_chat_sent) << "Wrong " << d->dialog_id + << " in send_update_chat_unread_reaction_count from " << source; + LOG(INFO) << "Update unread reaction message count in " << d->dialog_id << " to " << d->unread_reaction_count + << " from " << source; on_dialog_updated(d->dialog_id, "send_update_chat_unread_reaction_count"); - send_closure(G()->td(), &Td::send_update, - make_tl_object(d->dialog_id.get(), d->unread_reaction_count)); + send_closure( + G()->td(), &Td::send_update, + td_api::make_object(d->dialog_id.get(), d->unread_reaction_count)); } void MessagesManager::send_update_chat_position(DialogListId dialog_list_id, const Dialog *d, @@ -32128,10 +32063,10 @@ void MessagesManager::on_create_new_dialog_success(int64 random_id, tl_object_pt return promise.set_value(Unit()); } - if (pending_created_dialogs_.find(dialog_id) == pending_created_dialogs_.end()) { + if (pending_created_dialogs_.count(dialog_id) == 0) { pending_created_dialogs_.emplace(dialog_id, std::move(promise)); } else { - LOG(ERROR) << dialog_id << " returned twice as result of chat creation"; + LOG(ERROR) << "Receive twice " << dialog_id << " as result of chat creation"; return on_create_new_dialog_fail(random_id, Status::Error(500, "Chat was created earlier"), std::move(promise)); } @@ -34456,7 +34391,7 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } if (*need_update && has_unread_message_reactions(dialog_id, message.get())) { set_dialog_unread_reaction_count(d, d->unread_reaction_count + 1); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "add_message_to_dialog"); } if (*need_update) { update_message_count_by_index(d, +1, message.get()); @@ -34684,6 +34619,8 @@ MessagesManager::Message *MessagesManager::add_message_to_dialog(Dialog *d, uniq } } + added_message_count_++; + return result_message; } @@ -35715,21 +35652,6 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me old_file_view.size() == new_file_view.size()) { auto old_file_type = old_file_view.get_type(); auto new_file_type = new_file_view.get_type(); - auto is_document_file_type = [](FileType file_type) { - switch (file_type) { - case FileType::Animation: - case FileType::Audio: - case FileType::Document: - case FileType::DocumentAsFile: - case FileType::Sticker: - case FileType::Video: - case FileType::VideoNote: - case FileType::VoiceNote: - return true; - default: - return false; - } - }; if (is_document_file_type(old_file_type) && is_document_file_type(new_file_type)) { auto &old_location = old_file_view.local_location(); @@ -37466,7 +37388,7 @@ MessagesManager::DialogList &MessagesManager::add_dialog_list(DialogListId dialo if (dialog_list_id.is_folder() && dialog_list_id.get_folder_id() != FolderId::archive()) { dialog_list_id = DialogListId(FolderId::main()); } - if (dialog_lists_.find(dialog_list_id) == dialog_lists_.end()) { + if (dialog_lists_.count(dialog_list_id) == 0) { LOG(INFO) << "Create " << dialog_list_id; } auto &list = dialog_lists_[dialog_list_id]; @@ -37840,8 +37762,7 @@ void MessagesManager::process_get_channel_difference_updates( auto update_new_channel_message = static_cast(update.get()); auto message_id = get_message_id(update_new_channel_message->message_, false); FullMessageId full_message_id(dialog_id, message_id); - if (update_message_ids_.find(full_message_id) != update_message_ids_.end() && - changed_message_ids.find(message_id) != changed_message_ids.end()) { + if (update_message_ids_.count(full_message_id) > 0 && changed_message_ids.count(message_id) > 0) { changed_message_ids.erase(message_id); AwaitedMessage awaited_message; awaited_message.message = std::move(update_new_channel_message->message_); @@ -38015,7 +37936,7 @@ void MessagesManager::on_get_channel_dialog(DialogId dialog_id, MessageId last_m if (d->unread_reaction_count != unread_reaction_count) { set_dialog_unread_reaction_count(d, unread_reaction_count); // update_dialog_mention_notification_count(d); - send_update_chat_unread_reaction_count(d); + send_update_chat_unread_reaction_count(d, "on_get_channel_dialog 60"); } if (d->last_read_outbox_message_id != read_outbox_max_message_id) { diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 80e620f40..3e0adee57 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -820,7 +820,7 @@ class MessagesManager final : public Actor { tl_object_ptr get_messages_object(int32 total_count, const vector &full_message_ids, bool skip_not_found, const char *source); - void process_pts_update(tl_object_ptr &&update); + void process_pts_update(tl_object_ptr &&update_ptr); void skip_old_pending_pts_update(tl_object_ptr &&update, int32 new_pts, int32 old_pts, int32 pts_count, const char *source); @@ -1844,6 +1844,8 @@ class MessagesManager final : public Actor { void delete_update_message_id(DialogId dialog_id, MessageId message_id); + void get_dialog_message_count_from_server(DialogId dialog_id, MessageSearchFilter filter, Promise &&promise); + FullMessageId on_get_message(MessageInfo &&message_info, bool from_update, bool is_channel_message, bool have_previous, bool have_next, const char *source); @@ -1917,9 +1919,9 @@ class MessagesManager final : public Actor { void add_postponed_channel_update(DialogId dialog_id, tl_object_ptr &&update, int32 new_pts, int32 pts_count, Promise &&promise); - void process_channel_update(tl_object_ptr &&update); + void process_channel_update(tl_object_ptr &&update_ptr); - void on_message_edited(FullMessageId full_message_id, int32 pts); + void on_message_edited(FullMessageId full_message_id, int32 pts, bool had_message); void delete_messages_from_updates(const vector &message_ids); @@ -2031,6 +2033,15 @@ class MessagesManager final : public Actor { bool can_get_message_statistics(DialogId dialog_id, const Message *m) const; + struct CanDeleteDialog { + bool for_self_; + bool for_all_users_; + + CanDeleteDialog(bool for_self, bool for_all_users) : for_self_(for_self), for_all_users_(for_all_users) { + } + }; + CanDeleteDialog can_delete_dialog(const Dialog *d) const; + static bool can_delete_channel_message(const DialogParticipantStatus &status, const Message *m, bool is_bot); bool can_delete_message(DialogId dialog_id, const Message *m) const; @@ -2449,7 +2460,7 @@ class MessagesManager final : public Actor { void send_update_chat_unread_mention_count(const Dialog *d); - void send_update_chat_unread_reaction_count(const Dialog *d); + void send_update_chat_unread_reaction_count(const Dialog *d, const char *source); void send_update_chat_position(DialogListId dialog_list_id, const Dialog *d, const char *source) const; @@ -3419,6 +3430,7 @@ class MessagesManager final : public Actor { bool running_get_difference_ = false; // true after before_get_difference and false after after_get_difference FlatHashMap, DialogIdHash> dialogs_; + int64 added_message_count_ = 0; FlatHashSet loaded_dialogs_; // dialogs loaded from database, but not added to dialogs_ diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index 3c16b3634..a07c6c1ab 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -204,7 +204,7 @@ void NotificationManager::start_up() { } void NotificationManager::init() { - if (is_disabled()) { + if (is_disabled() || is_inited_) { return; } diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index b8aeed011..df1aff573 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -70,18 +70,8 @@ tl_object_ptr get_profile_photo_object(FileManager *file_m profile_photo.has_animation); } -bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs) { - bool location_differs = lhs.small_file_id != rhs.small_file_id || lhs.big_file_id != rhs.big_file_id; - bool id_differs = lhs.id != rhs.id; - - if (location_differs) { - return false; - } - return lhs.has_animation == rhs.has_animation && lhs.minithumbnail == rhs.minithumbnail && !id_differs; -} - -bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs) { - return !(lhs == rhs); +bool need_update_profile_photo(const ProfilePhoto &from, const ProfilePhoto &to) { + return from.id != to.id || need_update_dialog_photo(from, to); } StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &profile_photo) { @@ -163,9 +153,10 @@ DialogPhoto as_fake_dialog_photo(const Photo &photo, DialogId dialog_id) { return result; } -ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo) { - ProfilePhoto result; - static_cast(result) = as_fake_dialog_photo(photo, DialogId(user_id)); +DialogPhoto as_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash, + const Photo &photo) { + DialogPhoto result; + static_cast(result) = as_fake_dialog_photo(photo, dialog_id); if (!result.small_file_id.is_valid()) { return result; } @@ -176,25 +167,40 @@ ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 u auto remote = file_view.remote_location(); CHECK(remote.is_photo()); CHECK(!remote.is_web()); - remote.set_source(PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, is_big)); - return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(), - file_view.size(), file_view.expected_size(), file_view.remote_name()); + remote.set_source(PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, is_big)); + return file_manager->register_remote(std::move(remote), FileLocationSource::FromServer, DialogId(), 0, 0, + file_view.remote_name()); }; - result.id = photo.id.get(); result.small_file_id = reregister_photo(false, result.small_file_id); result.big_file_id = reregister_photo(true, result.big_file_id); return result; } -bool operator==(const DialogPhoto &lhs, const DialogPhoto &rhs) { - return lhs.small_file_id == rhs.small_file_id && lhs.big_file_id == rhs.big_file_id && - lhs.minithumbnail == rhs.minithumbnail && lhs.has_animation == rhs.has_animation; +ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo) { + ProfilePhoto result; + static_cast(result) = as_dialog_photo(file_manager, DialogId(user_id), user_access_hash, photo); + if (result.small_file_id.is_valid()) { + result.id = photo.id.get(); + } + return result; } -bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs) { - return !(lhs == rhs); +bool is_same_dialog_photo(FileManager *file_manager, DialogId dialog_id, const Photo &photo, + const DialogPhoto &dialog_photo) { + auto get_unique_file_id = [file_manager](FileId file_id) { + return file_manager->get_file_view(file_id).get_unique_file_id(); + }; + auto fake_photo = as_fake_dialog_photo(photo, dialog_id); + return get_unique_file_id(fake_photo.small_file_id) == get_unique_file_id(dialog_photo.small_file_id) && + get_unique_file_id(fake_photo.big_file_id) == get_unique_file_id(dialog_photo.big_file_id); +} + +bool need_update_dialog_photo(const DialogPhoto &from, const DialogPhoto &to) { + return from.small_file_id != to.small_file_id || from.big_file_id != to.big_file_id || + from.has_animation != to.has_animation || + need_update_dialog_photo_minithumbnail(from.minithumbnail, to.minithumbnail); } StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo) { diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index 0b2953082..577ad4e47 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -57,8 +57,7 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 tl_object_ptr get_profile_photo_object(FileManager *file_manager, const ProfilePhoto &profile_photo); -bool operator==(const ProfilePhoto &lhs, const ProfilePhoto &rhs); -bool operator!=(const ProfilePhoto &lhs, const ProfilePhoto &rhs); +bool need_update_profile_photo(const ProfilePhoto &from, const ProfilePhoto &to); StringBuilder &operator<<(StringBuilder &string_builder, const ProfilePhoto &profile_photo); @@ -69,12 +68,17 @@ tl_object_ptr get_chat_photo_info_object(FileManager *fil DialogPhoto as_fake_dialog_photo(const Photo &photo, DialogId dialog_id); +DialogPhoto as_dialog_photo(FileManager *file_manager, DialogId dialog_id, int64 dialog_access_hash, + const Photo &photo); + ProfilePhoto as_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, const Photo &photo); +bool is_same_dialog_photo(FileManager *file_manager, DialogId dialog_id, const Photo &photo, + const DialogPhoto &dialog_photo); + vector dialog_photo_get_file_ids(const DialogPhoto &dialog_photo); -bool operator==(const DialogPhoto &lhs, const DialogPhoto &rhs); -bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs); +bool need_update_dialog_photo(const DialogPhoto &from, const DialogPhoto &to); StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo); diff --git a/td/telegram/PhotoSize.cpp b/td/telegram/PhotoSize.cpp index db1688c72..4772c4d39 100644 --- a/td/telegram/PhotoSize.cpp +++ b/td/telegram/PhotoSize.cpp @@ -57,6 +57,27 @@ StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimen return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")"; } +static int32 get_minithumbnail_size(const string &packed) { + if (packed.size() < 3) { + return 0; + } + if (packed[0] == '\x01') { + return max(static_cast(packed[1]), static_cast(packed[2])); + } + return 0; +} + +bool need_update_dialog_photo_minithumbnail(const string &from, const string &to) { + if (from == to) { + return false; + } + + auto from_size = get_minithumbnail_size(from); + auto to_size = get_minithumbnail_size(to); + // dialog photo minithumbnail is expected to be 8x8 + return to_size != 0 && (to_size <= 8 || from_size > 8); +} + td_api::object_ptr get_minithumbnail_object(const string &packed) { if (packed.size() < 3) { return nullptr; diff --git a/td/telegram/PhotoSize.h b/td/telegram/PhotoSize.h index d73903b43..2510983ca 100644 --- a/td/telegram/PhotoSize.h +++ b/td/telegram/PhotoSize.h @@ -48,6 +48,8 @@ bool operator!=(const Dimensions &lhs, const Dimensions &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions); +bool need_update_dialog_photo_minithumbnail(const string &from, const string &to); + td_api::object_ptr get_minithumbnail_object(const string &packed); FileId register_photo_size(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash, diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index 36ef8da56..42a4b82da 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -1172,7 +1172,7 @@ void PollManager::on_update_poll_timeout(PollId poll_id) { if (poll->is_closed && poll->is_updated_after_close) { return; } - if (pending_answers_.find(poll_id) != pending_answers_.end()) { + if (pending_answers_.count(poll_id) > 0) { LOG(INFO) << "Skip fetching results of " << poll_id << ", because it is being voted now"; return; } diff --git a/td/telegram/SecretChatActor.cpp b/td/telegram/SecretChatActor.cpp index c708b3d11..181c3eb3c 100644 --- a/td/telegram/SecretChatActor.cpp +++ b/td/telegram/SecretChatActor.cpp @@ -548,7 +548,7 @@ void SecretChatActor::run_fill_gaps() { auto message = std::move(begin->second); pending_inbound_messages_.erase(begin); check_status(do_inbound_message_decrypted_unchecked(std::move(message), -1)); - CHECK(pending_inbound_messages_.find(next_seq_no) == pending_inbound_messages_.end()); + CHECK(pending_inbound_messages_.count(next_seq_no) == 0); } else { break; } diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index c26e1cbae..b2068d055 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -548,7 +548,7 @@ void SetSecureValue::start_upload(FileManager *file_manager, FileId &file_id, Se auto download_file_id = file_manager->dup_file_id(file_id); file_id = file_manager - ->register_generate(FileType::Secure, FileLocationSource::FromServer, file_view.suggested_path(), + ->register_generate(FileType::SecureEncrypted, FileLocationSource::FromServer, file_view.suggested_path(), PSTRING() << "#file_id#" << download_file_id.get(), DialogId(), file_view.size()) .ok(); } diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index 7f09ab71c..4a4895ad9 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -354,9 +354,9 @@ EncryptedSecureFile get_encrypted_secure_file(FileManager *file_manager, break; } result.file.file_id = file_manager->register_remote( - FullRemoteFileLocation(FileType::Secure, secure_file->id_, secure_file->access_hash_, DcId::internal(dc_id), - ""), - FileLocationSource::FromServer, DialogId(), 0, secure_file->size_, PSTRING() << secure_file->id_ << ".jpg"); + FullRemoteFileLocation(FileType::SecureEncrypted, secure_file->id_, secure_file->access_hash_, + DcId::internal(dc_id), ""), + FileLocationSource::FromServer, DialogId(), secure_file->size_, 0, PSTRING() << secure_file->id_ << ".jpg"); result.file.date = secure_file->date_; if (result.file.date < 0) { LOG(ERROR) << "Receive wrong date " << result.file.date; @@ -427,12 +427,16 @@ static td_api::object_ptr get_dated_file_object(FileManager * LOG(ERROR) << "Have wrong file in get_dated_file_object"; return nullptr; } - dated_file.file_id = - file_manager->register_remote(FullRemoteFileLocation(FileType::SecureRaw, file_view.remote_location().get_id(), - file_view.remote_location().get_access_hash(), - file_view.remote_location().get_dc_id(), ""), - FileLocationSource::FromServer, DialogId(), file_view.size(), - file_view.expected_size(), file_view.suggested_path()); + if (file_view.get_type() != FileType::SecureEncrypted) { + LOG(ERROR) << "Have file of a wrong type in get_dated_file_object"; + } else if (file_view.encryption_key().empty()) { + return get_dated_file_object(file_manager, dated_file); + } + dated_file.file_id = file_manager->register_remote( + FullRemoteFileLocation(FileType::SecureDecrypted, file_view.remote_location().get_id(), + file_view.remote_location().get_access_hash(), file_view.remote_location().get_dc_id(), + ""), + FileLocationSource::FromServer, DialogId(), 0, file_view.expected_size(), file_view.suggested_path()); return get_dated_file_object(file_manager, dated_file); } @@ -843,7 +847,8 @@ static Status check_document_number(string &number) { } static Result get_secure_file(FileManager *file_manager, td_api::object_ptr &&file) { - TRY_RESULT(file_id, file_manager->get_input_file_id(FileType::Secure, file, DialogId(), false, false, false, true)); + TRY_RESULT(file_id, + file_manager->get_input_file_id(FileType::SecureEncrypted, file, DialogId(), false, false, false, true)); DatedFile result; result.file_id = file_id; result.date = G()->unix_time(); diff --git a/td/telegram/SendCodeHelper.hpp b/td/telegram/SendCodeHelper.hpp index 09dad7dfa..673f20b43 100644 --- a/td/telegram/SendCodeHelper.hpp +++ b/td/telegram/SendCodeHelper.hpp @@ -41,9 +41,9 @@ void SendCodeHelper::store(StorerT &storer) const { template void SendCodeHelper::parse(ParserT &parser) { - bool legacy_is_registered = false; using td::parse; parse(phone_number_, parser); + bool legacy_is_registered; parse(legacy_is_registered, parser); parse(phone_code_hash_, parser); parse(sent_code_info_, parser); diff --git a/td/telegram/StickersManager.cpp b/td/telegram/StickersManager.cpp index 29f53a874..d12af140e 100644 --- a/td/telegram/StickersManager.cpp +++ b/td/telegram/StickersManager.cpp @@ -1295,7 +1295,7 @@ void StickersManager::start_up() { } void StickersManager::init() { - if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || G()->close_flag()) { + if (is_inited_ || !td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot() || G()->close_flag()) { return; } LOG(INFO) << "Init StickersManager"; @@ -5867,7 +5867,7 @@ void StickersManager::create_new_sticker_set(UserId user_id, string &title, stri int64 random_id; do { random_id = Random::secure_int64(); - } while (random_id == 0 || pending_new_sticker_sets_.find(random_id) != pending_new_sticker_sets_.end()); + } while (random_id == 0 || pending_new_sticker_sets_.count(random_id) > 0); pending_new_sticker_sets_[random_id] = std::move(pending_new_sticker_set); multipromise.add_promise(PromiseCreator::lambda([actor_id = actor_id(this), random_id](Result result) { @@ -6066,7 +6066,7 @@ void StickersManager::add_sticker_to_set(UserId user_id, string &short_name, int64 random_id; do { random_id = Random::secure_int64(); - } while (random_id == 0 || pending_add_sticker_to_sets_.find(random_id) != pending_add_sticker_to_sets_.end()); + } while (random_id == 0 || pending_add_sticker_to_sets_.count(random_id) > 0); pending_add_sticker_to_sets_[random_id] = std::move(pending_add_sticker_to_set); auto on_upload_promise = PromiseCreator::lambda([random_id](Result result) { @@ -6162,8 +6162,7 @@ void StickersManager::do_set_sticker_set_thumbnail(UserId user_id, string short_ int64 random_id; do { random_id = Random::secure_int64(); - } while (random_id == 0 || - pending_set_sticker_set_thumbnails_.find(random_id) != pending_set_sticker_set_thumbnails_.end()); + } while (random_id == 0 || pending_set_sticker_set_thumbnails_.count(random_id) > 0); pending_set_sticker_set_thumbnails_[random_id] = std::move(pending_set_sticker_set_thumbnail); auto on_upload_promise = PromiseCreator::lambda([random_id](Result result) { @@ -7590,7 +7589,7 @@ int64 StickersManager::get_emoji_suggestions_url(const string &language_code, Pr int64 random_id = 0; do { random_id = Random::secure_int64(); - } while (random_id == 0 || emoji_suggestions_urls_.find(random_id) != emoji_suggestions_urls_.end()); + } while (random_id == 0 || emoji_suggestions_urls_.count(random_id) > 0); emoji_suggestions_urls_[random_id]; // reserve place for result auto query_promise = diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index b13e4a8cc..97b5c2b79 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5809,6 +5809,13 @@ void Td::on_request(uint64 id, td_api::sendCallDebugInformation &request) { std::move(request.debug_information_), std::move(promise)); } +void Td::on_request(uint64 id, td_api::sendCallLog &request) { + CHECK_IS_USER(); + CREATE_OK_REQUEST_PROMISE(); + send_closure(G()->call_manager(), &CallManager::send_call_log, CallId(request.call_id_), std::move(request.log_file_), + std::move(promise)); +} + void Td::on_request(uint64 id, const td_api::getVideoChatAvailableParticipants &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); @@ -6613,7 +6620,7 @@ void Td::on_request(uint64 id, td_api::uploadFile &request) { auto file_type = request.file_type_ == nullptr ? FileType::Temp : get_file_type(*request.file_type_); bool is_secret = file_type == FileType::Encrypted || file_type == FileType::EncryptedThumbnail; - bool is_secure = file_type == FileType::Secure; + bool is_secure = file_type == FileType::SecureEncrypted; auto r_file_id = file_manager_->get_input_file_id(file_type, request.file_, DialogId(), false, is_secret, !is_secure && !is_secret, is_secure); if (r_file_id.is_error()) { @@ -8102,6 +8109,9 @@ td_api::object_ptr Td::do_static_request(td_api::parseTextEntiti } auto r_entities = [&]() -> Result> { + if (utf8_length(request.text_) > 65536) { + return Status::Error("Text is too long"); + } switch (request.parse_mode_->get_id()) { case td_api::textParseModeHTML::ID: return parse_html(request.text_); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 28d4cf1e4..6a8e7ee7a 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -755,6 +755,8 @@ class Td final : public Actor { void on_request(uint64 id, td_api::sendCallDebugInformation &request); + void on_request(uint64 id, td_api::sendCallLog &request); + void on_request(uint64 id, const td_api::getVideoChatAvailableParticipants &request); void on_request(uint64 id, const td_api::setVideoChatDefaultParticipant &request); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index fea5d34db..ed1bde9db 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -177,6 +177,8 @@ UpdatesManager::UpdatesManager(Td *td, ActorShared<> parent) : td_(td), parent_( void UpdatesManager::tear_down() { parent_.reset(); + + LOG(DEBUG) << "Have " << being_processed_updates_ << " unprocessed updates to apply"; } void UpdatesManager::hangup_shared() { @@ -1192,19 +1194,12 @@ vector UpdatesManager::get_chat_dialog_ids(const telegram_api::Updates vector dialog_ids; dialog_ids.reserve(chats->size()); for (const auto &chat : *chats) { - auto chat_id = ContactsManager::get_chat_id(chat); - if (chat_id.is_valid()) { - dialog_ids.push_back(DialogId(chat_id)); - continue; + auto dialog_id = ContactsManager::get_dialog_id(chat); + if (dialog_id.is_valid()) { + dialog_ids.push_back(dialog_id); + } else { + LOG(ERROR) << "Can't find identifier of " << oneline(to_string(chat)); } - - auto channel_id = ContactsManager::get_channel_id(chat); - if (channel_id.is_valid()) { - dialog_ids.push_back(DialogId(channel_id)); - continue; - } - - LOG(ERROR) << "Can't find identifier of " << oneline(to_string(chat)); } return dialog_ids; } @@ -1709,6 +1704,7 @@ void UpdatesManager::on_pending_updates(vector &&result) mutable { send_closure(actor_id, &UpdatesManager::on_pending_updates_processed, std::move(result), std::move(promise)); }); @@ -1821,7 +1817,7 @@ void UpdatesManager::on_pending_updates(vector result, Promise promise) { + being_processed_updates_--; promise.set_result(std::move(result)); } diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 8afa58000..172e73fe6 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -195,6 +195,8 @@ class UpdatesManager final : public Actor { int32 pending_pts_ = 0; int32 pending_qts_ = 0; + int64 being_processed_updates_ = 0; + int32 short_update_date_ = 0; int32 accumulated_pts_count_ = 0; diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index f7218809d..cd6092a09 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -413,6 +413,8 @@ WebPagesManager::WebPagesManager(Td *td, ActorShared<> parent) : td_(td), parent void WebPagesManager::tear_down() { parent_.reset(); + + LOG(DEBUG) << "Have " << web_pages_.size() << " web pages to free"; } WebPagesManager::~WebPagesManager() = default; diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index a6a7035c4..8981e8b1f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -673,12 +673,28 @@ class CliClient final : public Actor { return as_input_thumbnail(as_generated_file(original_path, conversion), width, height); } - static int32 as_call_id(string str) { - return to_integer(trim(std::move(str))); + struct CallId { + int32 call_id = 0; + + operator int32() const { + return call_id; + } + }; + + void get_args(string &args, CallId &arg) const { + arg.call_id = to_integer(trim(args)); } - static int32 as_group_call_id(string str) { - return to_integer(trim(std::move(str))); + struct GroupCallId { + int32 group_call_id = 0; + + operator int32() const { + return group_call_id; + } + }; + + void get_args(string &args, GroupCallId &arg) const { + arg.group_call_id = to_integer(trim(args)); } static int32 as_proxy_id(string str) { @@ -2994,18 +3010,21 @@ class CliClient final : public Actor { user_id, td_api::make_object(true, true, 65, 65, vector{"2.6", "3.0"}), rand_bool())); } else if (op == "ac" || op == "AcceptCall") { + CallId call_id; + get_args(args, call_id); send_request(td_api::make_object( - as_call_id(args), - td_api::make_object(true, true, 65, 65, vector{"2.6", "3.0"}))); + call_id, td_api::make_object(true, true, 65, 65, vector{"2.6", "3.0"}))); } else if (op == "scsd") { - send_request(td_api::make_object(as_call_id(args), "abacaba")); + CallId call_id; + get_args(args, call_id); + send_request(td_api::make_object(call_id, "abacaba")); } else if (op == "dc" || op == "DiscardCall") { - string call_id; + CallId call_id; bool is_disconnected; get_args(args, call_id, is_disconnected); - send_request(td_api::make_object(as_call_id(call_id), is_disconnected, 0, rand_bool(), 0)); + send_request(td_api::make_object(call_id, is_disconnected, 0, rand_bool(), 0)); } else if (op == "scr" || op == "SendCallRating") { - string call_id; + CallId call_id; int32 rating; get_args(args, call_id, rating); vector> problems; @@ -3017,10 +3036,17 @@ class CliClient final : public Actor { problems.emplace_back(td_api::make_object()); problems.emplace_back(td_api::make_object()); problems.emplace_back(td_api::make_object()); - send_request(td_api::make_object( - as_call_id(call_id), rating, "Wow, such good call! (TDLib test)", std::move(problems))); - } else if (op == "scdi" || op == "SendCallDebugInformation") { - send_request(td_api::make_object(as_call_id(args), "{}")); + send_request(td_api::make_object(call_id, rating, "Wow, such good call! (TDLib test)", + std::move(problems))); + } else if (op == "scdi") { + CallId call_id; + get_args(args, call_id); + send_request(td_api::make_object(call_id, "{}")); + } else if (op == "sclog") { + CallId call_id; + string log_file; + get_args(args, call_id, log_file); + send_request(td_api::make_object(call_id, as_input_file(log_file))); } else if (op == "gvcap") { ChatId chat_id; get_args(args, chat_id); @@ -3047,24 +3073,30 @@ class CliClient final : public Actor { get_args(args, chat_id); send_request(td_api::make_object(chat_id)); } else if (op == "ggc") { - send_request(td_api::make_object(as_group_call_id(args))); - } else if (op == "ggcs") { - string group_call_id; + GroupCallId group_call_id; get_args(args, group_call_id); - send_request(td_api::make_object(as_group_call_id(group_call_id))); + send_request(td_api::make_object(group_call_id)); + } else if (op == "ggcs") { + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "ggcss") { - string group_call_id; + GroupCallId group_call_id; int32 channel_id; get_args(args, group_call_id, channel_id); send_request(td_api::make_object( - as_group_call_id(group_call_id), (std::time(nullptr) - 5) * 1000, 0, channel_id, nullptr)); + group_call_id, (std::time(nullptr) - 5) * 1000, 0, channel_id, nullptr)); } else if (op == "ssgc") { - send_request(td_api::make_object(as_group_call_id(args))); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "tgcesn" || op == "tgcesne") { - send_request(td_api::make_object(as_group_call_id(args), - op == "tgcesne")); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request( + td_api::make_object(group_call_id, op == "tgcesne")); } else if (op == "jgc" || op == "jgcv" || op == "sgcss") { - string group_call_id; + GroupCallId group_call_id; string participant_id; string invite_hash; get_args(args, group_call_id, participant_id, invite_hash); @@ -3090,106 +3122,108 @@ class CliClient final : public Actor { sim_sources + ",\"semantics\":\"SIM\"},{\"sources\":" + fid_sources + ",\"semantics\":\"FID\"}]}"; } if (op == "sgcss") { - send_request(td_api::make_object( - as_group_call_id(group_call_id), group_call_source_ + 1, std::move(payload))); + send_request(td_api::make_object(group_call_id, group_call_source_ + 1, + std::move(payload))); } else { - send_request(td_api::make_object(as_group_call_id(group_call_id), - as_message_sender(participant_id), group_call_source_, - std::move(payload), true, true, invite_hash)); + send_request(td_api::make_object(group_call_id, as_message_sender(participant_id), + group_call_source_, std::move(payload), true, true, + invite_hash)); } } else if (op == "tgcssip") { - string group_call_id; + GroupCallId group_call_id; bool is_paused; get_args(args, group_call_id, is_paused); - send_request(td_api::make_object(as_group_call_id(group_call_id), - is_paused)); + send_request(td_api::make_object(group_call_id, is_paused)); } else if (op == "egcss") { - const string &group_call_id = args; - send_request(td_api::make_object(as_group_call_id(group_call_id))); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "sgct") { - string group_call_id; + GroupCallId group_call_id; string title; get_args(args, group_call_id, title); - send_request(td_api::make_object(as_group_call_id(group_call_id), title)); + send_request(td_api::make_object(group_call_id, title)); } else if (op == "tgcmnp" || op == "tgcmnpe") { - send_request( - td_api::make_object(as_group_call_id(args), op == "tgcmnpe")); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id, op == "tgcmnpe")); } else if (op == "rgcil") { - send_request(td_api::make_object(as_group_call_id(args))); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "tgcimvp") { - string group_call_id; + GroupCallId group_call_id; bool is_my_video_paused; get_args(args, group_call_id, is_my_video_paused); - send_request(td_api::make_object(as_group_call_id(group_call_id), - is_my_video_paused)); + send_request(td_api::make_object(group_call_id, is_my_video_paused)); } else if (op == "tgcimve") { - string group_call_id; + GroupCallId group_call_id; bool is_my_video_enabled; get_args(args, group_call_id, is_my_video_enabled); - send_request(td_api::make_object(as_group_call_id(group_call_id), - is_my_video_enabled)); + send_request(td_api::make_object(group_call_id, is_my_video_enabled)); } else if (op == "sgcpis") { - string group_call_id; + GroupCallId group_call_id; int32 source_id; bool is_speaking; get_args(args, group_call_id, source_id, is_speaking); - send_request(td_api::make_object(as_group_call_id(group_call_id), - source_id, is_speaking)); + send_request( + td_api::make_object(group_call_id, source_id, is_speaking)); } else if (op == "igcp") { - string group_call_id; + GroupCallId group_call_id; string user_ids; get_args(args, group_call_id, user_ids); - send_request(td_api::make_object(as_group_call_id(group_call_id), - as_user_ids(user_ids))); + send_request(td_api::make_object(group_call_id, as_user_ids(user_ids))); } else if (op == "ggcil") { - string group_call_id; + GroupCallId group_call_id; bool can_self_unmute; get_args(args, group_call_id, can_self_unmute); - send_request( - td_api::make_object(as_group_call_id(group_call_id), can_self_unmute)); + send_request(td_api::make_object(group_call_id, can_self_unmute)); } else if (op == "sgcr") { - string group_call_id; + GroupCallId group_call_id; string title; bool record_video; bool use_portrait_orientation; get_args(args, group_call_id, title, record_video, use_portrait_orientation); - send_request(td_api::make_object(as_group_call_id(group_call_id), title, - record_video, use_portrait_orientation)); + send_request(td_api::make_object(group_call_id, title, record_video, + use_portrait_orientation)); } else if (op == "egcr") { - string group_call_id; + GroupCallId group_call_id; get_args(args, group_call_id); - send_request(td_api::make_object(as_group_call_id(group_call_id))); + send_request(td_api::make_object(group_call_id)); } else if (op == "tgcpim") { - string group_call_id; + GroupCallId group_call_id; string participant_id; bool is_muted; get_args(args, group_call_id, participant_id, is_muted); send_request(td_api::make_object( - as_group_call_id(group_call_id), as_message_sender(participant_id), is_muted)); + group_call_id, as_message_sender(participant_id), is_muted)); } else if (op == "sgcpvl") { - string group_call_id; + GroupCallId group_call_id; string participant_id; int32 volume_level; get_args(args, group_call_id, participant_id, volume_level); send_request(td_api::make_object( - as_group_call_id(group_call_id), as_message_sender(participant_id), volume_level)); + group_call_id, as_message_sender(participant_id), volume_level)); } else if (op == "tgcpihr") { - string group_call_id; + GroupCallId group_call_id; string participant_id; bool is_hand_raised; get_args(args, group_call_id, participant_id, is_hand_raised); send_request(td_api::make_object( - as_group_call_id(group_call_id), as_message_sender(participant_id), is_hand_raised)); + group_call_id, as_message_sender(participant_id), is_hand_raised)); } else if (op == "lgcp") { - string group_call_id; + GroupCallId group_call_id; string limit; get_args(args, group_call_id, limit); - send_request( - td_api::make_object(as_group_call_id(group_call_id), as_limit(limit))); + send_request(td_api::make_object(group_call_id, as_limit(limit))); } else if (op == "lgc") { - send_request(td_api::make_object(as_group_call_id(args))); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "egc") { - send_request(td_api::make_object(as_group_call_id(args))); + GroupCallId group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(group_call_id)); } else if (op == "rpcil") { ChatId chat_id; get_args(args, chat_id); diff --git a/td/telegram/files/FileDownloader.cpp b/td/telegram/files/FileDownloader.cpp index a694426d3..bc701dbba 100644 --- a/td/telegram/files/FileDownloader.cpp +++ b/td/telegram/files/FileDownloader.cpp @@ -63,7 +63,7 @@ Result FileDownloader::init() { if (encryption_key_.is_secure() && !encryption_key_.has_value_hash()) { LOG(ERROR) << "Can't download Secure file with unknown value_hash"; } - if (remote_.file_type_ == FileType::Secure) { + if (remote_.file_type_ == FileType::SecureEncrypted) { size_ = 0; } int32 part_size = 0; diff --git a/td/telegram/files/FileLoadManager.cpp b/td/telegram/files/FileLoadManager.cpp index 79b0bf512..712b4dc9e 100644 --- a/td/telegram/files/FileLoadManager.cpp +++ b/td/telegram/files/FileLoadManager.cpp @@ -45,7 +45,7 @@ void FileLoadManager::download(QueryId id, const FullRemoteFileLocation &remote_ if (stop_flag_) { return; } - CHECK(query_id_to_node_id_.find(id) == query_id_to_node_id_.end()); + CHECK(query_id_to_node_id_.count(id) == 0); NodeId node_id = nodes_container_.create(Node()); Node *node = nodes_container_.get(node_id); CHECK(node); @@ -68,7 +68,7 @@ void FileLoadManager::upload(QueryId id, const LocalFileLocation &local_location if (stop_flag_) { return; } - CHECK(query_id_to_node_id_.find(id) == query_id_to_node_id_.end()); + CHECK(query_id_to_node_id_.count(id) == 0); NodeId node_id = nodes_container_.create(Node()); Node *node = nodes_container_.get(node_id); CHECK(node); @@ -86,7 +86,7 @@ void FileLoadManager::upload_by_hash(QueryId id, const FullLocalFileLocation &lo if (stop_flag_) { return; } - CHECK(query_id_to_node_id_.find(id) == query_id_to_node_id_.end()); + CHECK(query_id_to_node_id_.count(id) == 0); NodeId node_id = nodes_container_.create(Node()); Node *node = nodes_container_.get(node_id); CHECK(node); @@ -117,7 +117,7 @@ void FileLoadManager::from_bytes(QueryId id, FileType type, BufferSlice bytes, s if (stop_flag_) { return; } - CHECK(query_id_to_node_id_.find(id) == query_id_to_node_id_.end()); + CHECK(query_id_to_node_id_.count(id) == 0); NodeId node_id = nodes_container_.create(Node()); Node *node = nodes_container_.get(node_id); CHECK(node); diff --git a/td/telegram/files/FileLocation.h b/td/telegram/files/FileLocation.h index d1980befb..2a120afc4 100644 --- a/td/telegram/files/FileLocation.h +++ b/td/telegram/files/FileLocation.h @@ -192,32 +192,17 @@ class FullRemoteFileLocation { if (is_web()) { return LocationType::Web; } - switch (file_type_) { - case FileType::Photo: - case FileType::ProfilePhoto: - case FileType::Thumbnail: - case FileType::EncryptedThumbnail: - case FileType::Wallpaper: + switch (get_file_type_class(file_type_)) { + case FileTypeClass::Photo: return LocationType::Photo; - case FileType::Video: - case FileType::VoiceNote: - case FileType::Document: - case FileType::Sticker: - case FileType::Audio: - case FileType::Animation: - case FileType::Encrypted: - case FileType::VideoNote: - case FileType::SecureRaw: - case FileType::Secure: - case FileType::Background: - case FileType::DocumentAsFile: - case FileType::Ringtone: + case FileTypeClass::Document: + case FileTypeClass::Secure: + case FileTypeClass::Encrypted: return LocationType::Common; - case FileType::None: - case FileType::Size: + case FileTypeClass::Temp: + return LocationType::None; default: UNREACHABLE(); - case FileType::Temp: return LocationType::None; } } @@ -376,13 +361,13 @@ class FullRemoteFileLocation { return file_type_ == FileType::Encrypted; } bool is_encrypted_secure() const { - return file_type_ == FileType::Secure; + return file_type_ == FileType::SecureEncrypted; } bool is_encrypted_any() const { return is_encrypted_secret() || is_encrypted_secure(); } bool is_secure() const { - return file_type_ == FileType::SecureRaw || file_type_ == FileType::Secure; + return file_type_ == FileType::SecureDecrypted || file_type_ == FileType::SecureEncrypted; } bool is_document() const { return is_common() && !is_secure() && !is_encrypted_secret(); diff --git a/td/telegram/files/FileLocation.hpp b/td/telegram/files/FileLocation.hpp index 30b485804..751a6deb1 100644 --- a/td/telegram/files/FileLocation.hpp +++ b/td/telegram/files/FileLocation.hpp @@ -289,37 +289,7 @@ void FullRemoteFileLocation::AsUnique::store(StorerT &storer) const { if (key->is_web()) { return 0; } - switch (key->file_type_) { - case FileType::Photo: - case FileType::ProfilePhoto: - case FileType::Thumbnail: - case FileType::EncryptedThumbnail: - case FileType::Wallpaper: - return 1; - case FileType::Video: - case FileType::VoiceNote: - case FileType::Document: - case FileType::Sticker: - case FileType::Audio: - case FileType::Animation: - case FileType::VideoNote: - case FileType::Background: - case FileType::DocumentAsFile: - case FileType::Ringtone: - return 2; - case FileType::SecureRaw: - case FileType::Secure: - return 3; - case FileType::Encrypted: - return 4; - case FileType::Temp: - return 5; - case FileType::None: - case FileType::Size: - default: - UNREACHABLE(); - return -1; - } + return static_cast(get_file_type_class(key->file_type_)) + 1; }(); store(type, storer); key.variant_.visit([&](auto &&value) { diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index dfab6dc83..df0be24f8 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -924,9 +924,10 @@ string FileManager::get_file_name(FileType file_type, Slice path) { case FileType::Encrypted: case FileType::Temp: case FileType::EncryptedThumbnail: - case FileType::Secure: - case FileType::SecureRaw: + case FileType::SecureEncrypted: + case FileType::SecureDecrypted: case FileType::DocumentAsFile: + case FileType::CallLog: break; default: UNREACHABLE(); @@ -2559,8 +2560,7 @@ void FileManager::resume_upload(FileId file_id, std::vector bad_parts, std: node->set_upload_pause(FileId()); } FileView file_view(node); - if (file_view.has_active_upload_remote_location() && file_view.get_type() != FileType::Thumbnail && - file_view.get_type() != FileType::EncryptedThumbnail && file_view.get_type() != FileType::Background) { + if (file_view.has_active_upload_remote_location() && can_reuse_remote_file(file_view.get_type())) { LOG(INFO) << "File " << file_id << " is already uploaded"; if (callback) { callback->on_upload_ok(file_id, nullptr); @@ -2797,7 +2797,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { << ", generate_id = " << node->generate_id_ << ", generate_was_update = " << node->generate_was_update_; return; } - if (file_view.has_generate_location() && file_view.generate_location().file_type_ == FileType::Secure) { + if (file_view.has_generate_location() && file_view.generate_location().file_type_ == FileType::SecureEncrypted) { // Can't upload secure file before its size is known LOG(INFO) << "Can't upload secure file " << node->main_file_id_ << " before it's size is known"; return; @@ -2816,7 +2816,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { } // create encryption key if necessary - if (file_view.has_local_location() && file_view.local_location().file_type_ == FileType::Secure && + if (file_view.has_local_location() && file_view.local_location().file_type_ == FileType::SecureEncrypted && file_view.encryption_key().empty()) { CHECK(!node->file_ids_.empty()); bool success = set_encryption_key(node->file_ids_[0], FileEncryptionKey::create_secure_key()); @@ -2832,8 +2832,7 @@ void FileManager::run_upload(FileNodePtr node, std::vector bad_parts) { CHECK(node->upload_id_ == 0); if (file_view.has_alive_remote_location() && !file_view.has_active_upload_remote_location() && - file_view.get_type() != FileType::Thumbnail && file_view.get_type() != FileType::EncryptedThumbnail && - file_view.get_type() != FileType::Background) { + can_reuse_remote_file(file_view.get_type())) { QueryId id = queries_container_.create(Query{file_id, Query::Type::UploadWaitFileReference}); node->upload_id_ = id; if (node->upload_was_update_file_reference_) { @@ -2884,12 +2883,6 @@ void FileManager::cancel_upload(FileId file_id) { return resume_upload(file_id, std::vector(), nullptr, 0, 0); } -static bool is_document_type(FileType type) { - return type == FileType::Document || type == FileType::Sticker || type == FileType::Audio || - type == FileType::Animation || type == FileType::VoiceNote || type == FileType::Background || - type == FileType::DocumentAsFile || type == FileType::Ringtone; -} - static bool is_background_type(FileType type) { return type == FileType::Wallpaper || type == FileType::Background; } @@ -2963,7 +2956,7 @@ Result FileManager::from_persistent_id_v23(Slice binary, FileType file_t return Status::Error(400, "Wrong remote file identifier specified: can't unserialize it"); } auto &real_file_type = remote_location.file_type_; - if (is_document_type(real_file_type) && is_document_type(file_type)) { + if (is_document_file_type(real_file_type) && is_document_file_type(file_type)) { real_file_type = file_type; } else if (is_background_type(real_file_type) && is_background_type(file_type)) { // type of file matches, but real type is in the stored remote location @@ -3083,7 +3076,7 @@ Result FileManager::check_input_file_id(FileType type, Result re LOG(INFO) << "Checking file " << file_id << " of type " << type << "/" << real_type; if (!is_encrypted && !is_secure) { if (real_type != type && !(real_type == FileType::Temp && file_view.has_url()) && - !(is_document_type(real_type) && is_document_type(type)) && + !(is_document_file_type(real_type) && is_document_file_type(type)) && !(is_background_type(real_type) && is_background_type(type)) && !(file_view.is_encrypted() && type == FileType::Ringtone)) { // TODO: send encrypted file to unencrypted chat @@ -3156,7 +3149,7 @@ Result FileManager::get_input_file_id(FileType type, const tl_object_ptr get_by_hash = false; } - auto new_type = is_encrypted ? FileType::Encrypted : (is_secure ? FileType::Secure : type); + auto new_type = is_encrypted ? FileType::Encrypted : (is_secure ? FileType::SecureEncrypted : type); auto r_file_id = [&]() -> Result { switch (file->get_id()) { @@ -3393,13 +3386,14 @@ FileId FileManager::next_file_id() { empty_file_ids_.pop_back(); return FileId{res, 0}; } + CHECK(file_id_info_.size() <= static_cast(std::numeric_limits::max())); FileId res(static_cast(file_id_info_.size()), 0); - // LOG(ERROR) << "NEXT file_id " << res; file_id_info_.push_back({}); return res; } FileManager::FileNodeId FileManager::next_file_node_id() { + CHECK(file_nodes_.size() <= static_cast(std::numeric_limits::max())); auto res = static_cast(file_nodes_.size()); file_nodes_.emplace_back(nullptr); return res; @@ -3937,6 +3931,10 @@ void FileManager::memory_stats(vector &output) { void FileManager::tear_down() { parent_.reset(); + + LOG(DEBUG) << "Have " << file_id_info_.size() << " files with " << file_nodes_.size() << " file nodes, " + << local_location_to_file_id_.size() << " local locations and " << remote_location_info_.size() + << " remote locations to free"; } constexpr int64 FileManager::KEEP_DOWNLOAD_LIMIT; diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index a8fb34850..0d12bbdfb 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -305,10 +305,10 @@ class FileView { return get_type() == FileType::Encrypted; } bool is_encrypted_secure() const { - return get_type() == FileType::Secure; + return get_type() == FileType::SecureEncrypted; } bool is_secure() const { - return get_type() == FileType::Secure || get_type() == FileType::SecureRaw; + return get_type() == FileType::SecureEncrypted || get_type() == FileType::SecureDecrypted; } bool is_encrypted_any() const { return is_encrypted_secret() || is_encrypted_secure(); diff --git a/td/telegram/files/FileType.cpp b/td/telegram/files/FileType.cpp index c7cbf3ee0..b5dbec2a3 100644 --- a/td/telegram/files/FileType.cpp +++ b/td/telegram/files/FileType.cpp @@ -39,7 +39,7 @@ FileType get_file_type(const td_api::FileType &file_type) { case td_api::fileTypeVideoNote::ID: return FileType::VideoNote; case td_api::fileTypeSecure::ID: - return FileType::Secure; + return FileType::SecureEncrypted; case td_api::fileTypeNotificationSound::ID: return FileType::Ringtone; case td_api::fileTypeNone::ID: @@ -80,9 +80,9 @@ tl_object_ptr get_file_type_object(FileType file_type) { return make_tl_object(); case FileType::VideoNote: return make_tl_object(); - case FileType::Secure: + case FileType::SecureEncrypted: return make_tl_object(); - case FileType::SecureRaw: + case FileType::SecureDecrypted: UNREACHABLE(); return make_tl_object(); case FileType::Background: @@ -91,6 +91,8 @@ tl_object_ptr get_file_type_object(FileType file_type) { return make_tl_object(); case FileType::Ringtone: return make_tl_object(); + case FileType::CallLog: + return make_tl_object(); case FileType::None: return make_tl_object(); default: @@ -103,10 +105,12 @@ FileType get_main_file_type(FileType file_type) { switch (file_type) { case FileType::Wallpaper: return FileType::Background; - case FileType::SecureRaw: - return FileType::Secure; + case FileType::SecureDecrypted: + return FileType::SecureEncrypted; case FileType::DocumentAsFile: return FileType::Document; + case FileType::CallLog: + return FileType::Document; default: return file_type; } @@ -142,9 +146,9 @@ CSlice get_file_type_name(FileType file_type) { return CSlice("wallpapers"); case FileType::VideoNote: return CSlice("video_notes"); - case FileType::SecureRaw: + case FileType::SecureDecrypted: return CSlice("passport"); - case FileType::Secure: + case FileType::SecureEncrypted: return CSlice("passport"); case FileType::Background: return CSlice("wallpapers"); @@ -152,6 +156,8 @@ CSlice get_file_type_name(FileType file_type) { return CSlice("documents"); case FileType::Ringtone: return CSlice("notification_sounds"); + case FileType::CallLog: + return CSlice("documents"); case FileType::Size: case FileType::None: default: @@ -160,6 +166,45 @@ CSlice get_file_type_name(FileType file_type) { } } +FileTypeClass get_file_type_class(FileType file_type) { + switch (file_type) { + case FileType::Photo: + case FileType::ProfilePhoto: + case FileType::Thumbnail: + case FileType::EncryptedThumbnail: + case FileType::Wallpaper: + return FileTypeClass::Photo; + case FileType::Video: + case FileType::VoiceNote: + case FileType::Document: + case FileType::Sticker: + case FileType::Audio: + case FileType::Animation: + case FileType::VideoNote: + case FileType::Background: + case FileType::DocumentAsFile: + case FileType::Ringtone: + case FileType::CallLog: + return FileTypeClass::Document; + case FileType::SecureDecrypted: + case FileType::SecureEncrypted: + return FileTypeClass::Secure; + case FileType::Encrypted: + return FileTypeClass::Encrypted; + case FileType::Temp: + return FileTypeClass::Temp; + case FileType::None: + case FileType::Size: + default: + UNREACHABLE(); + return FileTypeClass::Temp; + } +} + +bool is_document_file_type(FileType file_type) { + return get_file_type_class(file_type) == FileTypeClass::Document; +} + StringBuilder &operator<<(StringBuilder &string_builder, FileType file_type) { return string_builder << get_file_type_name(file_type); } @@ -173,8 +218,8 @@ FileDirType get_file_dir_type(FileType file_type) { case FileType::Temp: case FileType::Wallpaper: case FileType::EncryptedThumbnail: - case FileType::Secure: - case FileType::SecureRaw: + case FileType::SecureEncrypted: + case FileType::SecureDecrypted: case FileType::Background: case FileType::Ringtone: return FileDirType::Secure; @@ -191,6 +236,7 @@ bool is_file_big(FileType file_type, int64 expected_size) { case FileType::EncryptedThumbnail: case FileType::VideoNote: case FileType::Ringtone: + case FileType::CallLog: return false; default: break; @@ -200,4 +246,16 @@ bool is_file_big(FileType file_type, int64 expected_size) { return expected_size > SMALL_FILE_MAX_SIZE; } +bool can_reuse_remote_file(FileType file_type) { + switch (file_type) { + case FileType::Thumbnail: + case FileType::EncryptedThumbnail: + case FileType::Background: + case FileType::CallLog: + return false; + default: + return true; + } +} + } // namespace td diff --git a/td/telegram/files/FileType.h b/td/telegram/files/FileType.h index 7adc360d3..5281c7901 100644 --- a/td/telegram/files/FileType.h +++ b/td/telegram/files/FileType.h @@ -29,11 +29,12 @@ enum class FileType : int32 { EncryptedThumbnail, Wallpaper, VideoNote, - SecureRaw, - Secure, + SecureDecrypted, + SecureEncrypted, Background, DocumentAsFile, Ringtone, + CallLog, Size, None }; @@ -50,10 +51,18 @@ FileType get_main_file_type(FileType file_type); CSlice get_file_type_name(FileType file_type); +enum class FileTypeClass : int32 { Photo, Document, Secure, Encrypted, Temp }; + +FileTypeClass get_file_type_class(FileType file_type); + +bool is_document_file_type(FileType file_type); + StringBuilder &operator<<(StringBuilder &string_builder, FileType file_type); FileDirType get_file_dir_type(FileType file_type); bool is_file_big(FileType file_type, int64 expected_size); +bool can_reuse_remote_file(FileType file_type); + } // namespace td diff --git a/td/telegram/net/NetStatsManager.h b/td/telegram/net/NetStatsManager.h index 140cf5d1d..a869aa1f2 100644 --- a/td/telegram/net/NetStatsManager.h +++ b/td/telegram/net/NetStatsManager.h @@ -52,7 +52,7 @@ struct NetworkStats { result->since_date_ = since; result->entries_.reserve(entries.size()); for (const auto &entry : entries) { - if ((entry.rx != 0 || entry.tx != 0) && entry.file_type != FileType::SecureRaw) { + if ((entry.rx != 0 || entry.tx != 0) && entry.file_type != FileType::SecureDecrypted) { result->entries_.push_back(entry.get_network_statistics_entry_object()); } } diff --git a/tdactor/td/actor/Timeout.cpp b/tdactor/td/actor/Timeout.cpp index 95b94c79a..a3b191ab6 100644 --- a/tdactor/td/actor/Timeout.cpp +++ b/tdactor/td/actor/Timeout.cpp @@ -12,7 +12,7 @@ namespace td { bool MultiTimeout::has_timeout(int64 key) const { - return items_.find(Item(key)) != items_.end(); + return items_.count(Item(key)) > 0; } void MultiTimeout::set_timeout_at(int64 key, double timeout) { diff --git a/tdnet/td/net/HttpConnectionBase.cpp b/tdnet/td/net/HttpConnectionBase.cpp index ead46ab10..c1f630ea1 100644 --- a/tdnet/td/net/HttpConnectionBase.cpp +++ b/tdnet/td/net/HttpConnectionBase.cpp @@ -129,7 +129,7 @@ void HttpConnectionBase::loop() { live_event(); state_ = State::Write; if (res.error().code() == 500) { - LOG(WARNING) << "Failed to process an HTTP query:" << res.error(); + LOG(WARNING) << "Failed to process an HTTP query: " << res.error(); } else { LOG(INFO) << res.error(); } diff --git a/tdutils/td/utils/Enumerator.h b/tdutils/td/utils/Enumerator.h index 998cd8436..e89fd6718 100644 --- a/tdutils/td/utils/Enumerator.h +++ b/tdutils/td/utils/Enumerator.h @@ -37,6 +37,11 @@ class Enumerator { return *arr_[pos]; } + size_t size() const { + CHECK(map_.size() == arr_.size()); + return arr_.size(); + } + private: std::map map_; std::vector arr_; diff --git a/tdutils/td/utils/Hints.cpp b/tdutils/td/utils/Hints.cpp index bbffba6af..dfe584fc5 100644 --- a/tdutils/td/utils/Hints.cpp +++ b/tdutils/td/utils/Hints.cpp @@ -211,7 +211,7 @@ std::pair> Hints::search(Slice query, int32 limit, b } bool Hints::has_key(KeyT key) const { - return key_to_name_.find(key) != key_to_name_.end(); + return key_to_name_.count(key) > 0; } string Hints::key_to_string(KeyT key) const { diff --git a/tdutils/td/utils/filesystem.cpp b/tdutils/td/utils/filesystem.cpp index c4f5e4282..ae7483cf4 100644 --- a/tdutils/td/utils/filesystem.cpp +++ b/tdutils/td/utils/filesystem.cpp @@ -159,8 +159,8 @@ string clean_filename(CSlice name) { } PathView path_view(name); - auto filename = clean_filename_part(path_view.file_stem(), 60); - auto extension = clean_filename_part(path_view.extension(), 20); + auto filename = clean_filename_part(path_view.file_stem(), 64); + auto extension = clean_filename_part(path_view.extension(), 16); if (!extension.empty()) { if (filename.empty()) { filename = std::move(extension); diff --git a/tdutils/td/utils/port/FileFd.cpp b/tdutils/td/utils/port/FileFd.cpp index 2a50dbc13..6300f9f85 100644 --- a/tdutils/td/utils/port/FileFd.cpp +++ b/tdutils/td/utils/port/FileFd.cpp @@ -391,7 +391,7 @@ static Status create_local_lock(const string &path, int32 &max_tries) { while (true) { { // mutex lock scope std::lock_guard lock(in_process_lock_mutex); - if (!path.empty() && locked_files.find(path) == locked_files.end()) { + if (!path.empty() && locked_files.count(path) == 0) { VLOG(fd) << "Lock file \"" << path << '"'; locked_files.insert(path); return Status::OK();