1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/BackgroundManager.h"
8
9 #include "td/telegram/AuthManager.h"
10 #include "td/telegram/BackgroundType.hpp"
11 #include "td/telegram/ConfigShared.h"
12 #include "td/telegram/DialogId.h"
13 #include "td/telegram/Document.h"
14 #include "td/telegram/DocumentsManager.h"
15 #include "td/telegram/DocumentsManager.hpp"
16 #include "td/telegram/FileReferenceManager.h"
17 #include "td/telegram/files/FileManager.h"
18 #include "td/telegram/files/FileType.h"
19 #include "td/telegram/Global.h"
20 #include "td/telegram/Photo.h"
21 #include "td/telegram/Td.h"
22 #include "td/telegram/TdDb.h"
23 #include "td/telegram/TdParameters.h"
24
25 #include "td/db/SqliteKeyValueAsync.h"
26
27 #include "td/utils/algorithm.h"
28 #include "td/utils/base64.h"
29 #include "td/utils/buffer.h"
30 #include "td/utils/common.h"
31 #include "td/utils/format.h"
32 #include "td/utils/logging.h"
33 #include "td/utils/misc.h"
34 #include "td/utils/Slice.h"
35 #include "td/utils/SliceBuilder.h"
36 #include "td/utils/tl_helpers.h"
37
38 namespace td {
39
40 class GetBackgroundQuery final : public Td::ResultHandler {
41 Promise<Unit> promise_;
42 BackgroundId background_id_;
43 string background_name_;
44
45 public:
GetBackgroundQuery(Promise<Unit> && promise)46 explicit GetBackgroundQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
47 }
48
send(BackgroundId background_id,const string & background_name,telegram_api::object_ptr<telegram_api::InputWallPaper> && input_wallpaper)49 void send(BackgroundId background_id, const string &background_name,
50 telegram_api::object_ptr<telegram_api::InputWallPaper> &&input_wallpaper) {
51 background_id_ = background_id;
52 background_name_ = background_name;
53 send_query(G()->net_query_creator().create(telegram_api::account_getWallPaper(std::move(input_wallpaper))));
54 }
55
on_result(BufferSlice packet)56 void on_result(BufferSlice packet) final {
57 auto result_ptr = fetch_result<telegram_api::account_getWallPaper>(packet);
58 if (result_ptr.is_error()) {
59 return on_error(result_ptr.move_as_error());
60 }
61
62 td_->background_manager_->on_get_background(background_id_, background_name_, result_ptr.move_as_ok(), true);
63
64 promise_.set_value(Unit());
65 }
66
on_error(Status status)67 void on_error(Status status) final {
68 LOG(INFO) << "Receive error for GetBackgroundQuery for " << background_id_ << "/" << background_name_ << ": "
69 << status;
70 promise_.set_error(std::move(status));
71 }
72 };
73
74 class GetBackgroundsQuery final : public Td::ResultHandler {
75 Promise<telegram_api::object_ptr<telegram_api::account_WallPapers>> promise_;
76
77 public:
GetBackgroundsQuery(Promise<telegram_api::object_ptr<telegram_api::account_WallPapers>> && promise)78 explicit GetBackgroundsQuery(Promise<telegram_api::object_ptr<telegram_api::account_WallPapers>> &&promise)
79 : promise_(std::move(promise)) {
80 }
81
send()82 void send() {
83 send_query(G()->net_query_creator().create(telegram_api::account_getWallPapers(0)));
84 }
85
on_result(BufferSlice packet)86 void on_result(BufferSlice packet) final {
87 auto result_ptr = fetch_result<telegram_api::account_getWallPapers>(packet);
88 if (result_ptr.is_error()) {
89 return on_error(result_ptr.move_as_error());
90 }
91
92 promise_.set_value(result_ptr.move_as_ok());
93 }
94
on_error(Status status)95 void on_error(Status status) final {
96 promise_.set_error(std::move(status));
97 }
98 };
99
100 class InstallBackgroundQuery final : public Td::ResultHandler {
101 Promise<Unit> promise_;
102
103 public:
InstallBackgroundQuery(Promise<Unit> && promise)104 explicit InstallBackgroundQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
105 }
106
send(telegram_api::object_ptr<telegram_api::InputWallPaper> input_wallpaper,const BackgroundType & type)107 void send(telegram_api::object_ptr<telegram_api::InputWallPaper> input_wallpaper, const BackgroundType &type) {
108 send_query(G()->net_query_creator().create(
109 telegram_api::account_installWallPaper(std::move(input_wallpaper), type.get_input_wallpaper_settings())));
110 }
111
on_result(BufferSlice packet)112 void on_result(BufferSlice packet) final {
113 auto result_ptr = fetch_result<telegram_api::account_installWallPaper>(packet);
114 if (result_ptr.is_error()) {
115 return on_error(result_ptr.move_as_error());
116 }
117
118 LOG_IF(INFO, !result_ptr.ok()) << "Receive false from account.installWallPaper";
119 promise_.set_value(Unit());
120 }
121
on_error(Status status)122 void on_error(Status status) final {
123 promise_.set_error(std::move(status));
124 }
125 };
126
127 class UploadBackgroundQuery final : public Td::ResultHandler {
128 Promise<Unit> promise_;
129 FileId file_id_;
130 BackgroundType type_;
131 bool for_dark_theme_;
132
133 public:
UploadBackgroundQuery(Promise<Unit> && promise)134 explicit UploadBackgroundQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
135 }
136
send(FileId file_id,tl_object_ptr<telegram_api::InputFile> && input_file,const BackgroundType & type,bool for_dark_theme)137 void send(FileId file_id, tl_object_ptr<telegram_api::InputFile> &&input_file, const BackgroundType &type,
138 bool for_dark_theme) {
139 CHECK(input_file != nullptr);
140 file_id_ = file_id;
141 type_ = type;
142 for_dark_theme_ = for_dark_theme;
143 send_query(G()->net_query_creator().create(telegram_api::account_uploadWallPaper(
144 std::move(input_file), type_.get_mime_type(), type_.get_input_wallpaper_settings())));
145 }
146
on_result(BufferSlice packet)147 void on_result(BufferSlice packet) final {
148 auto result_ptr = fetch_result<telegram_api::account_uploadWallPaper>(packet);
149 if (result_ptr.is_error()) {
150 return on_error(result_ptr.move_as_error());
151 }
152
153 td_->background_manager_->on_uploaded_background_file(file_id_, type_, for_dark_theme_, result_ptr.move_as_ok(),
154 std::move(promise_));
155 }
156
on_error(Status status)157 void on_error(Status status) final {
158 CHECK(status.is_error());
159 CHECK(file_id_.is_valid());
160 if (begins_with(status.message(), "FILE_PART_") && ends_with(status.message(), "_MISSING")) {
161 // TODO td_->background_manager_->on_upload_background_file_part_missing(file_id_, to_integer<int32>(status.message().substr(10)));
162 // return;
163 } else {
164 if (status.code() != 429 && status.code() < 500 && !G()->close_flag()) {
165 td_->file_manager_->delete_partial_remote_location(file_id_);
166 }
167 }
168 td_->file_manager_->cancel_upload(file_id_);
169 promise_.set_error(std::move(status));
170 }
171 };
172
173 class UnsaveBackgroundQuery final : public Td::ResultHandler {
174 Promise<Unit> promise_;
175
176 public:
UnsaveBackgroundQuery(Promise<Unit> && promise)177 explicit UnsaveBackgroundQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
178 }
179
send(telegram_api::object_ptr<telegram_api::InputWallPaper> input_wallpaper)180 void send(telegram_api::object_ptr<telegram_api::InputWallPaper> input_wallpaper) {
181 send_query(G()->net_query_creator().create(telegram_api::account_saveWallPaper(
182 std::move(input_wallpaper), true, telegram_api::make_object<telegram_api::wallPaperSettings>())));
183 }
184
on_result(BufferSlice packet)185 void on_result(BufferSlice packet) final {
186 auto result_ptr = fetch_result<telegram_api::account_saveWallPaper>(packet);
187 if (result_ptr.is_error()) {
188 return on_error(result_ptr.move_as_error());
189 }
190
191 bool result = result_ptr.move_as_ok();
192 LOG(INFO) << "Receive result for save background: " << result;
193 promise_.set_value(Unit());
194 }
195
on_error(Status status)196 void on_error(Status status) final {
197 if (!G()->is_expected_error(status)) {
198 LOG(ERROR) << "Receive error for save background: " << status;
199 }
200 promise_.set_error(std::move(status));
201 }
202 };
203
204 class ResetBackgroundsQuery final : public Td::ResultHandler {
205 Promise<Unit> promise_;
206
207 public:
ResetBackgroundsQuery(Promise<Unit> && promise)208 explicit ResetBackgroundsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
209 }
210
send()211 void send() {
212 send_query(G()->net_query_creator().create(telegram_api::account_resetWallPapers()));
213 }
214
on_result(BufferSlice packet)215 void on_result(BufferSlice packet) final {
216 auto result_ptr = fetch_result<telegram_api::account_resetWallPapers>(packet);
217 if (result_ptr.is_error()) {
218 return on_error(result_ptr.move_as_error());
219 }
220
221 bool result = result_ptr.move_as_ok();
222 LOG(INFO) << "Receive result for reset backgrounds: " << result;
223 promise_.set_value(Unit());
224 }
225
on_error(Status status)226 void on_error(Status status) final {
227 if (!G()->is_expected_error(status)) {
228 LOG(ERROR) << "Receive error for reset backgrounds: " << status;
229 }
230 promise_.set_error(std::move(status));
231 }
232 };
233
234 class BackgroundManager::UploadBackgroundFileCallback final : public FileManager::UploadCallback {
235 public:
on_upload_ok(FileId file_id,tl_object_ptr<telegram_api::InputFile> input_file)236 void on_upload_ok(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file) final {
237 send_closure_later(G()->background_manager(), &BackgroundManager::on_upload_background_file, file_id,
238 std::move(input_file));
239 }
240
on_upload_encrypted_ok(FileId file_id,tl_object_ptr<telegram_api::InputEncryptedFile> input_file)241 void on_upload_encrypted_ok(FileId file_id, tl_object_ptr<telegram_api::InputEncryptedFile> input_file) final {
242 UNREACHABLE();
243 }
244
on_upload_secure_ok(FileId file_id,tl_object_ptr<telegram_api::InputSecureFile> input_file)245 void on_upload_secure_ok(FileId file_id, tl_object_ptr<telegram_api::InputSecureFile> input_file) final {
246 UNREACHABLE();
247 }
248
on_upload_error(FileId file_id,Status error)249 void on_upload_error(FileId file_id, Status error) final {
250 send_closure_later(G()->background_manager(), &BackgroundManager::on_upload_background_file_error, file_id,
251 std::move(error));
252 }
253 };
254
BackgroundManager(Td * td,ActorShared<> parent)255 BackgroundManager::BackgroundManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
256 upload_background_file_callback_ = std::make_shared<UploadBackgroundFileCallback>();
257 }
258
259 template <class StorerT>
store(StorerT & storer) const260 void BackgroundManager::Background::store(StorerT &storer) const {
261 bool has_file_id = file_id.is_valid();
262 BEGIN_STORE_FLAGS();
263 STORE_FLAG(is_creator);
264 STORE_FLAG(is_default);
265 STORE_FLAG(is_dark);
266 STORE_FLAG(has_file_id);
267 STORE_FLAG(has_new_local_id);
268 END_STORE_FLAGS();
269 td::store(id, storer);
270 td::store(access_hash, storer);
271 td::store(name, storer);
272 if (has_file_id) {
273 storer.context()->td().get_actor_unsafe()->documents_manager_->store_document(file_id, storer);
274 }
275 td::store(type, storer);
276 }
277
278 template <class ParserT>
parse(ParserT & parser)279 void BackgroundManager::Background::parse(ParserT &parser) {
280 bool has_file_id;
281 BEGIN_PARSE_FLAGS();
282 PARSE_FLAG(is_creator);
283 PARSE_FLAG(is_default);
284 PARSE_FLAG(is_dark);
285 PARSE_FLAG(has_file_id);
286 PARSE_FLAG(has_new_local_id);
287 END_PARSE_FLAGS();
288 td::parse(id, parser);
289 td::parse(access_hash, parser);
290 td::parse(name, parser);
291 if (has_file_id) {
292 file_id = parser.context()->td().get_actor_unsafe()->documents_manager_->parse_document(parser);
293 } else {
294 file_id = FileId();
295 }
296 td::parse(type, parser);
297 }
298
299 class BackgroundManager::BackgroundLogEvent {
300 public:
301 Background background_;
302 BackgroundType set_type_;
303
304 template <class StorerT>
store(StorerT & storer) const305 void store(StorerT &storer) const {
306 td::store(background_, storer);
307 td::store(set_type_, storer);
308 }
309
310 template <class ParserT>
parse(ParserT & parser)311 void parse(ParserT &parser) {
312 td::parse(background_, parser);
313 td::parse(set_type_, parser);
314 }
315 };
316
317 class BackgroundManager::BackgroundsLogEvent {
318 public:
319 vector<Background> backgrounds_;
320
321 template <class StorerT>
store(StorerT & storer) const322 void store(StorerT &storer) const {
323 td::store(backgrounds_, storer);
324 }
325
326 template <class ParserT>
parse(ParserT & parser)327 void parse(ParserT &parser) {
328 td::parse(backgrounds_, parser);
329 }
330 };
331
start_up()332 void BackgroundManager::start_up() {
333 max_local_background_id_ = BackgroundId(to_integer<int64>(G()->td_db()->get_binlog_pmc()->get("max_bg_id")));
334
335 // first parse all log events and fix max_local_background_id_ value
336 bool has_selected_background[2] = {false, false};
337 BackgroundLogEvent selected_background_log_event[2];
338 for (int i = 0; i < 2; i++) {
339 bool for_dark_theme = i != 0;
340 auto log_event_string = G()->td_db()->get_binlog_pmc()->get(get_background_database_key(for_dark_theme));
341 if (!log_event_string.empty()) {
342 has_selected_background[i] = true;
343 log_event_parse(selected_background_log_event[i], log_event_string).ensure();
344 const Background &background = selected_background_log_event[i].background_;
345 if (background.has_new_local_id && background.id.is_local() && !background.type.has_file() &&
346 background.id.get() > max_local_background_id_.get()) {
347 set_max_local_background_id(background.id);
348 }
349 }
350 }
351
352 for (int i = 0; i < 2; i++) {
353 bool for_dark_theme = i != 0;
354 auto log_event_string = G()->td_db()->get_binlog_pmc()->get(get_local_backgrounds_database_key(for_dark_theme));
355 if (!log_event_string.empty()) {
356 BackgroundsLogEvent log_event;
357 log_event_parse(log_event, log_event_string).ensure();
358 for (const auto &background : log_event.backgrounds_) {
359 CHECK(background.has_new_local_id);
360 CHECK(background.id.is_valid());
361 CHECK(background.id.is_local());
362 CHECK(!background.type.has_file());
363 CHECK(!background.file_id.is_valid());
364 if (background.id.get() > max_local_background_id_.get()) {
365 set_max_local_background_id(background.id);
366 }
367 add_background(background, true);
368 local_background_ids_[for_dark_theme].push_back(background.id);
369 }
370 }
371 }
372
373 // then add selected backgrounds fixing their ID
374 for (int i = 0; i < 2; i++) {
375 bool for_dark_theme = i != 0;
376 if (has_selected_background[i]) {
377 Background &background = selected_background_log_event[i].background_;
378
379 bool need_resave = false;
380 if (!background.has_new_local_id && !background.type.has_file()) {
381 background.has_new_local_id = true;
382 background.id = get_next_local_background_id();
383 need_resave = true;
384 }
385
386 CHECK(background.id.is_valid());
387 if (background.file_id.is_valid() != background.type.has_file()) {
388 LOG(ERROR) << "Failed to load " << background.id << " of " << background.type;
389 need_resave = true;
390 } else {
391 set_background_id_[for_dark_theme] = background.id;
392 set_background_type_[for_dark_theme] = selected_background_log_event[i].set_type_;
393
394 add_background(background, false);
395 }
396
397 if (need_resave) {
398 save_background_id(for_dark_theme);
399 }
400 }
401
402 send_update_selected_background(for_dark_theme);
403 }
404 }
405
tear_down()406 void BackgroundManager::tear_down() {
407 parent_.reset();
408 }
409
store_background(BackgroundId background_id,LogEventStorerCalcLength & storer)410 void BackgroundManager::store_background(BackgroundId background_id, LogEventStorerCalcLength &storer) {
411 const auto *background = get_background(background_id);
412 CHECK(background != nullptr);
413 store(*background, storer);
414 }
415
store_background(BackgroundId background_id,LogEventStorerUnsafe & storer)416 void BackgroundManager::store_background(BackgroundId background_id, LogEventStorerUnsafe &storer) {
417 const auto *background = get_background(background_id);
418 CHECK(background != nullptr);
419 store(*background, storer);
420 }
421
parse_background(BackgroundId & background_id,LogEventParser & parser)422 void BackgroundManager::parse_background(BackgroundId &background_id, LogEventParser &parser) {
423 Background background;
424 parse(background, parser);
425 CHECK(background.has_new_local_id);
426 if (background.file_id.is_valid() != background.type.has_file() || !background.id.is_valid()) {
427 parser.set_error(PSTRING() << "Failed to load " << background.id);
428 background_id = BackgroundId();
429 return;
430 }
431 if (background.id.is_local() && !background.type.has_file() && background.id.get() > max_local_background_id_.get()) {
432 set_max_local_background_id(background.id);
433 }
434 background_id = background.id;
435 add_background(background, false);
436 }
437
get_backgrounds(bool for_dark_theme,Promise<td_api::object_ptr<td_api::backgrounds>> && promise)438 void BackgroundManager::get_backgrounds(bool for_dark_theme,
439 Promise<td_api::object_ptr<td_api::backgrounds>> &&promise) {
440 pending_get_backgrounds_queries_.emplace_back(for_dark_theme, std::move(promise));
441 if (pending_get_backgrounds_queries_.size() == 1) {
442 auto request_promise = PromiseCreator::lambda(
443 [actor_id = actor_id(this)](Result<telegram_api::object_ptr<telegram_api::account_WallPapers>> result) {
444 send_closure(actor_id, &BackgroundManager::on_get_backgrounds, std::move(result));
445 });
446
447 td_->create_handler<GetBackgroundsQuery>(std::move(request_promise))->send();
448 }
449 }
450
get_background_url(const string & name,td_api::object_ptr<td_api::BackgroundType> background_type)451 Result<string> BackgroundManager::get_background_url(const string &name,
452 td_api::object_ptr<td_api::BackgroundType> background_type) {
453 TRY_RESULT(type, BackgroundType::get_background_type(background_type.get()));
454 auto url = PSTRING() << G()->shared_config().get_option_string("t_me_url", "https://t.me/") << "bg/";
455 auto link = type.get_link();
456 if (type.has_file()) {
457 url += name;
458 if (!link.empty()) {
459 url += '?';
460 url += link;
461 }
462 } else {
463 url += link;
464 }
465 return url;
466 }
467
reload_background_from_server(BackgroundId background_id,const string & background_name,telegram_api::object_ptr<telegram_api::InputWallPaper> && input_wallpaper,Promise<Unit> && promise) const468 void BackgroundManager::reload_background_from_server(
469 BackgroundId background_id, const string &background_name,
470 telegram_api::object_ptr<telegram_api::InputWallPaper> &&input_wallpaper, Promise<Unit> &&promise) const {
471 TRY_STATUS_PROMISE(promise, G()->close_status());
472
473 td_->create_handler<GetBackgroundQuery>(std::move(promise))
474 ->send(background_id, background_name, std::move(input_wallpaper));
475 }
476
reload_background(BackgroundId background_id,int64 access_hash,Promise<Unit> && promise)477 void BackgroundManager::reload_background(BackgroundId background_id, int64 access_hash, Promise<Unit> &&promise) {
478 reload_background_from_server(
479 background_id, string(),
480 telegram_api::make_object<telegram_api::inputWallPaper>(background_id.get(), access_hash), std::move(promise));
481 }
482
is_background_name_local(Slice name)483 static bool is_background_name_local(Slice name) {
484 return name.size() <= 13u || name.find('?') <= 13u || !is_base64url_characters(name.substr(0, name.find('?')));
485 }
486
search_background(const string & name,Promise<Unit> && promise)487 std::pair<BackgroundId, BackgroundType> BackgroundManager::search_background(const string &name,
488 Promise<Unit> &&promise) {
489 auto params_pos = name.find('?');
490 string slug = params_pos >= name.size() ? name : name.substr(0, params_pos);
491 auto it = name_to_background_id_.find(slug);
492 if (it != name_to_background_id_.end()) {
493 CHECK(!is_background_name_local(slug));
494
495 const auto *background = get_background(it->second);
496 CHECK(background != nullptr);
497 promise.set_value(Unit());
498 BackgroundType type = background->type;
499 type.apply_parameters_from_link(name);
500 return {it->second, std::move(type)};
501 }
502
503 if (slug.empty()) {
504 promise.set_error(Status::Error(400, "Background name must be non-empty"));
505 return {};
506 }
507
508 if (is_background_name_local(slug)) {
509 auto r_type = BackgroundType::get_local_background_type(name);
510 if (r_type.is_error()) {
511 promise.set_error(r_type.move_as_error());
512 return {};
513 }
514 auto background_id = add_local_background(r_type.ok());
515 promise.set_value(Unit());
516 return {background_id, r_type.ok()};
517 }
518
519 if (G()->parameters().use_file_db && loaded_from_database_backgrounds_.count(slug) == 0) {
520 auto &queries = being_loaded_from_database_backgrounds_[slug];
521 queries.push_back(std::move(promise));
522 if (queries.size() == 1) {
523 LOG(INFO) << "Trying to load background " << slug << " from database";
524 G()->td_db()->get_sqlite_pmc()->get(
525 get_background_name_database_key(slug), PromiseCreator::lambda([slug](string value) mutable {
526 send_closure(G()->background_manager(), &BackgroundManager::on_load_background_from_database,
527 std::move(slug), std::move(value));
528 }));
529 }
530 return {};
531 }
532
533 reload_background_from_server(BackgroundId(), slug, telegram_api::make_object<telegram_api::inputWallPaperSlug>(slug),
534 std::move(promise));
535 return {};
536 }
537
on_load_background_from_database(string name,string value)538 void BackgroundManager::on_load_background_from_database(string name, string value) {
539 if (G()->close_flag()) {
540 return;
541 }
542
543 auto promises_it = being_loaded_from_database_backgrounds_.find(name);
544 CHECK(promises_it != being_loaded_from_database_backgrounds_.end());
545 auto promises = std::move(promises_it->second);
546 CHECK(!promises.empty());
547 being_loaded_from_database_backgrounds_.erase(promises_it);
548
549 loaded_from_database_backgrounds_.insert(name);
550
551 CHECK(!is_background_name_local(name));
552 if (name_to_background_id_.count(name) == 0 && !value.empty()) {
553 LOG(INFO) << "Successfully loaded background " << name << " of size " << value.size() << " from database";
554 Background background;
555 auto status = log_event_parse(background, value);
556 if (status.is_error() || !background.type.has_file() || !background.file_id.is_valid() ||
557 !background.id.is_valid()) {
558 LOG(ERROR) << "Can't load background " << name << ": " << status << ' ' << format::as_hex_dump<4>(Slice(value));
559 } else {
560 if (background.name != name) {
561 LOG(ERROR) << "Expected background " << name << ", but received " << background.name;
562 name_to_background_id_.emplace(std::move(name), background.id);
563 }
564 add_background(background, false);
565 }
566 }
567
568 for (auto &promise : promises) {
569 promise.set_value(Unit());
570 }
571 }
572
get_update_selected_background_object(bool for_dark_theme) const573 td_api::object_ptr<td_api::updateSelectedBackground> BackgroundManager::get_update_selected_background_object(
574 bool for_dark_theme) const {
575 return td_api::make_object<td_api::updateSelectedBackground>(
576 for_dark_theme,
577 get_background_object(set_background_id_[for_dark_theme], for_dark_theme, &set_background_type_[for_dark_theme]));
578 }
579
send_update_selected_background(bool for_dark_theme) const580 void BackgroundManager::send_update_selected_background(bool for_dark_theme) const {
581 send_closure(G()->td(), &Td::send_update, get_update_selected_background_object(for_dark_theme));
582 }
583
prepare_input_file(const tl_object_ptr<td_api::InputFile> & input_file)584 Result<FileId> BackgroundManager::prepare_input_file(const tl_object_ptr<td_api::InputFile> &input_file) {
585 auto r_file_id = td_->file_manager_->get_input_file_id(FileType::Background, input_file, {}, false, false);
586 if (r_file_id.is_error()) {
587 return Status::Error(400, r_file_id.error().message());
588 }
589 auto file_id = r_file_id.move_as_ok();
590
591 FileView file_view = td_->file_manager_->get_file_view(file_id);
592 if (file_view.is_encrypted()) {
593 return Status::Error(400, "Can't use encrypted file");
594 }
595 if (!file_view.has_local_location() && !file_view.has_generate_location()) {
596 return Status::Error(400, "Need local or generate location to upload background");
597 }
598 return std::move(file_id);
599 }
600
set_max_local_background_id(BackgroundId background_id)601 void BackgroundManager::set_max_local_background_id(BackgroundId background_id) {
602 CHECK(background_id.is_local());
603 CHECK(background_id.get() > max_local_background_id_.get());
604 max_local_background_id_ = background_id;
605 G()->td_db()->get_binlog_pmc()->set("max_bg_id", to_string(max_local_background_id_.get()));
606 }
607
get_next_local_background_id()608 BackgroundId BackgroundManager::get_next_local_background_id() {
609 set_max_local_background_id(BackgroundId(max_local_background_id_.get() + 1));
610 return max_local_background_id_;
611 }
612
add_local_background(const BackgroundType & type)613 BackgroundId BackgroundManager::add_local_background(const BackgroundType &type) {
614 Background background;
615 background.id = get_next_local_background_id();
616 background.is_creator = true;
617 background.is_default = false;
618 background.is_dark = type.is_dark();
619 background.type = type;
620 background.name = type.get_link();
621 add_background(background, true);
622
623 return background.id;
624 }
625
set_background(const td_api::InputBackground * input_background,const td_api::BackgroundType * background_type,bool for_dark_theme,Promise<Unit> && promise)626 BackgroundId BackgroundManager::set_background(const td_api::InputBackground *input_background,
627 const td_api::BackgroundType *background_type, bool for_dark_theme,
628 Promise<Unit> &&promise) {
629 BackgroundType type;
630 if (background_type != nullptr) {
631 auto r_type = BackgroundType::get_background_type(background_type);
632 if (r_type.is_error()) {
633 promise.set_error(r_type.move_as_error());
634 return BackgroundId();
635 }
636 type = r_type.move_as_ok();
637 } else {
638 CHECK(!type.has_file());
639 }
640
641 if (input_background == nullptr) {
642 if (background_type == nullptr) {
643 set_background_id(BackgroundId(), BackgroundType(), for_dark_theme);
644 promise.set_value(Unit());
645 return BackgroundId();
646 }
647 if (type.has_file()) {
648 promise.set_error(Status::Error(400, "Input background must be non-empty for the background type"));
649 return BackgroundId();
650 }
651
652 auto background_id = add_local_background(type);
653 set_background_id(background_id, type, for_dark_theme);
654
655 local_background_ids_[for_dark_theme].insert(local_background_ids_[for_dark_theme].begin(), background_id);
656 save_local_backgrounds(for_dark_theme);
657
658 promise.set_value(Unit());
659 return background_id;
660 }
661
662 switch (input_background->get_id()) {
663 case td_api::inputBackgroundLocal::ID: {
664 if (!type.has_file()) {
665 promise.set_error(Status::Error(400, "Can't specify local file for the background type"));
666 return BackgroundId();
667 }
668 CHECK(background_type != nullptr);
669
670 auto background_local = static_cast<const td_api::inputBackgroundLocal *>(input_background);
671 auto r_file_id = prepare_input_file(background_local->background_);
672 if (r_file_id.is_error()) {
673 promise.set_error(r_file_id.move_as_error());
674 return BackgroundId();
675 }
676 auto file_id = r_file_id.move_as_ok();
677 LOG(INFO) << "Receive file " << file_id << " for input background";
678
679 auto it = file_id_to_background_id_.find(file_id);
680 if (it != file_id_to_background_id_.end()) {
681 return set_background(it->second, type, for_dark_theme, std::move(promise));
682 }
683
684 upload_background_file(file_id, type, for_dark_theme, std::move(promise));
685 break;
686 }
687 case td_api::inputBackgroundRemote::ID: {
688 auto background_remote = static_cast<const td_api::inputBackgroundRemote *>(input_background);
689 return set_background(BackgroundId(background_remote->background_id_), std::move(type), for_dark_theme,
690 std::move(promise));
691 }
692 default:
693 UNREACHABLE();
694 }
695 return BackgroundId();
696 }
697
set_background(BackgroundId background_id,BackgroundType type,bool for_dark_theme,Promise<Unit> && promise)698 BackgroundId BackgroundManager::set_background(BackgroundId background_id, BackgroundType type, bool for_dark_theme,
699 Promise<Unit> &&promise) {
700 LOG(INFO) << "Set " << background_id << " with " << type;
701 const auto *background = get_background(background_id);
702 if (background == nullptr) {
703 promise.set_error(Status::Error(400, "Background to set not found"));
704 return BackgroundId();
705 }
706 if (!type.has_file()) {
707 type = background->type;
708 } else if (!background->type.has_equal_type(type)) {
709 promise.set_error(Status::Error(400, "Background type mismatch"));
710 return BackgroundId();
711 }
712 if (set_background_id_[for_dark_theme] == background_id && set_background_type_[for_dark_theme] == type) {
713 promise.set_value(Unit());
714 return background_id;
715 }
716
717 LOG(INFO) << "Install " << background_id << " with " << type;
718
719 if (!type.has_file()) {
720 set_background_id(background_id, type, for_dark_theme);
721 promise.set_value(Unit());
722 return background_id;
723 }
724
725 auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), background_id, type, for_dark_theme,
726 promise = std::move(promise)](Result<Unit> &&result) mutable {
727 send_closure(actor_id, &BackgroundManager::on_installed_background, background_id, type, for_dark_theme,
728 std::move(result), std::move(promise));
729 });
730 td_->create_handler<InstallBackgroundQuery>(std::move(query_promise))
731 ->send(telegram_api::make_object<telegram_api::inputWallPaper>(background_id.get(), background->access_hash),
732 type);
733 return BackgroundId();
734 }
735
on_installed_background(BackgroundId background_id,BackgroundType type,bool for_dark_theme,Result<Unit> && result,Promise<Unit> && promise)736 void BackgroundManager::on_installed_background(BackgroundId background_id, BackgroundType type, bool for_dark_theme,
737 Result<Unit> &&result, Promise<Unit> &&promise) {
738 if (result.is_error()) {
739 return promise.set_error(result.move_as_error());
740 }
741
742 size_t i;
743 for (i = 0; i < installed_backgrounds_.size(); i++) {
744 if (installed_backgrounds_[i].first == background_id) {
745 installed_backgrounds_[i].second = type;
746 break;
747 }
748 }
749 if (i == installed_backgrounds_.size()) {
750 installed_backgrounds_.insert(installed_backgrounds_.begin(), {background_id, type});
751 }
752 set_background_id(background_id, type, for_dark_theme);
753 promise.set_value(Unit());
754 }
755
get_background_database_key(bool for_dark_theme)756 string BackgroundManager::get_background_database_key(bool for_dark_theme) {
757 return for_dark_theme ? "bgd" : "bg";
758 }
759
get_local_backgrounds_database_key(bool for_dark_theme)760 string BackgroundManager::get_local_backgrounds_database_key(bool for_dark_theme) {
761 return for_dark_theme ? "bgsd" : "bgs";
762 }
763
save_background_id(bool for_dark_theme)764 void BackgroundManager::save_background_id(bool for_dark_theme) {
765 string key = get_background_database_key(for_dark_theme);
766 auto background_id = set_background_id_[for_dark_theme];
767 if (background_id.is_valid()) {
768 const Background *background = get_background(background_id);
769 CHECK(background != nullptr);
770 BackgroundLogEvent log_event{*background, set_background_type_[for_dark_theme]};
771 G()->td_db()->get_binlog_pmc()->set(key, log_event_store(log_event).as_slice().str());
772 } else {
773 G()->td_db()->get_binlog_pmc()->erase(key);
774 }
775 }
776
set_background_id(BackgroundId background_id,const BackgroundType & type,bool for_dark_theme)777 void BackgroundManager::set_background_id(BackgroundId background_id, const BackgroundType &type, bool for_dark_theme) {
778 if (background_id == set_background_id_[for_dark_theme] && set_background_type_[for_dark_theme] == type) {
779 return;
780 }
781
782 set_background_id_[for_dark_theme] = background_id;
783 set_background_type_[for_dark_theme] = type;
784
785 save_background_id(for_dark_theme);
786 send_update_selected_background(for_dark_theme);
787 }
788
save_local_backgrounds(bool for_dark_theme)789 void BackgroundManager::save_local_backgrounds(bool for_dark_theme) {
790 string key = get_local_backgrounds_database_key(for_dark_theme);
791 auto &background_ids = local_background_ids_[for_dark_theme];
792 const size_t MAX_LOCAL_BACKGROUNDS = 100;
793 while (background_ids.size() > MAX_LOCAL_BACKGROUNDS) {
794 background_ids.pop_back();
795 }
796 if (!background_ids.empty()) {
797 BackgroundsLogEvent log_event;
798 log_event.backgrounds_ = transform(background_ids, [&](BackgroundId background_id) {
799 const Background *background = get_background(background_id);
800 CHECK(background != nullptr);
801 return *background;
802 });
803 G()->td_db()->get_binlog_pmc()->set(key, log_event_store(log_event).as_slice().str());
804 } else {
805 G()->td_db()->get_binlog_pmc()->erase(key);
806 }
807 }
808
upload_background_file(FileId file_id,const BackgroundType & type,bool for_dark_theme,Promise<Unit> && promise)809 void BackgroundManager::upload_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme,
810 Promise<Unit> &&promise) {
811 auto upload_file_id = td_->file_manager_->dup_file_id(file_id);
812 bool is_inserted =
813 being_uploaded_files_.emplace(upload_file_id, UploadedFileInfo(type, for_dark_theme, std::move(promise))).second;
814 CHECK(is_inserted);
815 LOG(INFO) << "Ask to upload background file " << upload_file_id;
816 td_->file_manager_->upload(upload_file_id, upload_background_file_callback_, 1, 0);
817 }
818
on_upload_background_file(FileId file_id,tl_object_ptr<telegram_api::InputFile> input_file)819 void BackgroundManager::on_upload_background_file(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file) {
820 LOG(INFO) << "Background file " << file_id << " has been uploaded";
821
822 auto it = being_uploaded_files_.find(file_id);
823 CHECK(it != being_uploaded_files_.end());
824
825 auto type = it->second.type_;
826 auto for_dark_theme = it->second.for_dark_theme_;
827 auto promise = std::move(it->second.promise_);
828
829 being_uploaded_files_.erase(it);
830
831 do_upload_background_file(file_id, type, for_dark_theme, std::move(input_file), std::move(promise));
832 }
833
on_upload_background_file_error(FileId file_id,Status status)834 void BackgroundManager::on_upload_background_file_error(FileId file_id, Status status) {
835 if (G()->close_flag()) {
836 // do not fail upload if closing
837 return;
838 }
839
840 LOG(WARNING) << "Background file " << file_id << " has upload error " << status;
841 CHECK(status.is_error());
842
843 auto it = being_uploaded_files_.find(file_id);
844 CHECK(it != being_uploaded_files_.end());
845
846 auto promise = std::move(it->second.promise_);
847
848 being_uploaded_files_.erase(it);
849
850 promise.set_error(Status::Error(status.code() > 0 ? status.code() : 500,
851 status.message())); // TODO CHECK that status has always a code
852 }
853
do_upload_background_file(FileId file_id,const BackgroundType & type,bool for_dark_theme,tl_object_ptr<telegram_api::InputFile> && input_file,Promise<Unit> && promise)854 void BackgroundManager::do_upload_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme,
855 tl_object_ptr<telegram_api::InputFile> &&input_file,
856 Promise<Unit> &&promise) {
857 if (input_file == nullptr) {
858 FileView file_view = td_->file_manager_->get_file_view(file_id);
859 file_id = file_view.file_id();
860 auto it = file_id_to_background_id_.find(file_id);
861 if (it != file_id_to_background_id_.end()) {
862 set_background(it->second, type, for_dark_theme, std::move(promise));
863 return;
864 }
865 return promise.set_error(Status::Error(500, "Failed to reupload background"));
866 }
867
868 td_->create_handler<UploadBackgroundQuery>(std::move(promise))
869 ->send(file_id, std::move(input_file), type, for_dark_theme);
870 }
871
on_uploaded_background_file(FileId file_id,const BackgroundType & type,bool for_dark_theme,telegram_api::object_ptr<telegram_api::WallPaper> wallpaper,Promise<Unit> && promise)872 void BackgroundManager::on_uploaded_background_file(FileId file_id, const BackgroundType &type, bool for_dark_theme,
873 telegram_api::object_ptr<telegram_api::WallPaper> wallpaper,
874 Promise<Unit> &&promise) {
875 CHECK(wallpaper != nullptr);
876
877 auto added_background = on_get_background(BackgroundId(), string(), std::move(wallpaper), true);
878 auto background_id = added_background.first;
879 if (!background_id.is_valid()) {
880 td_->file_manager_->cancel_upload(file_id);
881 return promise.set_error(Status::Error(500, "Receive wrong uploaded background"));
882 }
883 LOG_IF(ERROR, added_background.second != type)
884 << "Type of uploaded background has changed from " << type << " to " << added_background.second;
885
886 const auto *background = get_background(background_id);
887 CHECK(background != nullptr);
888 if (!background->file_id.is_valid()) {
889 td_->file_manager_->cancel_upload(file_id);
890 return promise.set_error(Status::Error(500, "Receive wrong uploaded background without file"));
891 }
892 LOG_STATUS(td_->file_manager_->merge(background->file_id, file_id));
893 set_background_id(background_id, type, for_dark_theme);
894 promise.set_value(Unit());
895 }
896
remove_background(BackgroundId background_id,Promise<Unit> && promise)897 void BackgroundManager::remove_background(BackgroundId background_id, Promise<Unit> &&promise) {
898 const auto *background = get_background(background_id);
899 if (background == nullptr) {
900 return promise.set_error(Status::Error(400, "Background not found"));
901 }
902
903 auto query_promise = PromiseCreator::lambda(
904 [actor_id = actor_id(this), background_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
905 send_closure(actor_id, &BackgroundManager::on_removed_background, background_id, std::move(result),
906 std::move(promise));
907 });
908
909 if (!background->type.has_file()) {
910 if (!background->id.is_local()) {
911 return td_->create_handler<UnsaveBackgroundQuery>(std::move(query_promise))
912 ->send(telegram_api::make_object<telegram_api::inputWallPaperNoFile>(background_id.get()));
913 } else {
914 return query_promise.set_value(Unit());
915 }
916 }
917
918 td_->create_handler<UnsaveBackgroundQuery>(std::move(query_promise))
919 ->send(telegram_api::make_object<telegram_api::inputWallPaper>(background_id.get(), background->access_hash));
920 }
921
on_removed_background(BackgroundId background_id,Result<Unit> && result,Promise<Unit> && promise)922 void BackgroundManager::on_removed_background(BackgroundId background_id, Result<Unit> &&result,
923 Promise<Unit> &&promise) {
924 if (result.is_error()) {
925 return promise.set_error(result.move_as_error());
926 }
927 td::remove_if(installed_backgrounds_,
928 [background_id](const auto &background) { return background.first == background_id; });
929 if (background_id == set_background_id_[0]) {
930 set_background_id(BackgroundId(), BackgroundType(), false);
931 }
932 if (background_id == set_background_id_[1]) {
933 set_background_id(BackgroundId(), BackgroundType(), true);
934 }
935 if (background_id.is_local()) {
936 if (td::remove(local_background_ids_[0], background_id)) {
937 save_local_backgrounds(false);
938 }
939 if (td::remove(local_background_ids_[1], background_id)) {
940 save_local_backgrounds(true);
941 }
942 }
943 promise.set_value(Unit());
944 }
945
reset_backgrounds(Promise<Unit> && promise)946 void BackgroundManager::reset_backgrounds(Promise<Unit> &&promise) {
947 auto query_promise =
948 PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](Result<Unit> &&result) mutable {
949 send_closure(actor_id, &BackgroundManager::on_reset_background, std::move(result), std::move(promise));
950 });
951
952 td_->create_handler<ResetBackgroundsQuery>(std::move(query_promise))->send();
953 }
954
on_reset_background(Result<Unit> && result,Promise<Unit> && promise)955 void BackgroundManager::on_reset_background(Result<Unit> &&result, Promise<Unit> &&promise) {
956 if (result.is_error()) {
957 return promise.set_error(result.move_as_error());
958 }
959 installed_backgrounds_.clear();
960 set_background_id(BackgroundId(), BackgroundType(), false);
961 set_background_id(BackgroundId(), BackgroundType(), true);
962 if (!local_background_ids_[0].empty()) {
963 local_background_ids_[0].clear();
964 save_local_backgrounds(false);
965 }
966 if (!local_background_ids_[1].empty()) {
967 local_background_ids_[1].clear();
968 save_local_backgrounds(true);
969 }
970
971 promise.set_value(Unit());
972 }
973
add_background(const Background & background,bool replace_type)974 void BackgroundManager::add_background(const Background &background, bool replace_type) {
975 LOG(INFO) << "Add " << background.id << " of " << background.type;
976
977 CHECK(background.id.is_valid());
978 auto *result = &backgrounds_[background.id];
979
980 FileSourceId file_source_id;
981 auto it = background_id_to_file_source_id_.find(background.id);
982 if (it != background_id_to_file_source_id_.end()) {
983 CHECK(!result->id.is_valid());
984 file_source_id = it->second.second;
985 background_id_to_file_source_id_.erase(it);
986 }
987
988 if (!result->id.is_valid()) {
989 result->id = background.id;
990 result->type = background.type;
991 } else {
992 CHECK(result->id == background.id);
993 if (replace_type) {
994 result->type = background.type;
995 }
996 }
997 result->access_hash = background.access_hash;
998 result->is_creator = background.is_creator;
999 result->is_default = background.is_default;
1000 result->is_dark = background.is_dark;
1001
1002 if (result->name != background.name) {
1003 if (!result->name.empty()) {
1004 LOG(ERROR) << "Background name has changed from " << result->name << " to " << background.name;
1005 // keep correspondence from previous name to background ID
1006 // it will not harm, because background names can't be reassigned
1007 // name_to_background_id_.erase(result->name);
1008 }
1009
1010 result->name = background.name;
1011
1012 if (!is_background_name_local(result->name)) {
1013 name_to_background_id_.emplace(result->name, result->id);
1014 loaded_from_database_backgrounds_.erase(result->name); // don't needed anymore
1015 }
1016 }
1017
1018 if (result->file_id != background.file_id) {
1019 if (result->file_id.is_valid()) {
1020 if (!background.file_id.is_valid() || td_->file_manager_->get_file_view(result->file_id).file_id() !=
1021 td_->file_manager_->get_file_view(background.file_id).file_id()) {
1022 LOG(ERROR) << "Background file has changed from " << result->file_id << " to " << background.file_id;
1023 file_id_to_background_id_.erase(result->file_id);
1024 result->file_source_id = FileSourceId();
1025 }
1026 CHECK(!file_source_id.is_valid());
1027 }
1028 if (file_source_id.is_valid()) {
1029 result->file_source_id = file_source_id;
1030 }
1031
1032 result->file_id = background.file_id;
1033
1034 if (result->file_id.is_valid()) {
1035 if (!result->file_source_id.is_valid()) {
1036 result->file_source_id =
1037 td_->file_reference_manager_->create_background_file_source(result->id, result->access_hash);
1038 }
1039 for (auto file_id : Document(Document::Type::General, result->file_id).get_file_ids(td_)) {
1040 td_->file_manager_->add_file_source(file_id, result->file_source_id);
1041 }
1042 }
1043
1044 file_id_to_background_id_.emplace(result->file_id, result->id);
1045 } else {
1046 // if file_source_id is valid, then this is a new background with result->file_id == FileId()
1047 // then background.file_id == FileId(), then this is a fill background, which can't have file_source_id
1048 CHECK(!file_source_id.is_valid());
1049 }
1050 }
1051
get_background_ref(BackgroundId background_id)1052 BackgroundManager::Background *BackgroundManager::get_background_ref(BackgroundId background_id) {
1053 auto p = backgrounds_.find(background_id);
1054 if (p == backgrounds_.end()) {
1055 return nullptr;
1056 } else {
1057 return &p->second;
1058 }
1059 }
1060
get_background(BackgroundId background_id) const1061 const BackgroundManager::Background *BackgroundManager::get_background(BackgroundId background_id) const {
1062 auto p = backgrounds_.find(background_id);
1063 if (p == backgrounds_.end()) {
1064 return nullptr;
1065 } else {
1066 return &p->second;
1067 }
1068 }
1069
get_background_name_database_key(const string & name)1070 string BackgroundManager::get_background_name_database_key(const string &name) {
1071 return PSTRING() << "bgn" << name;
1072 }
1073
on_get_background(BackgroundId expected_background_id,const string & expected_background_name,telegram_api::object_ptr<telegram_api::WallPaper> wallpaper_ptr,bool replace_type)1074 std::pair<BackgroundId, BackgroundType> BackgroundManager::on_get_background(
1075 BackgroundId expected_background_id, const string &expected_background_name,
1076 telegram_api::object_ptr<telegram_api::WallPaper> wallpaper_ptr, bool replace_type) {
1077 if (wallpaper_ptr == nullptr) {
1078 return {};
1079 }
1080
1081 if (wallpaper_ptr->get_id() == telegram_api::wallPaperNoFile::ID) {
1082 auto wallpaper = move_tl_object_as<telegram_api::wallPaperNoFile>(wallpaper_ptr);
1083
1084 if (wallpaper->settings_ == nullptr) {
1085 LOG(ERROR) << "Receive wallPaperNoFile without settings: " << to_string(wallpaper);
1086 return {};
1087 }
1088
1089 auto background_id = BackgroundId(wallpaper->id_);
1090 if (background_id.is_local()) {
1091 LOG(ERROR) << "Receive " << to_string(wallpaper);
1092 return {};
1093 }
1094 if (!background_id.is_valid()) {
1095 background_id = get_next_local_background_id();
1096 }
1097
1098 Background background;
1099 background.id = background_id;
1100 background.is_creator = false;
1101 background.is_default = wallpaper->default_;
1102 background.is_dark = wallpaper->dark_;
1103 background.type = BackgroundType(true, false, std::move(wallpaper->settings_));
1104 background.name = background.type.get_link();
1105 add_background(background, replace_type);
1106
1107 return {background_id, background.type};
1108 }
1109
1110 auto wallpaper = move_tl_object_as<telegram_api::wallPaper>(wallpaper_ptr);
1111 auto background_id = BackgroundId(wallpaper->id_);
1112 if (!background_id.is_valid() || background_id.is_local() || is_background_name_local(wallpaper->slug_)) {
1113 LOG(ERROR) << "Receive " << to_string(wallpaper);
1114 return {};
1115 }
1116 if (expected_background_id.is_valid() && background_id != expected_background_id) {
1117 LOG(ERROR) << "Expected " << expected_background_id << ", but receive " << to_string(wallpaper);
1118 }
1119
1120 int32 document_id = wallpaper->document_->get_id();
1121 if (document_id == telegram_api::documentEmpty::ID) {
1122 LOG(ERROR) << "Receive " << to_string(wallpaper);
1123 return {};
1124 }
1125 CHECK(document_id == telegram_api::document::ID);
1126
1127 bool is_pattern = wallpaper->pattern_;
1128
1129 Document document = td_->documents_manager_->on_get_document(
1130 telegram_api::move_object_as<telegram_api::document>(wallpaper->document_), DialogId(), nullptr,
1131 Document::Type::General, true, is_pattern);
1132 if (!document.file_id.is_valid()) {
1133 LOG(ERROR) << "Receive wrong document in " << to_string(wallpaper);
1134 return {};
1135 }
1136 CHECK(document.type == Document::Type::General); // guaranteed by is_background parameter to on_get_document
1137
1138 Background background;
1139 background.id = background_id;
1140 background.access_hash = wallpaper->access_hash_;
1141 background.is_creator = wallpaper->creator_;
1142 background.is_default = wallpaper->default_;
1143 background.is_dark = wallpaper->dark_;
1144 background.type = BackgroundType(false, is_pattern, std::move(wallpaper->settings_));
1145 background.name = std::move(wallpaper->slug_);
1146 background.file_id = document.file_id;
1147 add_background(background, replace_type);
1148
1149 if (!expected_background_name.empty() && background.name != expected_background_name) {
1150 LOG(ERROR) << "Expected background " << expected_background_name << ", but receive " << background.name;
1151 name_to_background_id_.emplace(expected_background_name, background_id);
1152 }
1153
1154 if (G()->parameters().use_file_db) {
1155 LOG(INFO) << "Save " << background_id << " to database with name " << background.name;
1156 CHECK(!is_background_name_local(background.name));
1157 G()->td_db()->get_sqlite_pmc()->set(get_background_name_database_key(background.name),
1158 log_event_store(background).as_slice().str(), Auto());
1159 }
1160
1161 return {background_id, background.type};
1162 }
1163
on_get_backgrounds(Result<telegram_api::object_ptr<telegram_api::account_WallPapers>> result)1164 void BackgroundManager::on_get_backgrounds(Result<telegram_api::object_ptr<telegram_api::account_WallPapers>> result) {
1165 auto promises = std::move(pending_get_backgrounds_queries_);
1166 CHECK(!promises.empty());
1167 reset_to_empty(pending_get_backgrounds_queries_);
1168
1169 if (result.is_error()) {
1170 // do not clear installed_backgrounds_
1171
1172 auto error = result.move_as_error();
1173 for (auto &promise : promises) {
1174 promise.second.set_error(error.clone());
1175 }
1176 return;
1177 }
1178
1179 auto wallpapers_ptr = result.move_as_ok();
1180 LOG(INFO) << "Receive " << to_string(wallpapers_ptr);
1181 if (wallpapers_ptr->get_id() == telegram_api::account_wallPapersNotModified::ID) {
1182 for (auto &promise : promises) {
1183 promise.second.set_value(get_backgrounds_object(promise.first));
1184 }
1185 return;
1186 }
1187
1188 installed_backgrounds_.clear();
1189 auto wallpapers = telegram_api::move_object_as<telegram_api::account_wallPapers>(wallpapers_ptr);
1190 for (auto &wallpaper : wallpapers->wallpapers_) {
1191 auto background = on_get_background(BackgroundId(), string(), std::move(wallpaper), false);
1192 if (background.first.is_valid()) {
1193 installed_backgrounds_.push_back(std::move(background));
1194 }
1195 }
1196
1197 for (auto &promise : promises) {
1198 promise.second.set_value(get_backgrounds_object(promise.first));
1199 }
1200 }
1201
get_background_object(BackgroundId background_id,bool for_dark_theme,const BackgroundType * type) const1202 td_api::object_ptr<td_api::background> BackgroundManager::get_background_object(BackgroundId background_id,
1203 bool for_dark_theme,
1204 const BackgroundType *type) const {
1205 const auto *background = get_background(background_id);
1206 if (background == nullptr) {
1207 return nullptr;
1208 }
1209 if (type == nullptr) {
1210 type = &background->type;
1211 // first check another set_background_id to get correct type if both backgrounds are the same
1212 if (background_id == set_background_id_[1 - static_cast<int>(for_dark_theme)]) {
1213 type = &set_background_type_[1 - static_cast<int>(for_dark_theme)];
1214 }
1215 if (background_id == set_background_id_[for_dark_theme]) {
1216 type = &set_background_type_[for_dark_theme];
1217 }
1218 }
1219 return td_api::make_object<td_api::background>(
1220 background->id.get(), background->is_default, background->is_dark, background->name,
1221 td_->documents_manager_->get_document_object(background->file_id, PhotoFormat::Png),
1222 type->get_background_type_object());
1223 }
1224
get_backgrounds_object(bool for_dark_theme) const1225 td_api::object_ptr<td_api::backgrounds> BackgroundManager::get_backgrounds_object(bool for_dark_theme) const {
1226 auto backgrounds = transform(installed_backgrounds_,
1227 [this, for_dark_theme](const std::pair<BackgroundId, BackgroundType> &background) {
1228 return get_background_object(background.first, for_dark_theme, &background.second);
1229 });
1230 auto background_id = set_background_id_[for_dark_theme];
1231 bool have_background = false;
1232 for (const auto &background : installed_backgrounds_) {
1233 if (background_id == background.first) {
1234 have_background = true;
1235 break;
1236 }
1237 }
1238 if (background_id.is_valid() && !have_background) {
1239 backgrounds.push_back(get_background_object(background_id, for_dark_theme, nullptr));
1240 }
1241 for (auto local_background_id : local_background_ids_[for_dark_theme]) {
1242 if (local_background_id != background_id) {
1243 backgrounds.push_back(get_background_object(local_background_id, for_dark_theme, nullptr));
1244 }
1245 }
1246 std::stable_sort(backgrounds.begin(), backgrounds.end(),
1247 [background_id, for_dark_theme](const td_api::object_ptr<td_api::background> &lhs,
1248 const td_api::object_ptr<td_api::background> &rhs) {
1249 auto get_order = [background_id,
1250 for_dark_theme](const td_api::object_ptr<td_api::background> &background) {
1251 if (background->id_ == background_id.get()) {
1252 return 0;
1253 }
1254 int theme_score = background->is_dark_ == for_dark_theme ? 0 : 1;
1255 int local_score = BackgroundId(background->id_).is_local() ? 0 : 2;
1256 return 1 + local_score + theme_score;
1257 };
1258 return get_order(lhs) < get_order(rhs);
1259 });
1260 return td_api::make_object<td_api::backgrounds>(std::move(backgrounds));
1261 }
1262
get_background_file_source_id(BackgroundId background_id,int64 access_hash)1263 FileSourceId BackgroundManager::get_background_file_source_id(BackgroundId background_id, int64 access_hash) {
1264 Background *background = get_background_ref(background_id);
1265 if (background != nullptr) {
1266 if (!background->file_source_id.is_valid()) {
1267 background->file_source_id =
1268 td_->file_reference_manager_->create_background_file_source(background_id, background->access_hash);
1269 }
1270 return background->file_source_id;
1271 }
1272
1273 auto &result = background_id_to_file_source_id_[background_id];
1274 if (result.first == 0) {
1275 result.first = access_hash;
1276 }
1277 if (!result.second.is_valid()) {
1278 result.second = td_->file_reference_manager_->create_background_file_source(background_id, result.first);
1279 }
1280 return result.second;
1281 }
1282
get_current_state(vector<td_api::object_ptr<td_api::Update>> & updates) const1283 void BackgroundManager::get_current_state(vector<td_api::object_ptr<td_api::Update>> &updates) const {
1284 if (td_->auth_manager_->is_bot()) {
1285 return;
1286 }
1287
1288 updates.push_back(get_update_selected_background_object(false));
1289 updates.push_back(get_update_selected_background_object(true));
1290 }
1291
1292 } // namespace td
1293