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 #pragma once
8 
9 #include "td/telegram/StickersManager.h"
10 
11 #include "td/telegram/files/FileId.hpp"
12 #include "td/telegram/misc.h"
13 #include "td/telegram/Photo.hpp"
14 
15 #include "td/utils/emoji.h"
16 #include "td/utils/logging.h"
17 #include "td/utils/misc.h"
18 #include "td/utils/Slice.h"
19 #include "td/utils/tl_helpers.h"
20 #include "td/utils/utf8.h"
21 
22 namespace td {
23 
24 template <class StorerT>
store_sticker(FileId file_id,bool in_sticker_set,StorerT & storer,const char * source) const25 void StickersManager::store_sticker(FileId file_id, bool in_sticker_set, StorerT &storer, const char *source) const {
26   auto it = stickers_.find(file_id);
27   LOG_CHECK(it != stickers_.end()) << file_id << ' ' << in_sticker_set << ' ' << source;
28   const Sticker *sticker = it->second.get();
29   bool has_sticker_set_access_hash = sticker->set_id.is_valid() && !in_sticker_set;
30   bool has_minithumbnail = !sticker->minithumbnail.empty();
31   BEGIN_STORE_FLAGS();
32   STORE_FLAG(sticker->is_mask);
33   STORE_FLAG(has_sticker_set_access_hash);
34   STORE_FLAG(in_sticker_set);
35   STORE_FLAG(sticker->is_animated);
36   STORE_FLAG(has_minithumbnail);
37   END_STORE_FLAGS();
38   if (!in_sticker_set) {
39     store(sticker->set_id.get(), storer);
40     if (has_sticker_set_access_hash) {
41       auto sticker_set = get_sticker_set(sticker->set_id);
42       CHECK(sticker_set != nullptr);
43       store(sticker_set->access_hash, storer);
44     }
45   }
46   store(sticker->alt, storer);
47   store(sticker->dimensions, storer);
48   store(sticker->s_thumbnail, storer);
49   store(sticker->m_thumbnail, storer);
50   store(file_id, storer);
51   if (sticker->is_mask) {
52     store(sticker->point, storer);
53     store(sticker->x_shift, storer);
54     store(sticker->y_shift, storer);
55     store(sticker->scale, storer);
56   }
57   if (has_minithumbnail) {
58     store(sticker->minithumbnail, storer);
59   }
60 }
61 
62 template <class ParserT>
parse_sticker(bool in_sticker_set,ParserT & parser)63 FileId StickersManager::parse_sticker(bool in_sticker_set, ParserT &parser) {
64   if (parser.get_error() != nullptr) {
65     return FileId();
66   }
67 
68   auto sticker = make_unique<Sticker>();
69   bool has_sticker_set_access_hash;
70   bool in_sticker_set_stored;
71   bool has_minithumbnail;
72   BEGIN_PARSE_FLAGS();
73   PARSE_FLAG(sticker->is_mask);
74   PARSE_FLAG(has_sticker_set_access_hash);
75   PARSE_FLAG(in_sticker_set_stored);
76   PARSE_FLAG(sticker->is_animated);
77   PARSE_FLAG(has_minithumbnail);
78   END_PARSE_FLAGS();
79   if (in_sticker_set_stored != in_sticker_set) {
80     Slice data = parser.template fetch_string_raw<Slice>(parser.get_left_len());
81     for (auto c : data) {
82       if (c != '\0') {
83         parser.set_error("Invalid sticker set is stored in the database");
84         break;
85       }
86     }
87     parser.set_error("Zero sticker set is stored in the database");
88     return FileId();
89   }
90   if (!in_sticker_set) {
91     int64 set_id;
92     parse(set_id, parser);
93     sticker->set_id = StickerSetId(set_id);
94     if (has_sticker_set_access_hash) {
95       int64 sticker_set_access_hash;
96       parse(sticker_set_access_hash, parser);
97       add_sticker_set(sticker->set_id, sticker_set_access_hash);
98     } else {
99       // backward compatibility
100       sticker->set_id = StickerSetId();
101     }
102   }
103   parse(sticker->alt, parser);
104   parse(sticker->dimensions, parser);
105   PhotoSize thumbnail;
106   parse(thumbnail, parser);
107   add_sticker_thumbnail(sticker.get(), thumbnail);
108   parse(thumbnail, parser);
109   add_sticker_thumbnail(sticker.get(), thumbnail);
110   parse(sticker->file_id, parser);
111   if (sticker->is_mask) {
112     parse(sticker->point, parser);
113     parse(sticker->x_shift, parser);
114     parse(sticker->y_shift, parser);
115     parse(sticker->scale, parser);
116   }
117   if (has_minithumbnail) {
118     parse(sticker->minithumbnail, parser);
119   }
120   if (parser.get_error() != nullptr || !sticker->file_id.is_valid()) {
121     return FileId();
122   }
123   return on_get_sticker(std::move(sticker), false);  // data in the database is always outdated
124 }
125 
126 template <class StorerT>
store_sticker_set(const StickerSet * sticker_set,bool with_stickers,StorerT & storer,const char * source) const127 void StickersManager::store_sticker_set(const StickerSet *sticker_set, bool with_stickers, StorerT &storer,
128                                         const char *source) const {
129   size_t stickers_limit = with_stickers ? sticker_set->sticker_ids.size() : 5;
130   bool is_full = sticker_set->sticker_ids.size() <= stickers_limit;
131   bool was_loaded = sticker_set->was_loaded && is_full;
132   bool is_loaded = sticker_set->is_loaded && is_full;
133   bool has_expires_at = !sticker_set->is_installed && sticker_set->expires_at != 0;
134   bool has_thumbnail = sticker_set->thumbnail.file_id.is_valid();
135   bool has_minithumbnail = !sticker_set->minithumbnail.empty();
136   BEGIN_STORE_FLAGS();
137   STORE_FLAG(sticker_set->is_inited);
138   STORE_FLAG(was_loaded);
139   STORE_FLAG(is_loaded);
140   STORE_FLAG(sticker_set->is_installed);
141   STORE_FLAG(sticker_set->is_archived);
142   STORE_FLAG(sticker_set->is_official);
143   STORE_FLAG(sticker_set->is_masks);
144   STORE_FLAG(sticker_set->is_viewed);
145   STORE_FLAG(has_expires_at);
146   STORE_FLAG(has_thumbnail);
147   STORE_FLAG(sticker_set->is_thumbnail_reloaded);
148   STORE_FLAG(sticker_set->is_animated);
149   STORE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded);
150   STORE_FLAG(has_minithumbnail);
151   END_STORE_FLAGS();
152   store(sticker_set->id.get(), storer);
153   store(sticker_set->access_hash, storer);
154   if (sticker_set->is_inited) {
155     store(sticker_set->title, storer);
156     store(sticker_set->short_name, storer);
157     store(sticker_set->sticker_count, storer);
158     store(sticker_set->hash, storer);
159     if (has_expires_at) {
160       store(sticker_set->expires_at, storer);
161     }
162     if (has_thumbnail) {
163       store(sticker_set->thumbnail, storer);
164     }
165     if (has_minithumbnail) {
166       store(sticker_set->minithumbnail, storer);
167     }
168 
169     auto stored_sticker_count = narrow_cast<uint32>(is_full ? sticker_set->sticker_ids.size() : stickers_limit);
170     store(stored_sticker_count, storer);
171     for (uint32 i = 0; i < stored_sticker_count; i++) {
172       auto sticker_id = sticker_set->sticker_ids[i];
173       store_sticker(sticker_id, true, storer, source);
174 
175       if (was_loaded) {
176         auto it = sticker_set->sticker_emojis_map_.find(sticker_id);
177         if (it != sticker_set->sticker_emojis_map_.end()) {
178           store(it->second, storer);
179         } else {
180           store(vector<string>(), storer);
181         }
182       }
183     }
184   }
185 }
186 
187 template <class ParserT>
parse_sticker_set(StickerSet * sticker_set,ParserT & parser)188 void StickersManager::parse_sticker_set(StickerSet *sticker_set, ParserT &parser) {
189   CHECK(sticker_set != nullptr);
190   CHECK(!sticker_set->was_loaded);
191   bool was_inited = sticker_set->is_inited;
192   bool is_installed;
193   bool is_archived;
194   bool is_official;
195   bool is_masks;
196   bool has_expires_at;
197   bool has_thumbnail;
198   bool is_animated;
199   bool has_minithumbnail;
200   BEGIN_PARSE_FLAGS();
201   PARSE_FLAG(sticker_set->is_inited);
202   PARSE_FLAG(sticker_set->was_loaded);
203   PARSE_FLAG(sticker_set->is_loaded);
204   PARSE_FLAG(is_installed);
205   PARSE_FLAG(is_archived);
206   PARSE_FLAG(is_official);
207   PARSE_FLAG(is_masks);
208   PARSE_FLAG(sticker_set->is_viewed);
209   PARSE_FLAG(has_expires_at);
210   PARSE_FLAG(has_thumbnail);
211   PARSE_FLAG(sticker_set->is_thumbnail_reloaded);
212   PARSE_FLAG(is_animated);
213   PARSE_FLAG(sticker_set->are_legacy_sticker_thumbnails_reloaded);
214   PARSE_FLAG(has_minithumbnail);
215   END_PARSE_FLAGS();
216   int64 sticker_set_id;
217   int64 access_hash;
218   parse(sticker_set_id, parser);
219   parse(access_hash, parser);
220   CHECK(sticker_set->id.get() == sticker_set_id);
221   if (sticker_set->access_hash != access_hash) {
222     LOG(ERROR) << "Access hash of " << sticker_set->id << " has changed from " << access_hash << " to "
223                << sticker_set->access_hash;
224   }
225 
226   if (sticker_set->is_inited) {
227     string title;
228     string short_name;
229     string minithumbnail;
230     PhotoSize thumbnail;
231     int32 sticker_count;
232     int32 hash;
233     int32 expires_at = 0;
234     parse(title, parser);
235     parse(short_name, parser);
236     parse(sticker_count, parser);
237     parse(hash, parser);
238     if (has_expires_at) {
239       parse(expires_at, parser);
240     }
241     if (has_thumbnail) {
242       parse(thumbnail, parser);
243     }
244     if (has_minithumbnail) {
245       parse(minithumbnail, parser);
246     }
247 
248     if (!was_inited) {
249       sticker_set->title = std::move(title);
250       sticker_set->short_name = std::move(short_name);
251       sticker_set->minithumbnail = std::move(minithumbnail);
252       sticker_set->thumbnail = std::move(thumbnail);
253       sticker_set->sticker_count = sticker_count;
254       sticker_set->hash = hash;
255       sticker_set->expires_at = expires_at;
256       sticker_set->is_official = is_official;
257       sticker_set->is_masks = is_masks;
258       sticker_set->is_animated = is_animated;
259 
260       short_name_to_sticker_set_id_.emplace(clean_username(sticker_set->short_name), sticker_set->id);
261       on_update_sticker_set(sticker_set, is_installed, is_archived, false, true);
262     } else {
263       if (sticker_set->title != title) {
264         LOG(INFO) << "Title of " << sticker_set->id << " has changed";
265       }
266       if (sticker_set->short_name != short_name) {
267         LOG(ERROR) << "Short name of " << sticker_set->id << " has changed from \"" << short_name << "\" to \""
268                    << sticker_set->short_name << "\"";
269       }
270       if (sticker_set->sticker_count != sticker_count || sticker_set->hash != hash) {
271         sticker_set->is_loaded = false;
272       }
273       if (sticker_set->is_animated != is_animated) {
274         LOG(ERROR) << "Is animated of " << sticker_set->id << " has changed from \"" << is_animated << "\" to \""
275                    << sticker_set->is_animated << "\"";
276       }
277       if (sticker_set->is_masks != is_masks) {
278         LOG(ERROR) << "Is masks of " << sticker_set->id << " has changed from \"" << is_masks << "\" to \""
279                    << sticker_set->is_masks << "\"";
280       }
281     }
282 
283     uint32 stored_sticker_count;
284     parse(stored_sticker_count, parser);
285     sticker_set->sticker_ids.clear();
286     if (sticker_set->was_loaded) {
287       sticker_set->emoji_stickers_map_.clear();
288       sticker_set->sticker_emojis_map_.clear();
289     }
290     for (uint32 i = 0; i < stored_sticker_count; i++) {
291       auto sticker_id = parse_sticker(true, parser);
292       if (parser.get_error() != nullptr) {
293         return;
294       }
295       if (!sticker_id.is_valid()) {
296         return parser.set_error("Receive invalid sticker in a sticker set");
297       }
298       sticker_set->sticker_ids.push_back(sticker_id);
299 
300       Sticker *sticker = get_sticker(sticker_id);
301       CHECK(sticker != nullptr);
302       if (sticker->set_id != sticker_set->id) {
303         LOG_IF(ERROR, sticker->set_id.is_valid()) << "Sticker " << sticker_id << " set_id has changed";
304         sticker->set_id = sticker_set->id;
305       }
306 
307       if (sticker_set->was_loaded) {
308         vector<string> emojis;
309         parse(emojis, parser);
310         for (auto &emoji : emojis) {
311           auto &sticker_ids = sticker_set->emoji_stickers_map_[remove_emoji_modifiers(emoji).str()];
312           if (sticker_ids.empty() || sticker_ids.back() != sticker_id) {
313             sticker_ids.push_back(sticker_id);
314           }
315         }
316         sticker_set->sticker_emojis_map_[sticker_id] = std::move(emojis);
317       }
318     }
319     if (expires_at > sticker_set->expires_at) {
320       sticker_set->expires_at = expires_at;
321     }
322 
323     if (!check_utf8(sticker_set->title)) {
324       return parser.set_error("Have invalid sticker set title");
325     }
326     if (!check_utf8(sticker_set->short_name)) {
327       return parser.set_error("Have invalid sticker set name");
328     }
329   }
330 }
331 
332 template <class StorerT>
store_sticker_set_id(StickerSetId sticker_set_id,StorerT & storer) const333 void StickersManager::store_sticker_set_id(StickerSetId sticker_set_id, StorerT &storer) const {
334   CHECK(sticker_set_id.is_valid());
335   const StickerSet *sticker_set = get_sticker_set(sticker_set_id);
336   CHECK(sticker_set != nullptr);
337   store(sticker_set_id.get(), storer);
338   store(sticker_set->access_hash, storer);
339 }
340 
341 template <class ParserT>
parse_sticker_set_id(StickerSetId & sticker_set_id,ParserT & parser)342 void StickersManager::parse_sticker_set_id(StickerSetId &sticker_set_id, ParserT &parser) {
343   int64 set_id;
344   parse(set_id, parser);
345   sticker_set_id = StickerSetId(set_id);
346   int64 sticker_set_access_hash;
347   parse(sticker_set_access_hash, parser);
348   add_sticker_set(sticker_set_id, sticker_set_access_hash);
349 }
350 
351 }  // namespace td
352