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