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