Add pushReceiverId.

GitOrigin-RevId: 6beb6930eb456d94af50ed9455ee15df8ed36e20
This commit is contained in:
levlam 2018-12-29 01:48:32 +03:00
parent dd5b8a87c2
commit 04fbc9ff48
15 changed files with 158 additions and 41 deletions

View File

@ -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<photoSize> 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<int32> = 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<int32> = 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

Binary file not shown.

View File

@ -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<td_api::DeviceToken> device_token_ptr,
vector<int32> other_user_ids, Promise<tl_object_ptr<td_api::ok>> promise) {
vector<int32> other_user_ids,
Promise<td_api::object_ptr<td_api::pushReceiverId>> promise) {
CHECK(device_token_ptr != nullptr);
TokenType token_type;
string token;
@ -231,7 +236,7 @@ void DeviceTokenManager::register_device(tl_object_ptr<td_api::DeviceToken> devi
if (token.empty()) {
if (info.token.empty()) {
// already unregistered
return promise.set_value(make_tl_object<td_api::ok>());
return promise.set_value(td_api::make_object<td_api::pushReceiverId>());
}
info.state = TokenInfo::State::Unregister;
@ -244,24 +249,39 @@ void DeviceTokenManager::register_device(tl_object_ptr<td_api::DeviceToken> devi
if (encrypt != info.encrypt) {
if (encrypt) {
constexpr size_t ENCRYPTION_KEY_LENGTH = 256;
constexpr int64 MIN_ENCRYPTION_KEY_ID = static_cast<int64>(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<int64>(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<td_api::ok>());
info.promise.set_value(td_api::make_object<td_api::pushReceiverId>());
info.promise = std::move(promise);
save_info(token_type);
}
vector<Slice> DeviceTokenManager::get_encryption_keys() const {
vector<Slice> result;
vector<std::pair<int64, Slice>> DeviceTokenManager::get_encryption_keys() const {
vector<std::pair<int64, Slice>> 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<td_api::ok>());
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<td_api::pushReceiverId>(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) {

View File

@ -18,6 +18,7 @@
#include "td/utils/StringBuilder.h"
#include <array>
#include <utility>
namespace td {
@ -26,9 +27,9 @@ class DeviceTokenManager : public NetQueryCallback {
explicit DeviceTokenManager(ActorShared<> parent) : parent_(std::move(parent)) {
}
void register_device(tl_object_ptr<td_api::DeviceToken> device_token_ptr, vector<int32> other_user_ids,
Promise<tl_object_ptr<td_api::ok>> promise);
Promise<td_api::object_ptr<td_api::pushReceiverId>> promise);
vector<Slice> get_encryption_keys() const;
vector<std::pair<int64, Slice>> 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<tl_object_ptr<td_api::ok>> promise;
int64 encryption_key_id = 0;
Promise<td_api::object_ptr<td_api::pushReceiverId>> promise;
template <class StorerT>
void store(StorerT &storer) const;

View File

@ -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<Unit> &&promise) {
void NotificationManager::process_push_notification(string payload, Promise<Unit> &&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<int64> 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<int64>(data.data());
}
}
return Status::Error(200, "Unsupported push notification");
}
void NotificationManager::before_get_difference() {
if (is_disabled()) {
return;

View File

@ -87,7 +87,9 @@ class NotificationManager : public Actor {
void on_notification_default_delay_changed();
void process_push_notification(const string &payload, Promise<Unit> &&promise);
void process_push_notification(string payload, Promise<Unit> &&promise);
static Result<int64> get_push_receiver_id(string payload);
void before_get_difference();

View File

@ -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<td_api::object_ptr<td_api::ok>> promise) {
void PasswordManager::check_email_address_verification_code(string code, Promise<Unit> 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<td_api::ok>());
return promise.set_value(Unit());
}));
}

View File

@ -71,7 +71,7 @@ class PasswordManager : public NetQueryCallback {
string email, Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void resend_email_address_verification_code(
Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void check_email_address_verification_code(string code, Promise<td_api::object_ptr<td_api::ok>> promise);
void check_email_address_verification_code(string code, Promise<Unit> promise);
void request_password_recovery(Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise);
void recover_password(string code, Promise<State> promise);

View File

@ -279,8 +279,7 @@ void PrivacyManager::get_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
}
void PrivacyManager::set_privacy(tl_object_ptr<td_api::UserPrivacySetting> key,
tl_object_ptr<td_api::userPrivacySettingRules> rules,
Promise<tl_object_ptr<td_api::ok>> promise) {
tl_object_ptr<td_api::userPrivacySettingRules> rules, Promise<Unit> 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<td_api::UserPrivacySetting> key,
send_with_promise(std::move(net_query),
PromiseCreator::lambda([this, user_privacy_setting,
promise = std::move(promise)](Result<NetQueryPtr> x_net_query) mutable {
promise.set_result([&]() -> Result<tl_object_ptr<td_api::ok>> {
promise.set_result([&]() -> Result<Unit> {
TRY_RESULT(net_query, std::move(x_net_query));
TRY_RESULT(rules, fetch_result<telegram_api::account_setPrivacy>(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<td_api::ok>();
return Unit();
}());
}));
}

View File

@ -31,7 +31,7 @@ class PrivacyManager : public NetQueryCallback {
Promise<tl_object_ptr<td_api::userPrivacySettingRules>> promise);
void set_privacy(tl_object_ptr<td_api::UserPrivacySetting> key, tl_object_ptr<td_api::userPrivacySettingRules> rules,
Promise<tl_object_ptr<td_api::ok>> promise);
Promise<Unit> promise);
void update_privacy(tl_object_ptr<telegram_api::updatePrivacy> update);

View File

@ -19,6 +19,7 @@
#include <limits>
namespace td {
/*** Sequence Dispatcher ***/
// Sends queries with invokeAfter.
//
@ -266,4 +267,5 @@ void MultiSequenceDispatcher::ready_to_close() {
dispatchers_.erase(it);
}
}
} // namespace td

View File

@ -17,6 +17,7 @@
#include <unordered_map>
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

View File

@ -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_api::Object> 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_api::Object> 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<td_api::pushReceiverId>(r_push_receiver_id.ok());
}
td_api::object_ptr<td_api::Object> Td::do_static_request(td_api::getJsonValue &request) {
if (!check_utf8(request.json_)) {
return make_error(400, "JSON has invalid encoding");

View File

@ -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<td_api::Object> do_static_request(const td_api::getFileExtension &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::cleanFileName &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getLanguagePackString &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getPushReceiverId &request);
static td_api::object_ptr<td_api::Object> do_static_request(td_api::getJsonValue &request);
static td_api::object_ptr<td_api::Object> do_static_request(const td_api::getJsonString &request);
static td_api::object_ptr<td_api::Object> do_static_request(td_api::setLogStream &request);

View File

@ -1361,6 +1361,8 @@ class CliClient final : public Actor {
send_request(make_tl_object<td_api::processDcUpdate>(dc_id, ip_port));
} else if (op == "ppn") {
send_request(make_tl_object<td_api::processPushNotification>(args));
} else if (op == "gpri") {
send_request(make_tl_object<td_api::getPushReceiverId>(args));
} else if (op == "rda") {
send_request(make_tl_object<td_api::registerDevice>(make_tl_object<td_api::deviceTokenApplePush>(args, true),
as_user_ids("")));