diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 285076381..184ea2d54 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -1775,6 +1775,10 @@ deviceTokenBlackBerryPush token:string = DeviceToken; deviceTokenTizenPush reg_id:string = DeviceToken; +//@description Contains a globally unique push receiver identifier, which can be used to identify which account has received a push notification @id The globally unique identifier of push notification subscription +pushReceiverId id:int64 = PushReceiverId; + + //@description Contains information about a wallpaper @id Unique persistent wallpaper identifier @sizes Available variants of the wallpaper in different sizes. These photos can only be downloaded; they can't be sent in a message @color Main color of the wallpaper in RGB24 format; should be treated as background color if no photos are specified wallpaper id:int32 sizes:vector color:int32 = Wallpaper; @@ -2512,13 +2516,6 @@ createTemporaryPassword password:string valid_for:int32 = TemporaryPasswordState getTemporaryPasswordState = TemporaryPasswordState; -//@description Handles a DC_UPDATE push service notification. Can be called before authorization @dc Value of the "dc" parameter of the notification @addr Value of the "addr" parameter of the notification -processDcUpdate dc:string addr:string = Ok; - -//@description Handles a push notification. Can be called before authorization @payload Push notification payload -processPushNotification payload:string = Ok; - - //@description Returns the current user getMe = User; @@ -3284,8 +3281,17 @@ setCustomLanguagePackString language_pack_id:string new_string:languagePackStrin deleteLanguagePack language_pack_id:string = 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 Registers the currently used device for receiving push notifications. Returns a globally unique identifier of the push notification subscription @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 = PushReceiverId; + +//@description Handles a DC_UPDATE push notification. Can be called before authorization @dc Value of the "dc" parameter of the push notification @addr Value of the "addr" parameter of the push notification +processDcUpdate dc:string addr:string = Ok; + +//@description Handles a push notification. Can be called before authorization @payload JSON-encoded push notification payload +processPushNotification payload:string = Ok; + +//@description Returns a globally unique push notification subscription identifier for identification of an account, which has received a push notification. This is an offline method. Can be called before authorization. Can be called synchronously @payload JSON-encoded push notification payload +getPushReceiverId payload:string = PushReceiverId; //@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 56ec7a1bd..25d4587ed 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 a3861322f..c5ab3ebf4 100644 --- a/td/telegram/DeviceTokenManager.cpp +++ b/td/telegram/DeviceTokenManager.cpp @@ -14,8 +14,10 @@ #include "td/telegram/td_api.hpp" #include "td/telegram/telegram_api.h" +#include "td/utils/as.h" #include "td/utils/base64.h" #include "td/utils/buffer.h" +#include "td/utils/crypto.h" #include "td/utils/format.h" #include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" @@ -48,6 +50,7 @@ void DeviceTokenManager::TokenInfo::store(StorerT &storer) const { } if (encrypt) { store(encryption_key, storer); + store(encryption_key_id, storer); } } @@ -80,6 +83,7 @@ void DeviceTokenManager::TokenInfo::parse(ParserT &parser) { } if (encrypt) { parse(encryption_key, parser); + parse(encryption_key_id, parser); } } @@ -111,7 +115,8 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DeviceTokenManage } void DeviceTokenManager::register_device(tl_object_ptr device_token_ptr, - vector other_user_ids, Promise> promise) { + vector other_user_ids, + Promise> promise) { CHECK(device_token_ptr != nullptr); TokenType token_type; string token; @@ -231,7 +236,7 @@ void DeviceTokenManager::register_device(tl_object_ptr devi if (token.empty()) { if (info.token.empty()) { // already unregistered - return promise.set_value(make_tl_object()); + return promise.set_value(td_api::make_object()); } info.state = TokenInfo::State::Unregister; @@ -244,24 +249,39 @@ void DeviceTokenManager::register_device(tl_object_ptr devi if (encrypt != info.encrypt) { if (encrypt) { constexpr size_t ENCRYPTION_KEY_LENGTH = 256; + constexpr int64 MIN_ENCRYPTION_KEY_ID = static_cast(10000000000000ll); info.encryption_key.resize(ENCRYPTION_KEY_LENGTH); - Random::secure_bytes(info.encryption_key); + while (true) { + Random::secure_bytes(info.encryption_key); + uint8 sha1_buf[20]; + sha1(info.encryption_key, sha1_buf); + info.encryption_key_id = as(sha1_buf + 12); + if (info.encryption_key_id <= -MIN_ENCRYPTION_KEY_ID || info.encryption_key_id >= MIN_ENCRYPTION_KEY_ID) { + // ensure that encryption key ID never collide with anything + break; + } + } } else { info.encryption_key.clear(); + info.encryption_key_id = 0; } info.encrypt = encrypt; } - info.promise.set_value(make_tl_object()); + info.promise.set_value(td_api::make_object()); info.promise = std::move(promise); save_info(token_type); } -vector DeviceTokenManager::get_encryption_keys() const { - vector result; +vector> DeviceTokenManager::get_encryption_keys() const { + vector> result; for (int32 token_type = 1; token_type < TokenType::SIZE; token_type++) { auto &info = tokens_[token_type]; - if (!info.token.empty() && info.encrypt && info.state != TokenInfo::State::Unregister) { - result.push_back(info.encryption_key); + if (!info.token.empty() && info.state != TokenInfo::State::Unregister) { + if (info.encrypt) { + result.emplace_back(info.encryption_key_id, info.encryption_key); + } else { + result.emplace_back(G()->get_my_id(), Slice()); + } } } return result; @@ -281,7 +301,12 @@ void DeviceTokenManager::start_up() { auto &token = tokens_[token_type]; char c = serialized[0]; if (c == '*') { - unserialize(token, serialized.substr(1)).ensure(); + auto status = unserialize(token, serialized.substr(1)); + if (status.is_error()) { + token = TokenInfo(); + LOG(ERROR) << "Invalid serialized TokenInfo: " << format::escaped(serialized) << ' ' << status; + continue; + } } else { // legacy if (c == '+') { @@ -362,7 +387,15 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { info.net_query_id = 0; if (r_flag.is_ok() && r_flag.ok()) { if (info.promise) { - info.promise.set_value(make_tl_object()); + int64 push_token_id = 0; + if (info.state == TokenInfo::State::Register) { + if (info.encrypt) { + push_token_id = info.encryption_key_id; + } else { + push_token_id = G()->get_my_id(); + } + } + info.promise.set_value(td_api::make_object(push_token_id)); } if (info.state == TokenInfo::State::Unregister) { info.token.clear(); @@ -373,7 +406,7 @@ void DeviceTokenManager::on_result(NetQueryPtr net_query) { if (r_flag.is_error()) { info.promise.set_error(r_flag.error().clone()); } else { - info.promise.set_error(Status::Error(5, "Got false as result")); + info.promise.set_error(Status::Error(5, "Got false as result of server request")); } } if (info.state == TokenInfo::State::Register) { diff --git a/td/telegram/DeviceTokenManager.h b/td/telegram/DeviceTokenManager.h index 441b5a058..f3d9a0311 100644 --- a/td/telegram/DeviceTokenManager.h +++ b/td/telegram/DeviceTokenManager.h @@ -18,6 +18,7 @@ #include "td/utils/StringBuilder.h" #include +#include namespace td { @@ -26,9 +27,9 @@ class DeviceTokenManager : public NetQueryCallback { explicit DeviceTokenManager(ActorShared<> parent) : parent_(std::move(parent)) { } void register_device(tl_object_ptr device_token_ptr, vector other_user_ids, - Promise> promise); + Promise> promise); - vector get_encryption_keys() const; + vector> get_encryption_keys() const; private: static constexpr size_t MAX_OTHER_USER_IDS = 100; @@ -58,7 +59,8 @@ class DeviceTokenManager : public NetQueryCallback { bool is_app_sandbox = false; bool encrypt = false; string encryption_key; - Promise> promise; + int64 encryption_key_id = 0; + Promise> promise; template void store(StorerT &storer) const; diff --git a/td/telegram/NotificationManager.cpp b/td/telegram/NotificationManager.cpp index b9c797fa6..6deca9d8c 100644 --- a/td/telegram/NotificationManager.cpp +++ b/td/telegram/NotificationManager.cpp @@ -15,7 +15,9 @@ #include "td/telegram/Td.h" #include "td/telegram/TdDb.h" +#include "td/utils/as.h" #include "td/utils/format.h" +#include "td/utils/JsonBuilder.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/Slice.h" @@ -1948,23 +1950,74 @@ void NotificationManager::on_notification_default_delay_changed() { VLOG(notifications) << "Set notification_default_delay_ms to " << notification_default_delay_ms_; } -void NotificationManager::process_push_notification(const string &payload, Promise &&promise) { +void NotificationManager::process_push_notification(string payload, Promise &&promise) { if (G()->close_flag()) { + promise.set_value(Unit()); return; } + auto r_receiver_id = get_push_receiver_id(payload); + if (r_receiver_id.is_error()) { + VLOG(notifications) << "Failed to get push notification receiver from \"" << format::escaped(payload) << '"'; + promise.set_error(r_receiver_id.move_as_error()); + return; + } + + auto receiver_id = r_receiver_id.move_as_ok(); + VLOG(notifications) << "Process push notification \"" << format::escaped(payload) + << "\" with receiver_id = " << receiver_id; + auto encryption_keys = td_->device_token_manager_->get_actor_unsafe()->get_encryption_keys(); for (auto &key : encryption_keys) { - VLOG(notifications) << "Have key \"" << format::escaped(key) << '"'; + VLOG(notifications) << "Have key " << key.first << ": \"" << format::escaped(key.second) << '"'; + if (key.first == receiver_id) { + if (key.second.empty()) { + VLOG(notifications) << "Process unencrypted push notification"; + } else { + VLOG(notifications) << "Process encrypted push notification"; + } + promise.set_value(Unit()); + return; + } } - if (encryption_keys.empty()) { - VLOG(notifications) << "Process push notification \"" << payload << '"'; - } else { - VLOG(notifications) << "Process encrypted push notification \"" << format::escaped(payload) << '"'; + if (receiver_id != 0) { // TODO move this check up + promise.set_value(Unit()); + return; } + + VLOG(notifications) << "Failed to process push notification"; promise.set_value(Unit()); } +Result NotificationManager::get_push_receiver_id(string payload) { + VLOG(notifications) << "Get push notification receiver ID of \"" << format::escaped(payload) << '"'; + auto r_json_value = json_decode(payload); + if (r_json_value.is_error()) { + return Status::Error(400, "Failed to parse payload as JSON object"); + } + + auto json_value = r_json_value.move_as_ok(); + if (json_value.type() != JsonValue::Type::Object) { + return Status::Error(400, "Expected JSON object"); + } + + for (auto &field_value : json_value.get_object()) { + if (field_value.first == "p") { + auto encrypted_payload = std::move(field_value.second); + if (encrypted_payload.type() != JsonValue::Type::String) { + return Status::Error(400, "Expected encrypted payload as a String"); + } + Slice data = encrypted_payload.get_string(); + if (data.size() < 8) { + return Status::Error(400, "Encrypted payload is too small"); + } + return as(data.data()); + } + } + + return Status::Error(200, "Unsupported push notification"); +} + void NotificationManager::before_get_difference() { if (is_disabled()) { return; diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index 0fd84e4f4..2f243262a 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -87,7 +87,9 @@ class NotificationManager : public Actor { void on_notification_default_delay_changed(); - void process_push_notification(const string &payload, Promise &&promise); + void process_push_notification(string payload, Promise &&promise); + + static Result get_push_receiver_id(string payload); void before_get_difference(); diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index bfb520211..e7a3fbf10 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -407,8 +407,7 @@ void PasswordManager::resend_email_address_verification_code( send_email_address_verification_code(last_verified_email_address_, std::move(promise)); } -void PasswordManager::check_email_address_verification_code(string code, - Promise> promise) { +void PasswordManager::check_email_address_verification_code(string code, Promise promise) { if (last_verified_email_address_.empty()) { return promise.set_error(Status::Error(400, "No email address verification was sent")); } @@ -420,7 +419,7 @@ void PasswordManager::check_email_address_verification_code(string code, if (r_result.is_error()) { return promise.set_error(r_result.move_as_error()); } - return promise.set_value(td_api::make_object()); + return promise.set_value(Unit()); })); } diff --git a/td/telegram/PasswordManager.h b/td/telegram/PasswordManager.h index b8e0f9af7..8615cdd61 100644 --- a/td/telegram/PasswordManager.h +++ b/td/telegram/PasswordManager.h @@ -71,7 +71,7 @@ class PasswordManager : public NetQueryCallback { string email, Promise> promise); void resend_email_address_verification_code( Promise> promise); - void check_email_address_verification_code(string code, Promise> promise); + void check_email_address_verification_code(string code, Promise promise); void request_password_recovery(Promise> promise); void recover_password(string code, Promise promise); diff --git a/td/telegram/PrivacyManager.cpp b/td/telegram/PrivacyManager.cpp index dc95810d7..73974a705 100644 --- a/td/telegram/PrivacyManager.cpp +++ b/td/telegram/PrivacyManager.cpp @@ -279,8 +279,7 @@ void PrivacyManager::get_privacy(tl_object_ptr key, } void PrivacyManager::set_privacy(tl_object_ptr key, - tl_object_ptr rules, - Promise> promise) { + tl_object_ptr rules, Promise promise) { auto r_user_privacy_setting = UserPrivacySetting::from_td_api(std::move(key)); if (r_user_privacy_setting.is_error()) { return promise.set_error(r_user_privacy_setting.move_as_error()); @@ -305,13 +304,13 @@ void PrivacyManager::set_privacy(tl_object_ptr key, send_with_promise(std::move(net_query), PromiseCreator::lambda([this, user_privacy_setting, promise = std::move(promise)](Result x_net_query) mutable { - promise.set_result([&]() -> Result> { + promise.set_result([&]() -> Result { TRY_RESULT(net_query, std::move(x_net_query)); TRY_RESULT(rules, fetch_result(std::move(net_query))); TRY_RESULT(privacy_rules, UserPrivacySettingRules::from_telegram_api(std::move(rules))); get_info(user_privacy_setting).has_set_query = false; do_update_privacy(user_privacy_setting, std::move(privacy_rules), true); - return make_tl_object(); + return Unit(); }()); })); } diff --git a/td/telegram/PrivacyManager.h b/td/telegram/PrivacyManager.h index 3f0be231d..050e884b0 100644 --- a/td/telegram/PrivacyManager.h +++ b/td/telegram/PrivacyManager.h @@ -31,7 +31,7 @@ class PrivacyManager : public NetQueryCallback { Promise> promise); void set_privacy(tl_object_ptr key, tl_object_ptr rules, - Promise> promise); + Promise promise); void update_privacy(tl_object_ptr update); diff --git a/td/telegram/SequenceDispatcher.cpp b/td/telegram/SequenceDispatcher.cpp index bba39d701..f9611dbdf 100644 --- a/td/telegram/SequenceDispatcher.cpp +++ b/td/telegram/SequenceDispatcher.cpp @@ -19,6 +19,7 @@ #include namespace td { + /*** Sequence Dispatcher ***/ // Sends queries with invokeAfter. // @@ -266,4 +267,5 @@ void MultiSequenceDispatcher::ready_to_close() { dispatchers_.erase(it); } } + } // namespace td diff --git a/td/telegram/SequenceDispatcher.h b/td/telegram/SequenceDispatcher.h index 820332cac..7f0989a2a 100644 --- a/td/telegram/SequenceDispatcher.h +++ b/td/telegram/SequenceDispatcher.h @@ -17,6 +17,7 @@ #include namespace td { + class SequenceDispatcher : public NetQueryCallback { public: class Parent : public Actor { @@ -85,4 +86,5 @@ class MultiSequenceDispatcher : public SequenceDispatcher::Parent { void on_result() override; void ready_to_close() override; }; + } // namespace td diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index bb9757072..e2f204e9a 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3182,6 +3182,7 @@ bool Td::is_synchronous_request(int32 id) { case td_api::getFileExtension::ID: case td_api::cleanFileName::ID: case td_api::getLanguagePackString::ID: + case td_api::getPushReceiverId::ID: case td_api::getJsonValue::ID: case td_api::getJsonString::ID: case td_api::setLogStream::ID: @@ -4721,7 +4722,7 @@ void Td::on_request(uint64 id, td_api::getUserPrivacySettingRules &request) { void Td::on_request(uint64 id, td_api::setUserPrivacySettingRules &request) { CHECK_IS_USER(); - CREATE_REQUEST_PROMISE(); + CREATE_OK_REQUEST_PROMISE(); send_closure(privacy_manager_, &PrivacyManager::set_privacy, std::move(request.setting_), std::move(request.rules_), std::move(promise)); } @@ -6638,7 +6639,7 @@ void Td::on_request(uint64 id, const td_api::resendEmailAddressVerificationCode void Td::on_request(uint64 id, td_api::checkEmailAddressVerificationCode &request) { CHECK_IS_USER(); CLEAN_INPUT_STRING(request.code_); - CREATE_REQUEST_PROMISE(); + CREATE_OK_REQUEST_PROMISE(); send_closure(password_manager_, &PasswordManager::check_email_address_verification_code, request.code_, std::move(promise)); } @@ -6898,6 +6899,10 @@ void Td::on_request(uint64 id, const td_api::getLanguagePackString &request) { UNREACHABLE(); } +void Td::on_request(uint64 id, const td_api::getPushReceiverId &request) { + UNREACHABLE(); +} + void Td::on_request(uint64 id, const td_api::getJsonValue &request) { UNREACHABLE(); } @@ -6988,6 +6993,15 @@ td_api::object_ptr Td::do_static_request(const td_api::getLangua request.language_pack_database_path_, request.localization_target_, request.language_pack_id_, request.key_); } +td_api::object_ptr Td::do_static_request(const td_api::getPushReceiverId &request) { + // don't check push payload UTF-8 correctness + auto r_push_receiver_id = NotificationManager::get_push_receiver_id(std::move(request.payload_)); + if (r_push_receiver_id.is_error()) { + return make_error(r_push_receiver_id.error().code(), r_push_receiver_id.error().message()); + } + return td_api::make_object(r_push_receiver_id.ok()); +} + td_api::object_ptr Td::do_static_request(td_api::getJsonValue &request) { if (!check_utf8(request.json_)) { return make_error(400, "JSON has invalid encoding"); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index bce332443..b000009b3 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -928,6 +928,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::getLanguagePackString &request); + void on_request(uint64 id, const td_api::getPushReceiverId &request); + void on_request(uint64 id, const td_api::getJsonValue &request); void on_request(uint64 id, const td_api::getJsonString &request); @@ -970,6 +972,7 @@ class Td final : public NetQueryCallback { static td_api::object_ptr do_static_request(const td_api::getFileExtension &request); static td_api::object_ptr do_static_request(const td_api::cleanFileName &request); static td_api::object_ptr do_static_request(const td_api::getLanguagePackString &request); + static td_api::object_ptr do_static_request(const td_api::getPushReceiverId &request); static td_api::object_ptr do_static_request(td_api::getJsonValue &request); static td_api::object_ptr do_static_request(const td_api::getJsonString &request); static td_api::object_ptr do_static_request(td_api::setLogStream &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 15d4d315b..5b1d95642 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1361,6 +1361,8 @@ class CliClient final : public Actor { send_request(make_tl_object(dc_id, ip_port)); } else if (op == "ppn") { send_request(make_tl_object(args)); + } else if (op == "gpri") { + send_request(make_tl_object(args)); } else if (op == "rda") { send_request(make_tl_object(make_tl_object(args, true), as_user_ids("")));