diff --git a/CMakeLists.txt b/CMakeLists.txt index 96d1f8ae..757d4338 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) -project(TDLib VERSION 1.0.0 LANGUAGES CXX C) +project(TDLib VERSION 1.0.1 LANGUAGES CXX C) if (NOT DEFINED CMAKE_MODULE_PATH) set(CMAKE_MODULE_PATH "") diff --git a/example/cpp/CMakeLists.txt b/example/cpp/CMakeLists.txt index a4968802..5f161dde 100644 --- a/example/cpp/CMakeLists.txt +++ b/example/cpp/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(TdExample VERSION 1.0 LANGUAGES CXX) -find_package(Td 1.0.0) +find_package(Td 1.0.1) add_executable(tdjson_example tdjson_example.cpp) target_link_libraries(tdjson_example PRIVATE Td::TdJson) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index b060ad0f..91b70569 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2613,8 +2613,8 @@ getSupportUser = User; getWallpapers = Wallpapers; -//@description Registers the currently used device for receiving push notifications @device_token Device token -registerDevice device_token:DeviceToken = Ok; +//@description Registers the currently used device for receiving push notifications @device_token Device token @other_user_ids List of at most 100 user identifiers of other users currently using the client +registerDevice device_token:DeviceToken other_user_ids:vector = Ok; //@description Returns t.me URLs recently visited by a newly registered user @referrer Google Play referrer to identify the user diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 095fabb2..a88e64f2 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/DeviceTokenManager.cpp b/td/telegram/DeviceTokenManager.cpp index 42ab85e7..98f7c91e 100644 --- a/td/telegram/DeviceTokenManager.cpp +++ b/td/telegram/DeviceTokenManager.cpp @@ -9,6 +9,7 @@ #include "td/telegram/Global.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQueryDispatcher.h" +#include "td/telegram/UserId.h" #include "td/telegram/td_api.hpp" #include "td/telegram/telegram_api.h" @@ -16,120 +17,197 @@ #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/Status.h" +#include "td/utils/tl_helpers.h" #include namespace td { -void DeviceTokenManager::register_device(tl_object_ptr device_token, - Promise> promise) { - Token token(*device_token); - if (!clean_input_string(token.token)) { - return promise.set_error(Status::Error(400, "Device token must be encoded in UTF-8")); - } - auto &info = tokens_[token.type]; - info.net_query_id = 0; - if (token.token.empty()) { - info.state = TokenInfo::State::Unregister; - if (info.token.empty()) { - info.state = TokenInfo::State::Sync; - } +template +void DeviceTokenManager::TokenInfo::store(StorerT &storer) const { + using td::store; + bool has_other_user_ids = !other_user_ids.empty(); + bool is_sync = state == State::Sync; + bool is_unregister = state == State::Unregister; + bool is_register = state == State::Register; + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_other_user_ids); + STORE_FLAG(is_sync); + STORE_FLAG(is_unregister); + STORE_FLAG(is_register); + END_STORE_FLAGS(); + store(token, storer); + if (has_other_user_ids) { + store(other_user_ids, storer); + } +} + +template +void DeviceTokenManager::TokenInfo::parse(ParserT &parser) { + using td::parse; + bool has_other_user_ids; + bool is_sync; + bool is_unregister; + bool is_register; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_other_user_ids); + PARSE_FLAG(is_sync); + PARSE_FLAG(is_unregister); + PARSE_FLAG(is_register); + END_PARSE_FLAGS(); + CHECK(is_sync + is_unregister + is_register == 1); + if (is_sync) { + state = State::Sync; + } else if (is_unregister) { + state = State::Unregister; } else { - info.token = token.token; - info.state = TokenInfo::State::Register; + state = State::Register; } - if (info.promise) { - info.promise.set_error(Status::Error(5, "Cancelled due to new registerDevice request")); + parse(token, parser); + if (has_other_user_ids) { + parse(other_user_ids, parser); } - info.promise = std::move(promise); - save_info(token.type); - loop(); } -DeviceTokenManager::Token::Token(td_api::DeviceToken &device_token) { - bool ok = downcast_call(device_token, [&](auto &obj) { token = obj.token_; }); - CHECK(ok); - type = [&] { - switch (device_token.get_id()) { - case td_api::deviceTokenApplePush::ID: - return TokenType::APNS; - case td_api::deviceTokenGoogleCloudMessaging::ID: - return TokenType::GCM; - case td_api::deviceTokenMicrosoftPush::ID: - return TokenType::MPNS; - case td_api::deviceTokenUbuntuPush::ID: - return TokenType::UbuntuPhone; - case td_api::deviceTokenBlackberryPush::ID: - return TokenType::Blackberry; - default: - UNREACHABLE(); - } - }(); -} -tl_object_ptr DeviceTokenManager::Token::as_td_api() { - switch (type) { - case TokenType::APNS: - return make_tl_object(token); - case TokenType::GCM: - return make_tl_object(token); - case TokenType::MPNS: - return make_tl_object(token); - case TokenType::SimplePush: - return make_tl_object(token); - case TokenType::UbuntuPhone: - return make_tl_object(token); - case TokenType::Blackberry: - return make_tl_object(token); +StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManager::TokenInfo &token_info) { + switch (token_info.state) { + case DeviceTokenManager::TokenInfo::State::Sync: + string_builder << "Synchronized"; + break; + case DeviceTokenManager::TokenInfo::State::Unregister: + string_builder << "Unregister"; + break; + case DeviceTokenManager::TokenInfo::State::Register: + string_builder << "Register"; + break; default: UNREACHABLE(); } -} -DeviceTokenManager::TokenInfo::TokenInfo(string from) { - if (from.empty()) { - return; + string_builder << " token \"" << format::escaped(token_info.token) << "\""; + if (!token_info.other_user_ids.empty()) { + string_builder << ", with other users " << token_info.other_user_ids; } - char c = from[0]; - if (c == '+') { - state = State::Register; - } else if (c == '-') { - state = State::Unregister; - } else if (c == '=') { - state = State::Sync; - } else { - LOG(ERROR) << "Invalid serialized TokenInfo: " << tag("token_info", from); - return; - } - token = from.substr(1); + return string_builder; } -string DeviceTokenManager::TokenInfo::serialize() { - char c = [&] { - switch (state) { - case State::Sync: - return '='; - case State::Unregister: - return '-'; - case State::Register: - return '+'; - default: - UNREACHABLE(); +void DeviceTokenManager::register_device(tl_object_ptr device_token_ptr, + vector other_user_ids, Promise> promise) { + CHECK(device_token_ptr != nullptr); + TokenType token_type; + string token; + switch (device_token_ptr->get_id()) { + case td_api::deviceTokenApplePush::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::APNS; + break; } - }(); - return c + token; + case td_api::deviceTokenGoogleCloudMessaging::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::GCM; + break; + } + case td_api::deviceTokenMicrosoftPush::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::MPNS; + break; + } + case td_api::deviceTokenSimplePush::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::SimplePush; + break; + } + case td_api::deviceTokenUbuntuPush::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::UbuntuPhone; + break; + } + case td_api::deviceTokenBlackberryPush::ID: { + auto device_token = static_cast(device_token_ptr.get()); + token = std::move(device_token->token_); + token_type = TokenType::Blackberry; + break; + } + default: + UNREACHABLE(); + } + if (!clean_input_string(token)) { + return promise.set_error(Status::Error(400, "Device token must be encoded in UTF-8")); + } + for (auto &other_user_id : other_user_ids) { + UserId user_id(other_user_id); + if (!user_id.is_valid()) { + return promise.set_error(Status::Error(400, "Invalid user_id among other user_ids")); + } + } + if (other_user_ids.size() > MAX_OTHER_USER_IDS) { + return promise.set_error(Status::Error(400, "Too much other user_ids")); + } + + auto &info = tokens_[token_type]; + info.net_query_id = 0; + if (token.empty()) { + if (info.token.empty()) { + // already unregistered + return promise.set_value(make_tl_object()); + } + + info.state = TokenInfo::State::Unregister; + } else { + info.state = TokenInfo::State::Register; + info.token = std::move(token); + } + info.other_user_ids = std::move(other_user_ids); + info.promise.set_value(make_tl_object()); + info.promise = std::move(promise); + save_info(token_type); +} + +string DeviceTokenManager::get_database_key(int32 token_type) { + return PSTRING() << "device_token" << token_type; } void DeviceTokenManager::start_up() { for (int32 token_type = 1; token_type < TokenType::Size; token_type++) { - tokens_[token_type] = TokenInfo(G()->td_db()->get_binlog_pmc()->get(PSTRING() << "device_token" << token_type)); - LOG(INFO) << token_type << "--->" << tokens_[token_type].serialize(); + auto serialized = G()->td_db()->get_binlog_pmc()->get(get_database_key(token_type)); + if (serialized.empty()) { + continue; + } + + auto &token = tokens_[token_type]; + char c = serialized[0]; + if (c == '*') { + unserialize(token, serialized.substr(1)).ensure(); + } else { + // legacy + if (c == '+') { + token.state = TokenInfo::State::Register; + } else if (c == '-') { + token.state = TokenInfo::State::Unregister; + } else if (c == '=') { + token.state = TokenInfo::State::Sync; + } else { + LOG(ERROR) << "Invalid serialized TokenInfo: " << format::escaped(serialized); + continue; + } + token.token = serialized.substr(1); + } + LOG(INFO) << "GET device token " << token_type << "--->" << tokens_[token_type]; } loop(); } + void DeviceTokenManager::save_info(int32 token_type) { - auto key = PSTRING() << "device_token" << token_type; - string value = tokens_[token_type].serialize(); - LOG(INFO) << "SET " << key << "--->" << value; - G()->td_db()->get_binlog_pmc()->set(key, value); + LOG(INFO) << "SET device token " << token_type << "--->" << tokens_[token_type]; + if (tokens_[token_type].token.empty()) { + G()->td_db()->get_binlog_pmc()->erase(get_database_key(token_type)); + } else { + G()->td_db()->get_binlog_pmc()->set(get_database_key(token_type), "*" + serialize(tokens_[token_type])); + } sync_cnt_++; G()->td_db()->get_binlog_pmc()->force_sync( PromiseCreator::event(self_closure(this, &DeviceTokenManager::dec_sync_cnt))); @@ -154,12 +232,13 @@ void DeviceTokenManager::loop() { } // have to send query NetQueryPtr net_query; + auto other_user_ids = info.other_user_ids; if (info.state == TokenInfo::State::Unregister) { net_query = G()->net_query_creator().create( - create_storer(telegram_api::account_unregisterDevice(token_type, info.token, {}))); + create_storer(telegram_api::account_unregisterDevice(token_type, info.token, std::move(other_user_ids)))); } else { - net_query = G()->net_query_creator().create( - create_storer(telegram_api::account_registerDevice(token_type, info.token, false, {}))); + net_query = G()->net_query_creator().create(create_storer( + telegram_api::account_registerDevice(token_type, info.token, false, std::move(other_user_ids)))); } info.net_query_id = net_query->id(); G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this, token_type)); @@ -209,6 +288,5 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { LOG(ERROR) << r_flag.error(); } } - loop(); } } // namespace td diff --git a/td/telegram/DeviceTokenManager.h b/td/telegram/DeviceTokenManager.h index 3e63c543..2cfbb357 100644 --- a/td/telegram/DeviceTokenManager.h +++ b/td/telegram/DeviceTokenManager.h @@ -18,44 +18,48 @@ #include namespace td { + class DeviceTokenManager : public NetQueryCallback { public: explicit DeviceTokenManager(ActorShared<> parent) : parent_(std::move(parent)) { } - void register_device(tl_object_ptr device_token, Promise> promise); + void register_device(tl_object_ptr device_token_ptr, vector other_user_ids, + Promise> promise); private: + static constexpr size_t MAX_OTHER_USER_IDS = 100; + ActorShared<> parent_; enum TokenType : int32 { APNS = 1, GCM = 2, MPNS = 3, SimplePush = 4, UbuntuPhone = 5, Blackberry = 6, Size }; - struct Token { - TokenType type; - string token; - Token(TokenType type, string token) : type(type), token(std::move(token)) { - } - explicit Token(td_api::DeviceToken &device_token); - tl_object_ptr as_td_api(); - }; struct TokenInfo { enum class State { Sync, Unregister, Register }; State state = State::Sync; string token; uint64 net_query_id = 0; + vector other_user_ids; Promise> promise; - TokenInfo() = default; - explicit TokenInfo(string from); + template + void store(StorerT &storer) const; - string serialize(); + template + void parse(ParserT &parser); }; + friend StringBuilder &operator<<(StringBuilder &string_builder, const TokenInfo &token_info); + std::array tokens_; int32 sync_cnt_{0}; void start_up() override; + + static string get_database_key(int32 token_type); void save_info(int32 token_type); + void dec_sync_cnt(); void loop() override; void on_result(NetQueryPtr net_query) override; }; + } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 3f5c35c7..b929a7b8 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -4598,7 +4598,7 @@ void Td::on_request(uint64 id, td_api::registerDevice &request) { CREATE_REQUEST_PROMISE(promise); send_closure(device_token_manager_, &DeviceTokenManager::register_device, std::move(request.device_token_), - std::move(promise)); + std::move(request.other_user_ids_), std::move(promise)); } void Td::on_request(uint64 id, td_api::getUserPrivacySettingRules &request) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 415ee5ac..4d38d8dd 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -187,7 +187,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr static_request(td_api::object_ptr function); private: - static constexpr const char *tdlib_version = "1.0.0"; + static constexpr const char *tdlib_version = "1.0.1"; static constexpr int32 ONLINE_TIMEOUT = 240; void send_result(uint64 id, tl_object_ptr object); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 21731175..fd1b2291 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1010,9 +1010,15 @@ class CliClient final : public Actor { std::tie(dc_id, ip_port) = split(args); send_request(make_tl_object(dc_id, ip_port)); } else if (op == "rdb" || op == "RegisterDeviceB") { - send_request(make_tl_object(make_tl_object(args))); + send_request(make_tl_object(make_tl_object(args), + as_user_ids(""))); } else if (op == "rdu" || op == "RegisterDeviceU") { - send_request(make_tl_object(make_tl_object(args))); + string token; + string other_user_ids_str; + + std::tie(token, other_user_ids_str) = split(args); + send_request(make_tl_object(make_tl_object(token), + as_user_ids(other_user_ids_str))); } else if (op == "gpf") { string chat_id; string message_id;