1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "storage/storage_account.h"
9
10 #include "storage/localstorage.h"
11 #include "storage/storage_domain.h"
12 #include "storage/storage_encryption.h"
13 #include "storage/storage_clear_legacy.h"
14 #include "storage/cache/storage_cache_types.h"
15 #include "storage/details/storage_file_utilities.h"
16 #include "storage/details/storage_settings_scheme.h"
17 #include "storage/serialize_common.h"
18 #include "storage/serialize_peer.h"
19 #include "storage/serialize_document.h"
20 #include "main/main_account.h"
21 #include "main/main_session.h"
22 #include "mtproto/mtproto_config.h"
23 #include "mtproto/mtproto_dc_options.h"
24 #include "mtproto/mtp_instance.h"
25 #include "history/history.h"
26 #include "core/application.h"
27 #include "core/file_location.h"
28 #include "data/stickers/data_stickers.h"
29 #include "data/data_session.h"
30 #include "data/data_document.h"
31 #include "data/data_user.h"
32 #include "data/data_drafts.h"
33 #include "export/export_settings.h"
34 #include "window/themes/window_theme.h"
35
36 namespace Storage {
37 namespace {
38
39 using namespace details;
40 using Database = Cache::Database;
41
42 constexpr auto kDelayedWriteTimeout = crl::time(1000);
43
44 constexpr auto kStickersVersionTag = quint32(-1);
45 constexpr auto kStickersSerializeVersion = 2;
46 constexpr auto kMaxSavedStickerSetsCount = 1000;
47 constexpr auto kDefaultStickerInstallDate = TimeId(1);
48
49 constexpr auto kSinglePeerTypeUserOld = qint32(1);
50 constexpr auto kSinglePeerTypeChatOld = qint32(2);
51 constexpr auto kSinglePeerTypeChannelOld = qint32(3);
52 constexpr auto kSinglePeerTypeUser = qint32(8 + 1);
53 constexpr auto kSinglePeerTypeChat = qint32(8 + 2);
54 constexpr auto kSinglePeerTypeChannel = qint32(8 + 3);
55 constexpr auto kSinglePeerTypeSelf = qint32(4);
56 constexpr auto kSinglePeerTypeEmpty = qint32(0);
57 constexpr auto kMultiDraftTagOld = quint64(0xFFFF'FFFF'FFFF'FF01ULL);
58 constexpr auto kMultiDraftCursorsTagOld = quint64(0xFFFF'FFFF'FFFF'FF02ULL);
59 constexpr auto kMultiDraftTag = quint64(0xFFFF'FFFF'FFFF'FF03ULL);
60 constexpr auto kMultiDraftCursorsTag = quint64(0xFFFF'FFFF'FFFF'FF04ULL);
61
62 enum { // Local Storage Keys
63 lskUserMap = 0x00,
64 lskDraft = 0x01, // data: PeerId peer
65 lskDraftPosition = 0x02, // data: PeerId peer
66 lskLegacyImages = 0x03, // legacy
67 lskLocations = 0x04, // no data
68 lskLegacyStickerImages = 0x05, // legacy
69 lskLegacyAudios = 0x06, // legacy
70 lskRecentStickersOld = 0x07, // no data
71 lskBackgroundOldOld = 0x08, // no data
72 lskUserSettings = 0x09, // no data
73 lskRecentHashtagsAndBots = 0x0a, // no data
74 lskStickersOld = 0x0b, // no data
75 lskSavedPeersOld = 0x0c, // no data
76 lskReportSpamStatusesOld = 0x0d, // no data
77 lskSavedGifsOld = 0x0e, // no data
78 lskSavedGifs = 0x0f, // no data
79 lskStickersKeys = 0x10, // no data
80 lskTrustedBots = 0x11, // no data
81 lskFavedStickers = 0x12, // no data
82 lskExportSettings = 0x13, // no data
83 lskBackgroundOld = 0x14, // no data
84 lskSelfSerialized = 0x15, // serialized self
85 lskMasksKeys = 0x16, // no data
86 };
87
EmptyMessageDraftSources()88 auto EmptyMessageDraftSources()
89 -> const base::flat_map<Data::DraftKey, MessageDraftSource> & {
90 static const auto result = base::flat_map<
91 Data::DraftKey,
92 MessageDraftSource>();
93 return result;
94 }
95
ComputeDataNameKey(const QString & dataName)96 [[nodiscard]] FileKey ComputeDataNameKey(const QString &dataName) {
97 // We dropped old test authorizations when migrated to multi auth.
98 //const auto testAddition = (cTestMode() ? qsl(":/test/") : QString());
99 const auto testAddition = QString();
100 const auto dataNameUtf8 = (dataName + testAddition).toUtf8();
101 FileKey dataNameHash[2] = { 0 };
102 hashMd5(dataNameUtf8.constData(), dataNameUtf8.size(), dataNameHash);
103 return dataNameHash[0];
104 }
105
BaseGlobalPath()106 [[nodiscard]] QString BaseGlobalPath() {
107 return cWorkingDir() + qsl("tdata/");
108 }
109
ComputeDatabasePath(const QString & dataName)110 [[nodiscard]] QString ComputeDatabasePath(const QString &dataName) {
111 return BaseGlobalPath()
112 + "user_" + dataName
113 // We dropped old test authorizations when migrated to multi auth.
114 //+ (cTestMode() ? "[test]" : "")
115 + '/';
116 }
117
LegacyTempDirectory()118 [[nodiscard]] QString LegacyTempDirectory() {
119 return cWorkingDir() + qsl("tdata/tdld/");
120 }
121
122 } // namespace
123
Account(not_null<Main::Account * > owner,const QString & dataName)124 Account::Account(not_null<Main::Account*> owner, const QString &dataName)
125 : _owner(owner)
126 , _dataName(dataName)
127 , _dataNameKey(ComputeDataNameKey(dataName))
128 , _basePath(BaseGlobalPath() + ToFilePart(_dataNameKey) + QChar('/'))
129 , _tempPath(BaseGlobalPath() + "temp_" + _dataName + QChar('/'))
130 , _databasePath(ComputeDatabasePath(dataName))
131 , _cacheTotalSizeLimit(Database::Settings().totalSizeLimit)
132 , _cacheBigFileTotalSizeLimit(Database::Settings().totalSizeLimit)
133 , _cacheTotalTimeLimit(Database::Settings().totalTimeLimit)
134 , _cacheBigFileTotalTimeLimit(Database::Settings().totalTimeLimit)
135 , _writeMapTimer([=] { writeMap(); })
__anon08530e130402null136 , _writeLocationsTimer([=] { writeLocations(); }) {
137 }
138
~Account()139 Account::~Account() {
140 if (_localKey && _mapChanged) {
141 writeMap();
142 }
143 }
144
tempDirectory() const145 QString Account::tempDirectory() const {
146 return _tempPath;
147 }
148
legacyStart(const QByteArray & passcode)149 StartResult Account::legacyStart(const QByteArray &passcode) {
150 const auto result = readMapWith(MTP::AuthKeyPtr(), passcode);
151 if (result == ReadMapResult::Failed) {
152 Assert(_localKey == nullptr);
153 } else if (result == ReadMapResult::IncorrectPasscode) {
154 return StartResult::IncorrectPasscodeLegacy;
155 }
156 clearLegacyFiles();
157 return StartResult::Success;
158 }
159
start(MTP::AuthKeyPtr localKey)160 std::unique_ptr<MTP::Config> Account::start(MTP::AuthKeyPtr localKey) {
161 Expects(localKey != nullptr);
162
163 _localKey = std::move(localKey);
164 readMapWith(_localKey);
165 clearLegacyFiles();
166 return readMtpConfig();
167 }
168
startAdded(MTP::AuthKeyPtr localKey)169 void Account::startAdded(MTP::AuthKeyPtr localKey) {
170 Expects(localKey != nullptr);
171
172 _localKey = std::move(localKey);
173 clearLegacyFiles();
174 }
175
clearLegacyFiles()176 void Account::clearLegacyFiles() {
177 const auto weak = base::make_weak(_owner.get());
178 ClearLegacyFiles(_basePath, [weak, this](
179 FnMut<void(base::flat_set<QString>&&)> then) {
180 crl::on_main(weak, [this, then = std::move(then)]() mutable {
181 then(collectGoodNames());
182 });
183 });
184 }
185
collectGoodNames() const186 base::flat_set<QString> Account::collectGoodNames() const {
187 const auto keys = {
188 _locationsKey,
189 _settingsKey,
190 _installedStickersKey,
191 _featuredStickersKey,
192 _recentStickersKey,
193 _favedStickersKey,
194 _archivedStickersKey,
195 _recentStickersKeyOld,
196 _savedGifsKey,
197 _legacyBackgroundKeyNight,
198 _legacyBackgroundKeyDay,
199 _recentHashtagsAndBotsKey,
200 _exportSettingsKey,
201 _trustedBotsKey,
202 _installedMasksKey,
203 _recentMasksKey,
204 _archivedMasksKey,
205 };
206 auto result = base::flat_set<QString>{
207 "map0",
208 "map1",
209 "maps",
210 "configs",
211 };
212 const auto push = [&](FileKey key) {
213 if (!key) {
214 return;
215 }
216 auto name = ToFilePart(key) + '0';
217 result.emplace(name);
218 name[name.size() - 1] = '1';
219 result.emplace(name);
220 name[name.size() - 1] = 's';
221 result.emplace(name);
222 };
223 for (const auto &[key, value] : _draftsMap) {
224 push(value);
225 }
226 for (const auto &[key, value] : _draftCursorsMap) {
227 push(value);
228 }
229 for (const auto &value : keys) {
230 push(value);
231 }
232 return result;
233 }
234
readMapWith(MTP::AuthKeyPtr localKey,const QByteArray & legacyPasscode)235 Account::ReadMapResult Account::readMapWith(
236 MTP::AuthKeyPtr localKey,
237 const QByteArray &legacyPasscode) {
238 auto ms = crl::now();
239
240 FileReadDescriptor mapData;
241 if (!ReadFile(mapData, qsl("map"), _basePath)) {
242 return ReadMapResult::Failed;
243 }
244 LOG(("App Info: reading map..."));
245
246 QByteArray legacySalt, legacyKeyEncrypted, mapEncrypted;
247 mapData.stream >> legacySalt >> legacyKeyEncrypted >> mapEncrypted;
248 if (!CheckStreamStatus(mapData.stream)) {
249 return ReadMapResult::Failed;
250 }
251 if (!localKey) {
252 if (legacySalt.size() != LocalEncryptSaltSize) {
253 LOG(("App Error: bad salt in map file, size: %1").arg(legacySalt.size()));
254 return ReadMapResult::Failed;
255 }
256 auto legacyPasscodeKey = CreateLegacyLocalKey(legacyPasscode, legacySalt);
257
258 EncryptedDescriptor keyData;
259 if (!DecryptLocal(keyData, legacyKeyEncrypted, legacyPasscodeKey)) {
260 LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password..."));
261 return ReadMapResult::IncorrectPasscode;
262 }
263 auto key = Serialize::read<MTP::AuthKey::Data>(keyData.stream);
264 if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) {
265 LOG(("App Error: could not read pass-protected key from map file"));
266 return ReadMapResult::Failed;
267 }
268 localKey = std::make_shared<MTP::AuthKey>(key);
269 }
270
271 EncryptedDescriptor map;
272 if (!DecryptLocal(map, mapEncrypted, localKey)) {
273 LOG(("App Error: could not decrypt map."));
274 return ReadMapResult::Failed;
275 }
276 LOG(("App Info: reading encrypted map..."));
277
278 QByteArray selfSerialized;
279 base::flat_map<PeerId, FileKey> draftsMap;
280 base::flat_map<PeerId, FileKey> draftCursorsMap;
281 base::flat_map<PeerId, bool> draftsNotReadMap;
282 quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedBotsKey = 0;
283 quint64 recentStickersKeyOld = 0;
284 quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0;
285 quint64 installedMasksKey = 0, recentMasksKey = 0, archivedMasksKey = 0;
286 quint64 savedGifsKey = 0;
287 quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0;
288 quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0;
289 while (!map.stream.atEnd()) {
290 quint32 keyType;
291 map.stream >> keyType;
292 switch (keyType) {
293 case lskDraft: {
294 quint32 count = 0;
295 map.stream >> count;
296 for (quint32 i = 0; i < count; ++i) {
297 FileKey key;
298 quint64 peerIdSerialized;
299 map.stream >> key >> peerIdSerialized;
300 const auto peerId = DeserializePeerId(peerIdSerialized);
301 draftsMap.emplace(peerId, key);
302 draftsNotReadMap.emplace(peerId, true);
303 }
304 } break;
305 case lskSelfSerialized: {
306 map.stream >> selfSerialized;
307 } break;
308 case lskDraftPosition: {
309 quint32 count = 0;
310 map.stream >> count;
311 for (quint32 i = 0; i < count; ++i) {
312 FileKey key;
313 quint64 peerIdSerialized;
314 map.stream >> key >> peerIdSerialized;
315 const auto peerId = DeserializePeerId(peerIdSerialized);
316 draftCursorsMap.emplace(peerId, key);
317 }
318 } break;
319 case lskLegacyImages:
320 case lskLegacyStickerImages:
321 case lskLegacyAudios: {
322 quint32 count = 0;
323 map.stream >> count;
324 for (quint32 i = 0; i < count; ++i) {
325 FileKey key;
326 quint64 first, second;
327 qint32 size;
328 map.stream >> key >> first >> second >> size;
329 // Just ignore the key, it will be removed as a leaked one.
330 }
331 } break;
332 case lskLocations: {
333 map.stream >> locationsKey;
334 } break;
335 case lskReportSpamStatusesOld: {
336 map.stream >> reportSpamStatusesKey;
337 ClearKey(reportSpamStatusesKey, _basePath);
338 } break;
339 case lskTrustedBots: {
340 map.stream >> trustedBotsKey;
341 } break;
342 case lskRecentStickersOld: {
343 map.stream >> recentStickersKeyOld;
344 } break;
345 case lskBackgroundOldOld: {
346 map.stream >> (Window::Theme::IsNightMode()
347 ? legacyBackgroundKeyNight
348 : legacyBackgroundKeyDay);
349 } break;
350 case lskBackgroundOld: {
351 map.stream >> legacyBackgroundKeyDay >> legacyBackgroundKeyNight;
352 } break;
353 case lskUserSettings: {
354 map.stream >> userSettingsKey;
355 } break;
356 case lskRecentHashtagsAndBots: {
357 map.stream >> recentHashtagsAndBotsKey;
358 } break;
359 case lskStickersOld: {
360 map.stream >> installedStickersKey;
361 } break;
362 case lskStickersKeys: {
363 map.stream >> installedStickersKey >> featuredStickersKey >> recentStickersKey >> archivedStickersKey;
364 } break;
365 case lskFavedStickers: {
366 map.stream >> favedStickersKey;
367 } break;
368 case lskSavedGifsOld: {
369 quint64 key;
370 map.stream >> key;
371 } break;
372 case lskSavedGifs: {
373 map.stream >> savedGifsKey;
374 } break;
375 case lskSavedPeersOld: {
376 quint64 key;
377 map.stream >> key;
378 } break;
379 case lskExportSettings: {
380 map.stream >> exportSettingsKey;
381 } break;
382 case lskMasksKeys: {
383 map.stream
384 >> installedMasksKey
385 >> recentMasksKey
386 >> archivedMasksKey;
387 } break;
388 default:
389 LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
390 return ReadMapResult::Failed;
391 }
392 if (!CheckStreamStatus(map.stream)) {
393 return ReadMapResult::Failed;
394 }
395 }
396
397 _localKey = std::move(localKey);
398
399 _draftsMap = draftsMap;
400 _draftCursorsMap = draftCursorsMap;
401 _draftsNotReadMap = draftsNotReadMap;
402
403 _locationsKey = locationsKey;
404 _trustedBotsKey = trustedBotsKey;
405 _recentStickersKeyOld = recentStickersKeyOld;
406 _installedStickersKey = installedStickersKey;
407 _featuredStickersKey = featuredStickersKey;
408 _recentStickersKey = recentStickersKey;
409 _favedStickersKey = favedStickersKey;
410 _archivedStickersKey = archivedStickersKey;
411 _savedGifsKey = savedGifsKey;
412 _installedMasksKey = installedMasksKey;
413 _recentMasksKey = recentMasksKey;
414 _archivedMasksKey = archivedMasksKey;
415 _legacyBackgroundKeyDay = legacyBackgroundKeyDay;
416 _legacyBackgroundKeyNight = legacyBackgroundKeyNight;
417 _settingsKey = userSettingsKey;
418 _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
419 _exportSettingsKey = exportSettingsKey;
420 _oldMapVersion = mapData.version;
421
422 if (_oldMapVersion < AppVersion) {
423 writeMapDelayed();
424 } else {
425 _mapChanged = false;
426 }
427
428 if (_locationsKey) {
429 readLocations();
430 }
431 if (_legacyBackgroundKeyDay || _legacyBackgroundKeyNight) {
432 Local::moveLegacyBackground(
433 _basePath,
434 _localKey,
435 _legacyBackgroundKeyDay,
436 _legacyBackgroundKeyNight);
437 }
438
439 auto stored = readSessionSettings();
440 readMtpData();
441
442 DEBUG_LOG(("selfSerialized set: %1").arg(selfSerialized.size()));
443 _owner->setSessionFromStorage(
444 std::move(stored),
445 std::move(selfSerialized),
446 _oldMapVersion);
447
448 LOG(("Map read time: %1").arg(crl::now() - ms));
449
450 return ReadMapResult::Success;
451 }
452
writeMapDelayed()453 void Account::writeMapDelayed() {
454 _mapChanged = true;
455 _writeMapTimer.callOnce(kDelayedWriteTimeout);
456 }
457
writeMapQueued()458 void Account::writeMapQueued() {
459 _mapChanged = true;
460 crl::on_main(_owner, [=] {
461 writeMap();
462 });
463 }
464
writeMap()465 void Account::writeMap() {
466 Expects(_localKey != nullptr);
467
468 _writeMapTimer.cancel();
469 if (!_mapChanged) {
470 return;
471 }
472 _mapChanged = false;
473
474 if (!QDir().exists(_basePath)) {
475 QDir().mkpath(_basePath);
476 }
477
478 FileWriteDescriptor map(u"map"_q, _basePath);
479 map.writeData(QByteArray());
480 map.writeData(QByteArray());
481
482 uint32 mapSize = 0;
483 const auto self = [&] {
484 if (!_owner->sessionExists()) {
485 DEBUG_LOG(("AuthSelf Warning: Session does not exist."));
486 return QByteArray();
487 }
488 const auto self = _owner->session().user();
489 if (self->phone().isEmpty()) {
490 DEBUG_LOG(("AuthSelf Error: Phone is empty."));
491 return QByteArray();
492 }
493 auto result = QByteArray();
494 result.reserve(Serialize::peerSize(self)
495 + Serialize::stringSize(self->about()));
496 {
497 QBuffer buffer(&result);
498 buffer.open(QIODevice::WriteOnly);
499 QDataStream stream(&buffer);
500 Serialize::writePeer(stream, self);
501 stream << self->about();
502 }
503 return result;
504 }();
505 if (!self.isEmpty()) mapSize += sizeof(quint32) + Serialize::bytearraySize(self);
506 if (!_draftsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2;
507 if (!_draftCursorsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2;
508 if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
509 if (_trustedBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
510 if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64);
511 if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) {
512 mapSize += sizeof(quint32) + 4 * sizeof(quint64);
513 }
514 if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
515 if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64);
516 if (_settingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
517 if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
518 if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
519 if (_installedMasksKey || _recentMasksKey || _archivedMasksKey) {
520 mapSize += sizeof(quint32) + 3 * sizeof(quint64);
521 }
522
523 EncryptedDescriptor mapData(mapSize);
524 if (!self.isEmpty()) {
525 mapData.stream << quint32(lskSelfSerialized) << self;
526 }
527 if (!_draftsMap.empty()) {
528 mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
529 for (const auto &[key, value] : _draftsMap) {
530 mapData.stream << quint64(value) << SerializePeerId(key);
531 }
532 }
533 if (!_draftCursorsMap.empty()) {
534 mapData.stream << quint32(lskDraftPosition) << quint32(_draftCursorsMap.size());
535 for (const auto &[key, value] : _draftCursorsMap) {
536 mapData.stream << quint64(value) << SerializePeerId(key);
537 }
538 }
539 if (_locationsKey) {
540 mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
541 }
542 if (_trustedBotsKey) {
543 mapData.stream << quint32(lskTrustedBots) << quint64(_trustedBotsKey);
544 }
545 if (_recentStickersKeyOld) {
546 mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld);
547 }
548 if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) {
549 mapData.stream << quint32(lskStickersKeys);
550 mapData.stream << quint64(_installedStickersKey) << quint64(_featuredStickersKey) << quint64(_recentStickersKey) << quint64(_archivedStickersKey);
551 }
552 if (_favedStickersKey) {
553 mapData.stream << quint32(lskFavedStickers) << quint64(_favedStickersKey);
554 }
555 if (_savedGifsKey) {
556 mapData.stream << quint32(lskSavedGifs) << quint64(_savedGifsKey);
557 }
558 if (_settingsKey) {
559 mapData.stream << quint32(lskUserSettings) << quint64(_settingsKey);
560 }
561 if (_recentHashtagsAndBotsKey) {
562 mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey);
563 }
564 if (_exportSettingsKey) {
565 mapData.stream << quint32(lskExportSettings) << quint64(_exportSettingsKey);
566 }
567 if (_installedMasksKey || _recentMasksKey || _archivedMasksKey) {
568 mapData.stream << quint32(lskMasksKeys);
569 mapData.stream
570 << quint64(_installedMasksKey)
571 << quint64(_recentMasksKey)
572 << quint64(_archivedMasksKey);
573 }
574 map.writeEncrypted(mapData, _localKey);
575
576 _mapChanged = false;
577 }
578
reset()579 void Account::reset() {
580 auto names = collectGoodNames();
581 _draftsMap.clear();
582 _draftCursorsMap.clear();
583 _draftsNotReadMap.clear();
584 _locationsKey = _trustedBotsKey = 0;
585 _recentStickersKeyOld = 0;
586 _installedStickersKey = 0;
587 _featuredStickersKey = 0;
588 _recentStickersKey = 0;
589 _favedStickersKey = 0;
590 _archivedStickersKey = 0;
591 _savedGifsKey = 0;
592 _installedMasksKey = 0;
593 _recentMasksKey = 0;
594 _archivedMasksKey = 0;
595 _legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0;
596 _settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0;
597 _oldMapVersion = 0;
598 _fileLocations.clear();
599 _fileLocationPairs.clear();
600 _fileLocationAliases.clear();
601 _cacheTotalSizeLimit = Database::Settings().totalSizeLimit;
602 _cacheTotalTimeLimit = Database::Settings().totalTimeLimit;
603 _cacheBigFileTotalSizeLimit = Database::Settings().totalSizeLimit;
604 _cacheBigFileTotalTimeLimit = Database::Settings().totalTimeLimit;
605 _mapChanged = true;
606 writeMap();
607 writeMtpData();
608
609 crl::async([base = _basePath, temp = _tempPath, names = std::move(names)] {
610 for (const auto &name : names) {
611 if (!name.endsWith(qstr("map0"))
612 && !name.endsWith(qstr("map1"))
613 && !name.endsWith(qstr("maps"))
614 && !name.endsWith(qstr("configs"))) {
615 QFile::remove(base + name);
616 }
617 }
618 QDir(LegacyTempDirectory()).removeRecursively();
619 QDir(temp).removeRecursively();
620 });
621
622 Local::sync();
623 }
624
writeLocations()625 void Account::writeLocations() {
626 _writeLocationsTimer.cancel();
627 if (!_locationsChanged) {
628 return;
629 }
630 _locationsChanged = false;
631
632 if (_fileLocations.isEmpty()) {
633 if (_locationsKey) {
634 ClearKey(_locationsKey, _basePath);
635 _locationsKey = 0;
636 writeMapDelayed();
637 }
638 } else {
639 if (!_locationsKey) {
640 _locationsKey = GenerateKey(_basePath);
641 writeMapQueued();
642 }
643 quint32 size = 0;
644 for (auto i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) {
645 // location + type + namelen + name
646 size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name());
647 if (AppVersion > 9013) {
648 // bookmark
649 size += Serialize::bytearraySize(i.value().bookmark());
650 }
651 // date + size
652 size += Serialize::dateTimeSize() + sizeof(quint32);
653 }
654
655 //end mark
656 size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString());
657 if (AppVersion > 9013) {
658 size += Serialize::bytearraySize(QByteArray());
659 }
660 size += Serialize::dateTimeSize() + sizeof(quint32);
661
662 size += sizeof(quint32); // aliases count
663 for (auto i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) {
664 // alias + location
665 size += sizeof(quint64) * 2 + sizeof(quint64) * 2;
666 }
667
668 EncryptedDescriptor data(size);
669 auto legacyTypeField = 0;
670 for (auto i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) {
671 data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(legacyTypeField) << i.value().name();
672 if (AppVersion > 9013) {
673 data.stream << i.value().bookmark();
674 }
675 data.stream << i.value().modified << quint32(i.value().size);
676 }
677
678 data.stream << quint64(0) << quint64(0) << quint32(0) << QString();
679 if (AppVersion > 9013) {
680 data.stream << QByteArray();
681 }
682 data.stream << QDateTime::currentDateTime() << quint32(0);
683
684 data.stream << quint32(_fileLocationAliases.size());
685 for (auto i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) {
686 data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second);
687 }
688
689 FileWriteDescriptor file(_locationsKey, _basePath);
690 file.writeEncrypted(data, _localKey);
691 }
692 }
693
writeLocationsQueued()694 void Account::writeLocationsQueued() {
695 _locationsChanged = true;
696 crl::on_main(_owner, [=] {
697 writeLocations();
698 });
699 }
700
writeLocationsDelayed()701 void Account::writeLocationsDelayed() {
702 _locationsChanged = true;
703 _writeLocationsTimer.callOnce(kDelayedWriteTimeout);
704 }
705
readLocations()706 void Account::readLocations() {
707 FileReadDescriptor locations;
708 if (!ReadEncryptedFile(locations, _locationsKey, _basePath, _localKey)) {
709 ClearKey(_locationsKey, _basePath);
710 _locationsKey = 0;
711 writeMapDelayed();
712 return;
713 }
714
715 bool endMarkFound = false;
716 while (!locations.stream.atEnd()) {
717 quint64 first, second;
718 QByteArray bookmark;
719 Core::FileLocation loc;
720 quint32 legacyTypeField = 0;
721 locations.stream >> first >> second >> legacyTypeField >> loc.fname;
722 if (locations.version > 9013) {
723 locations.stream >> bookmark;
724 }
725 locations.stream >> loc.modified >> loc.size;
726 loc.setBookmark(bookmark);
727
728 if (!first && !second && !legacyTypeField && loc.fname.isEmpty() && !loc.size) { // end mark
729 endMarkFound = true;
730 break;
731 }
732
733 MediaKey key(first, second);
734
735 _fileLocations.insert(key, loc);
736 if (!loc.inMediaCache()) {
737 _fileLocationPairs.insert(loc.fname, { key, loc });
738 }
739 }
740
741 if (endMarkFound) {
742 quint32 cnt;
743 locations.stream >> cnt;
744 for (quint32 i = 0; i < cnt; ++i) {
745 quint64 kfirst, ksecond, vfirst, vsecond;
746 locations.stream >> kfirst >> ksecond >> vfirst >> vsecond;
747 _fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond));
748 }
749
750 if (!locations.stream.atEnd()) {
751 quint32 webLocationsCount;
752 locations.stream >> webLocationsCount;
753 for (quint32 i = 0; i < webLocationsCount; ++i) {
754 QString url;
755 quint64 key;
756 qint32 size;
757 locations.stream >> url >> key >> size;
758 ClearKey(key, _basePath);
759 }
760 }
761 }
762 }
763
writeSessionSettings()764 void Account::writeSessionSettings() {
765 writeSessionSettings(nullptr);
766 }
767
writeSessionSettings(Main::SessionSettings * stored)768 void Account::writeSessionSettings(Main::SessionSettings *stored) {
769 if (_readingUserSettings) {
770 LOG(("App Error: attempt to write settings while reading them!"));
771 return;
772 }
773 LOG(("App Info: writing encrypted user settings..."));
774
775 if (!_settingsKey) {
776 _settingsKey = GenerateKey(_basePath);
777 writeMapQueued();
778 }
779
780 auto userDataInstance = stored
781 ? stored
782 : _owner->getSessionSettings();
783 auto userData = userDataInstance
784 ? userDataInstance->serialize()
785 : QByteArray();
786
787 auto recentStickers = cRecentStickersPreload();
788 if (recentStickers.isEmpty() && _owner->sessionExists()) {
789 const auto &stickers = _owner->session().data().stickers();
790 recentStickers.reserve(stickers.getRecentPack().size());
791 for (const auto &pair : std::as_const(stickers.getRecentPack())) {
792 recentStickers.push_back(qMakePair(pair.first->id, pair.second));
793 }
794 }
795
796 uint32 size = 24 * (sizeof(quint32) + sizeof(qint32));
797 size += sizeof(quint32);
798 size += sizeof(quint32) + sizeof(qint32) + recentStickers.size() * (sizeof(uint64) + sizeof(ushort));
799 size += sizeof(quint32) + 3 * sizeof(qint32);
800 size += sizeof(quint32) + 2 * sizeof(qint32);
801 size += sizeof(quint32) + sizeof(qint64) + sizeof(qint32);
802 if (!userData.isEmpty()) {
803 size += sizeof(quint32) + Serialize::bytearraySize(userData);
804 }
805
806 EncryptedDescriptor data(size);
807 data.stream << quint32(dbiUseExternalVideoPlayer) << qint32(cUseExternalVideoPlayer());
808 data.stream << quint32(dbiCacheSettings) << qint64(_cacheTotalSizeLimit) << qint32(_cacheTotalTimeLimit) << qint64(_cacheBigFileTotalSizeLimit) << qint32(_cacheBigFileTotalTimeLimit);
809 if (!userData.isEmpty()) {
810 data.stream << quint32(dbiSessionSettings) << userData;
811 }
812 data.stream << quint32(dbiRecentStickers) << recentStickers;
813
814 FileWriteDescriptor file(_settingsKey, _basePath);
815 file.writeEncrypted(data, _localKey);
816 }
817
prepareReadSettingsContext() const818 ReadSettingsContext Account::prepareReadSettingsContext() const {
819 return ReadSettingsContext{
820 .legacyHasCustomDayBackground = (_legacyBackgroundKeyDay != 0)
821 };
822 }
823
readSessionSettings()824 std::unique_ptr<Main::SessionSettings> Account::readSessionSettings() {
825 ReadSettingsContext context;
826 FileReadDescriptor userSettings;
827 if (!ReadEncryptedFile(userSettings, _settingsKey, _basePath, _localKey)) {
828 LOG(("App Info: could not read encrypted user settings..."));
829
830 Local::readOldUserSettings(true, context);
831 auto result = applyReadContext(std::move(context));
832
833 writeSessionSettings(result.get());
834
835 return result;
836 }
837
838 LOG(("App Info: reading encrypted user settings..."));
839 _readingUserSettings = true;
840 while (!userSettings.stream.atEnd()) {
841 quint32 blockId;
842 userSettings.stream >> blockId;
843 if (!CheckStreamStatus(userSettings.stream)) {
844 _readingUserSettings = false;
845 writeSessionSettings();
846 return nullptr;
847 }
848
849 if (!ReadSetting(blockId, userSettings.stream, userSettings.version, context)) {
850 _readingUserSettings = false;
851 writeSessionSettings();
852 return nullptr;
853 }
854 }
855 _readingUserSettings = false;
856 LOG(("App Info: encrypted user settings read."));
857
858 auto result = applyReadContext(std::move(context));
859 if (context.legacyRead) {
860 writeSessionSettings(result.get());
861 }
862 return result;
863 }
864
applyReadContext(ReadSettingsContext && context)865 std::unique_ptr<Main::SessionSettings> Account::applyReadContext(
866 ReadSettingsContext &&context) {
867 ApplyReadFallbackConfig(context);
868
869 if (context.cacheTotalSizeLimit) {
870 _cacheTotalSizeLimit = context.cacheTotalSizeLimit;
871 _cacheTotalTimeLimit = context.cacheTotalTimeLimit;
872 _cacheBigFileTotalSizeLimit = context.cacheBigFileTotalSizeLimit;
873 _cacheBigFileTotalTimeLimit = context.cacheBigFileTotalTimeLimit;
874
875 const auto &normal = Database::Settings();
876 Assert(_cacheTotalSizeLimit > normal.maxDataSize);
877 Assert(_cacheBigFileTotalSizeLimit > normal.maxDataSize);
878 }
879
880 if (!context.mtpAuthorization.isEmpty()) {
881 _owner->setMtpAuthorization(context.mtpAuthorization);
882 } else {
883 for (auto &key : context.mtpLegacyKeys) {
884 _owner->setLegacyMtpKey(std::move(key));
885 }
886 if (context.mtpLegacyMainDcId) {
887 _owner->setMtpMainDcId(context.mtpLegacyMainDcId);
888 _owner->setSessionUserId(context.mtpLegacyUserId);
889 }
890 }
891
892 if (context.tileRead) {
893 Window::Theme::Background()->setTileDayValue(context.tileDay);
894 Window::Theme::Background()->setTileNightValue(context.tileNight);
895 }
896
897 return std::move(context.sessionSettingsStorage);
898 }
899
writeMtpData()900 void Account::writeMtpData() {
901 Expects(_localKey != nullptr);
902
903 const auto serialized = _owner->serializeMtpAuthorization();
904 const auto size = sizeof(quint32) + Serialize::bytearraySize(serialized);
905
906 FileWriteDescriptor mtp(ToFilePart(_dataNameKey), BaseGlobalPath());
907 EncryptedDescriptor data(size);
908 data.stream << quint32(dbiMtpAuthorization) << serialized;
909 mtp.writeEncrypted(data, _localKey);
910 }
911
readMtpData()912 void Account::readMtpData() {
913 auto context = prepareReadSettingsContext();
914
915 FileReadDescriptor mtp;
916 if (!ReadEncryptedFile(mtp, ToFilePart(_dataNameKey), BaseGlobalPath(), _localKey)) {
917 if (_localKey) {
918 Local::readOldMtpData(true, context);
919 applyReadContext(std::move(context));
920 writeMtpData();
921 }
922 return;
923 }
924
925 LOG(("App Info: reading encrypted mtp data..."));
926 while (!mtp.stream.atEnd()) {
927 quint32 blockId;
928 mtp.stream >> blockId;
929 if (!CheckStreamStatus(mtp.stream)) {
930 return writeMtpData();
931 }
932
933 if (!ReadSetting(blockId, mtp.stream, mtp.version, context)) {
934 return writeMtpData();
935 }
936 }
937 applyReadContext(std::move(context));
938 }
939
writeMtpConfig()940 void Account::writeMtpConfig() {
941 Expects(_localKey != nullptr);
942
943 const auto serialized = _owner->mtp().config().serialize();
944 const auto size = Serialize::bytearraySize(serialized);
945
946 FileWriteDescriptor file(u"config"_q, _basePath);
947 EncryptedDescriptor data(size);
948 data.stream << serialized;
949 file.writeEncrypted(data, _localKey);
950 }
951
readMtpConfig()952 std::unique_ptr<MTP::Config> Account::readMtpConfig() {
953 Expects(_localKey != nullptr);
954
955 FileReadDescriptor file;
956 if (!ReadEncryptedFile(file, "config", _basePath, _localKey)) {
957 return nullptr;
958 }
959
960 LOG(("App Info: reading encrypted mtp config..."));
961 auto serialized = QByteArray();
962 file.stream >> serialized;
963 if (!CheckStreamStatus(file.stream)) {
964 return nullptr;
965 }
966 return MTP::Config::FromSerialized(serialized);
967 }
968
969 template <typename Callback>
EnumerateDrafts(const Data::HistoryDrafts & map,Data::Draft * cloudDraft,bool supportMode,const base::flat_map<Data::DraftKey,MessageDraftSource> & sources,Callback && callback)970 void EnumerateDrafts(
971 const Data::HistoryDrafts &map,
972 Data::Draft *cloudDraft,
973 bool supportMode,
974 const base::flat_map<Data::DraftKey, MessageDraftSource> &sources,
975 Callback &&callback) {
976 for (const auto &[key, draft] : map) {
977 if (key == Data::DraftKey::Cloud() || sources.contains(key)) {
978 continue;
979 } else if (key == Data::DraftKey::Local()
980 && !supportMode
981 && Data::draftsAreEqual(draft.get(), cloudDraft)) {
982 continue;
983 }
984 callback(
985 key,
986 draft->msgId,
987 draft->textWithTags,
988 draft->previewState,
989 draft->cursor);
990 }
991 for (const auto &[key, source] : sources) {
992 const auto draft = source.draft();
993 const auto cursor = source.cursor();
994 if (draft.msgId
995 || !draft.textWithTags.text.isEmpty()
996 || cursor != MessageCursor()) {
997 callback(
998 key,
999 draft.msgId,
1000 draft.textWithTags,
1001 draft.previewState,
1002 cursor);
1003 }
1004 }
1005 }
1006
registerDraftSource(not_null<History * > history,Data::DraftKey key,MessageDraftSource source)1007 void Account::registerDraftSource(
1008 not_null<History*> history,
1009 Data::DraftKey key,
1010 MessageDraftSource source) {
1011 Expects(source.draft != nullptr);
1012 Expects(source.cursor != nullptr);
1013
1014 _draftSources[history][key] = std::move(source);
1015 }
1016
unregisterDraftSource(not_null<History * > history,Data::DraftKey key)1017 void Account::unregisterDraftSource(
1018 not_null<History*> history,
1019 Data::DraftKey key) {
1020 const auto i = _draftSources.find(history);
1021 if (i != _draftSources.end()) {
1022 i->second.remove(key);
1023 if (i->second.empty()) {
1024 _draftSources.erase(i);
1025 }
1026 }
1027 }
1028
writeDrafts(not_null<History * > history)1029 void Account::writeDrafts(not_null<History*> history) {
1030 const auto peerId = history->peer->id;
1031 const auto &map = history->draftsMap();
1032 const auto cloudIt = map.find(Data::DraftKey::Cloud());
1033 const auto cloudDraft = (cloudIt != end(map))
1034 ? cloudIt->second.get()
1035 : nullptr;
1036 const auto supportMode = _owner->session().supportMode();
1037 const auto sourcesIt = _draftSources.find(history);
1038 const auto &sources = (sourcesIt != _draftSources.end())
1039 ? sourcesIt->second
1040 : EmptyMessageDraftSources();
1041 auto count = 0;
1042 EnumerateDrafts(
1043 map,
1044 cloudDraft,
1045 supportMode,
1046 sources,
1047 [&](auto&&...) { ++count; });
1048 if (!count) {
1049 auto i = _draftsMap.find(peerId);
1050 if (i != _draftsMap.cend()) {
1051 ClearKey(i->second, _basePath);
1052 _draftsMap.erase(i);
1053 writeMapDelayed();
1054 }
1055
1056 _draftsNotReadMap.remove(peerId);
1057 return;
1058 }
1059
1060 auto i = _draftsMap.find(peerId);
1061 if (i == _draftsMap.cend()) {
1062 i = _draftsMap.emplace(peerId, GenerateKey(_basePath)).first;
1063 writeMapQueued();
1064 }
1065
1066 auto size = int(sizeof(quint64) * 2 + sizeof(quint32));
1067 const auto sizeCallback = [&](
1068 auto&&, // key
1069 MsgId, // msgId
1070 const TextWithTags &text,
1071 Data::PreviewState,
1072 auto&&) { // cursor
1073 size += sizeof(qint64) // key
1074 + Serialize::stringSize(text.text)
1075 + sizeof(qint64) + TextUtilities::SerializeTagsSize(text.tags)
1076 + sizeof(qint64) + sizeof(qint32); // msgId, previewState
1077 };
1078 EnumerateDrafts(
1079 map,
1080 cloudDraft,
1081 supportMode,
1082 sources,
1083 sizeCallback);
1084
1085 EncryptedDescriptor data(size);
1086 data.stream
1087 << quint64(kMultiDraftTag)
1088 << SerializePeerId(peerId)
1089 << quint32(count);
1090
1091 const auto writeCallback = [&](
1092 const Data::DraftKey &key,
1093 MsgId msgId,
1094 const TextWithTags &text,
1095 Data::PreviewState previewState,
1096 auto&&) { // cursor
1097 data.stream
1098 << key.serialize()
1099 << text.text
1100 << TextUtilities::SerializeTags(text.tags)
1101 << qint64(msgId.bare)
1102 << qint32(previewState);
1103 };
1104 EnumerateDrafts(
1105 map,
1106 cloudDraft,
1107 supportMode,
1108 sources,
1109 writeCallback);
1110
1111 FileWriteDescriptor file(i->second, _basePath);
1112 file.writeEncrypted(data, _localKey);
1113
1114 _draftsNotReadMap.remove(peerId);
1115 }
1116
writeDraftCursors(not_null<History * > history)1117 void Account::writeDraftCursors(not_null<History*> history) {
1118 const auto peerId = history->peer->id;
1119 const auto &map = history->draftsMap();
1120 const auto cloudIt = map.find(Data::DraftKey::Cloud());
1121 const auto cloudDraft = (cloudIt != end(map))
1122 ? cloudIt->second.get()
1123 : nullptr;
1124 const auto supportMode = _owner->session().supportMode();
1125 const auto sourcesIt = _draftSources.find(history);
1126 const auto &sources = (sourcesIt != _draftSources.end())
1127 ? sourcesIt->second
1128 : EmptyMessageDraftSources();
1129 auto count = 0;
1130 EnumerateDrafts(
1131 map,
1132 cloudDraft,
1133 supportMode,
1134 sources,
1135 [&](auto&&...) { ++count; });
1136 if (!count) {
1137 clearDraftCursors(peerId);
1138 return;
1139 }
1140 auto i = _draftCursorsMap.find(peerId);
1141 if (i == _draftCursorsMap.cend()) {
1142 i = _draftCursorsMap.emplace(peerId, GenerateKey(_basePath)).first;
1143 writeMapQueued();
1144 }
1145
1146 auto size = int(sizeof(quint64) * 2
1147 + sizeof(quint32)
1148 + (sizeof(qint64) + sizeof(qint32) * 3) * count);
1149
1150 EncryptedDescriptor data(size);
1151 data.stream
1152 << quint64(kMultiDraftCursorsTag)
1153 << SerializePeerId(peerId)
1154 << quint32(count);
1155
1156 const auto writeCallback = [&](
1157 const Data::DraftKey &key,
1158 MsgId, // msgId
1159 auto&&, // text
1160 Data::PreviewState,
1161 const MessageCursor &cursor) { // cursor
1162 data.stream
1163 << key.serialize()
1164 << qint32(cursor.position)
1165 << qint32(cursor.anchor)
1166 << qint32(cursor.scroll);
1167 };
1168 EnumerateDrafts(
1169 map,
1170 cloudDraft,
1171 supportMode,
1172 sources,
1173 writeCallback);
1174
1175 FileWriteDescriptor file(i->second, _basePath);
1176 file.writeEncrypted(data, _localKey);
1177 }
1178
clearDraftCursors(PeerId peerId)1179 void Account::clearDraftCursors(PeerId peerId) {
1180 const auto i = _draftCursorsMap.find(peerId);
1181 if (i != _draftCursorsMap.cend()) {
1182 ClearKey(i->second, _basePath);
1183 _draftCursorsMap.erase(i);
1184 writeMapDelayed();
1185 }
1186 }
1187
readDraftCursors(PeerId peerId,Data::HistoryDrafts & map)1188 void Account::readDraftCursors(PeerId peerId, Data::HistoryDrafts &map) {
1189 const auto j = _draftCursorsMap.find(peerId);
1190 if (j == _draftCursorsMap.cend()) {
1191 return;
1192 }
1193
1194 FileReadDescriptor draft;
1195 if (!ReadEncryptedFile(draft, j->second, _basePath, _localKey)) {
1196 clearDraftCursors(peerId);
1197 return;
1198 }
1199 quint64 tag = 0;
1200 draft.stream >> tag;
1201 if (tag != kMultiDraftCursorsTag
1202 && tag != kMultiDraftCursorsTagOld
1203 && tag != kMultiDraftTagOld) {
1204 readDraftCursorsLegacy(peerId, draft, tag, map);
1205 return;
1206 }
1207 quint64 draftPeerSerialized = 0;
1208 quint32 count = 0;
1209 draft.stream >> draftPeerSerialized >> count;
1210 const auto draftPeer = DeserializePeerId(draftPeerSerialized);
1211 if (!count || count > 1000 || draftPeer != peerId) {
1212 clearDraftCursors(peerId);
1213 return;
1214 }
1215 const auto keysWritten = (tag == kMultiDraftCursorsTag);
1216 const auto keysOld = (tag == kMultiDraftCursorsTagOld);
1217 for (auto i = 0; i != count; ++i) {
1218 qint64 keyValue = 0;
1219 qint32 keyValueOld = 0;
1220 if (keysWritten) {
1221 draft.stream >> keyValue;
1222 } else if (keysOld) {
1223 draft.stream >> keyValueOld;
1224 }
1225 const auto key = keysWritten
1226 ? Data::DraftKey::FromSerialized(keyValue)
1227 : keysOld
1228 ? Data::DraftKey::FromSerializedOld(keyValueOld)
1229 : Data::DraftKey::Local();
1230 qint32 position = 0, anchor = 0, scroll = QFIXED_MAX;
1231 draft.stream >> position >> anchor >> scroll;
1232 if (const auto i = map.find(key); i != end(map)) {
1233 i->second->cursor = MessageCursor(position, anchor, scroll);
1234 }
1235 }
1236 }
1237
readDraftCursorsLegacy(PeerId peerId,details::FileReadDescriptor & draft,quint64 draftPeerSerialized,Data::HistoryDrafts & map)1238 void Account::readDraftCursorsLegacy(
1239 PeerId peerId,
1240 details::FileReadDescriptor &draft,
1241 quint64 draftPeerSerialized,
1242 Data::HistoryDrafts &map) {
1243 qint32 localPosition = 0, localAnchor = 0, localScroll = QFIXED_MAX;
1244 qint32 editPosition = 0, editAnchor = 0, editScroll = QFIXED_MAX;
1245 draft.stream >> localPosition >> localAnchor >> localScroll;
1246 if (!draft.stream.atEnd()) {
1247 draft.stream >> editPosition >> editAnchor >> editScroll;
1248 }
1249
1250 const auto draftPeer = DeserializePeerId(draftPeerSerialized);
1251 if (draftPeer != peerId) {
1252 clearDraftCursors(peerId);
1253 return;
1254 }
1255
1256 if (const auto i = map.find(Data::DraftKey::Local()); i != end(map)) {
1257 i->second->cursor = MessageCursor(
1258 localPosition,
1259 localAnchor,
1260 localScroll);
1261 }
1262 if (const auto i = map.find(Data::DraftKey::LocalEdit()); i != end(map)) {
1263 i->second->cursor = MessageCursor(
1264 editPosition,
1265 editAnchor,
1266 editScroll);
1267 }
1268 }
1269
readDraftsWithCursors(not_null<History * > history)1270 void Account::readDraftsWithCursors(not_null<History*> history) {
1271 const auto guard = gsl::finally([&] {
1272 if (const auto migrated = history->migrateFrom()) {
1273 readDraftsWithCursors(migrated);
1274 migrated->clearLocalEditDraft();
1275 history->takeLocalDraft(migrated);
1276 }
1277 });
1278
1279 PeerId peerId = history->peer->id;
1280 if (!_draftsNotReadMap.remove(peerId)) {
1281 clearDraftCursors(peerId);
1282 return;
1283 }
1284
1285 const auto j = _draftsMap.find(peerId);
1286 if (j == _draftsMap.cend()) {
1287 clearDraftCursors(peerId);
1288 return;
1289 }
1290 FileReadDescriptor draft;
1291 if (!ReadEncryptedFile(draft, j->second, _basePath, _localKey)) {
1292 ClearKey(j->second, _basePath);
1293 _draftsMap.erase(j);
1294 clearDraftCursors(peerId);
1295 return;
1296 }
1297
1298 quint64 tag = 0;
1299 draft.stream >> tag;
1300 if (tag != kMultiDraftTag && tag != kMultiDraftTagOld) {
1301 readDraftsWithCursorsLegacy(history, draft, tag);
1302 return;
1303 }
1304 quint32 count = 0;
1305 quint64 draftPeerSerialized = 0;
1306 draft.stream >> draftPeerSerialized >> count;
1307 const auto draftPeer = DeserializePeerId(draftPeerSerialized);
1308 if (!count || count > 1000 || draftPeer != peerId) {
1309 ClearKey(j->second, _basePath);
1310 _draftsMap.erase(j);
1311 clearDraftCursors(peerId);
1312 return;
1313 }
1314 auto map = Data::HistoryDrafts();
1315 const auto keysOld = (tag == kMultiDraftTagOld);
1316 for (auto i = 0; i != count; ++i) {
1317 TextWithTags data;
1318 QByteArray tagsSerialized;
1319 qint64 keyValue = 0;
1320 qint32 keyValueOld = 0, messageId = 0, uncheckedPreviewState = 0;
1321 if (keysOld) {
1322 draft.stream >> keyValueOld;
1323 } else {
1324 draft.stream >> keyValue;
1325 }
1326 draft.stream
1327 >> data.text
1328 >> tagsSerialized
1329 >> messageId
1330 >> uncheckedPreviewState;
1331 data.tags = TextUtilities::DeserializeTags(
1332 tagsSerialized,
1333 data.text.size());
1334 auto previewState = Data::PreviewState::Allowed;
1335 switch (static_cast<Data::PreviewState>(uncheckedPreviewState)) {
1336 case Data::PreviewState::Cancelled:
1337 case Data::PreviewState::EmptyOnEdit:
1338 previewState = Data::PreviewState(uncheckedPreviewState);
1339 }
1340 const auto key = keysOld
1341 ? Data::DraftKey::FromSerializedOld(keyValueOld)
1342 : Data::DraftKey::FromSerialized(keyValue);
1343 if (key && key != Data::DraftKey::Cloud()) {
1344 map.emplace(key, std::make_unique<Data::Draft>(
1345 data,
1346 messageId,
1347 MessageCursor(),
1348 previewState));
1349 }
1350 }
1351 if (draft.stream.status() != QDataStream::Ok) {
1352 ClearKey(j->second, _basePath);
1353 _draftsMap.erase(j);
1354 clearDraftCursors(peerId);
1355 return;
1356 }
1357 readDraftCursors(peerId, map);
1358 history->setDraftsMap(std::move(map));
1359 }
1360
readDraftsWithCursorsLegacy(not_null<History * > history,details::FileReadDescriptor & draft,quint64 draftPeerSerialized)1361 void Account::readDraftsWithCursorsLegacy(
1362 not_null<History*> history,
1363 details::FileReadDescriptor &draft,
1364 quint64 draftPeerSerialized) {
1365 TextWithTags msgData, editData;
1366 QByteArray msgTagsSerialized, editTagsSerialized;
1367 qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0;
1368 draft.stream >> msgData.text;
1369 if (draft.version >= 9048) {
1370 draft.stream >> msgTagsSerialized;
1371 }
1372 if (draft.version >= 7021) {
1373 draft.stream >> msgReplyTo;
1374 if (draft.version >= 8001) {
1375 draft.stream >> msgPreviewCancelled;
1376 if (!draft.stream.atEnd()) {
1377 draft.stream >> editData.text;
1378 if (draft.version >= 9048) {
1379 draft.stream >> editTagsSerialized;
1380 }
1381 draft.stream >> editMsgId >> editPreviewCancelled;
1382 }
1383 }
1384 }
1385 const auto peerId = history->peer->id;
1386 const auto draftPeer = DeserializePeerId(draftPeerSerialized);
1387 if (draftPeer != peerId) {
1388 const auto j = _draftsMap.find(peerId);
1389 if (j != _draftsMap.cend()) {
1390 ClearKey(j->second, _basePath);
1391 _draftsMap.erase(j);
1392 }
1393 clearDraftCursors(peerId);
1394 return;
1395 }
1396
1397 msgData.tags = TextUtilities::DeserializeTags(
1398 msgTagsSerialized,
1399 msgData.text.size());
1400 editData.tags = TextUtilities::DeserializeTags(
1401 editTagsSerialized,
1402 editData.text.size());
1403
1404 auto map = base::flat_map<Data::DraftKey, std::unique_ptr<Data::Draft>>();
1405 if (!msgData.text.isEmpty() || msgReplyTo) {
1406 map.emplace(Data::DraftKey::Local(), std::make_unique<Data::Draft>(
1407 msgData,
1408 msgReplyTo,
1409 MessageCursor(),
1410 (msgPreviewCancelled
1411 ? Data::PreviewState::Cancelled
1412 : Data::PreviewState::Allowed)));
1413 }
1414 if (editMsgId) {
1415 map.emplace(Data::DraftKey::LocalEdit(), std::make_unique<Data::Draft>(
1416 editData,
1417 editMsgId,
1418 MessageCursor(),
1419 (editPreviewCancelled
1420 ? Data::PreviewState::Cancelled
1421 : Data::PreviewState::Allowed)));
1422 }
1423 readDraftCursors(peerId, map);
1424 history->setDraftsMap(std::move(map));
1425 }
1426
hasDraftCursors(PeerId peer)1427 bool Account::hasDraftCursors(PeerId peer) {
1428 return _draftCursorsMap.contains(peer);
1429 }
1430
hasDraft(PeerId peer)1431 bool Account::hasDraft(PeerId peer) {
1432 return _draftsMap.contains(peer);
1433 }
1434
writeFileLocation(MediaKey location,const Core::FileLocation & local)1435 void Account::writeFileLocation(MediaKey location, const Core::FileLocation &local) {
1436 if (local.fname.isEmpty()) {
1437 return;
1438 }
1439 if (!local.inMediaCache()) {
1440 const auto aliasIt = _fileLocationAliases.constFind(location);
1441 if (aliasIt != _fileLocationAliases.cend()) {
1442 location = aliasIt.value();
1443 }
1444
1445 const auto i = _fileLocationPairs.find(local.fname);
1446 if (i != _fileLocationPairs.cend()) {
1447 if (i.value().second == local) {
1448 if (i.value().first != location) {
1449 _fileLocationAliases.insert(location, i.value().first);
1450 writeLocationsQueued();
1451 }
1452 return;
1453 }
1454 if (i.value().first != location) {
1455 for (auto j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first); ++j) {
1456 if (j.value() == i.value().second) {
1457 _fileLocations.erase(j);
1458 break;
1459 }
1460 }
1461 _fileLocationPairs.erase(i);
1462 }
1463 }
1464 _fileLocationPairs.insert(local.fname, { location, local });
1465 } else {
1466 for (auto i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
1467 if (i.value().inMediaCache() || i.value().check()) {
1468 return;
1469 }
1470 i = _fileLocations.erase(i);
1471 }
1472 }
1473 _fileLocations.insert(location, local);
1474 writeLocationsQueued();
1475 }
1476
removeFileLocation(MediaKey location)1477 void Account::removeFileLocation(MediaKey location) {
1478 auto i = _fileLocations.find(location);
1479 if (i == _fileLocations.end()) {
1480 return;
1481 }
1482 while (i != _fileLocations.end() && (i.key() == location)) {
1483 i = _fileLocations.erase(i);
1484 }
1485 writeLocationsQueued();
1486 }
1487
readFileLocation(MediaKey location)1488 Core::FileLocation Account::readFileLocation(MediaKey location) {
1489 const auto aliasIt = _fileLocationAliases.constFind(location);
1490 if (aliasIt != _fileLocationAliases.cend()) {
1491 location = aliasIt.value();
1492 }
1493
1494 for (auto i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) {
1495 if (!i.value().inMediaCache() && !i.value().check()) {
1496 _fileLocationPairs.remove(i.value().fname);
1497 i = _fileLocations.erase(i);
1498 writeLocationsDelayed();
1499 continue;
1500 }
1501 return i.value();
1502 }
1503 return Core::FileLocation();
1504 }
1505
cacheKey() const1506 EncryptionKey Account::cacheKey() const {
1507 Expects(_localKey != nullptr);
1508
1509 return EncryptionKey(bytes::make_vector(_localKey->data()));
1510 }
1511
cacheBigFileKey() const1512 EncryptionKey Account::cacheBigFileKey() const {
1513 return cacheKey();
1514 }
1515
cachePath() const1516 QString Account::cachePath() const {
1517 Expects(!_databasePath.isEmpty());
1518
1519 return _databasePath + "cache";
1520 }
1521
cacheSettings() const1522 Cache::Database::Settings Account::cacheSettings() const {
1523 auto result = Cache::Database::Settings();
1524 result.clearOnWrongKey = true;
1525 result.totalSizeLimit = _cacheTotalSizeLimit;
1526 result.totalTimeLimit = _cacheTotalTimeLimit;
1527 result.maxDataSize = kMaxFileInMemory;
1528 return result;
1529 }
1530
updateCacheSettings(Cache::Database::SettingsUpdate & update,Cache::Database::SettingsUpdate & updateBig)1531 void Account::updateCacheSettings(
1532 Cache::Database::SettingsUpdate &update,
1533 Cache::Database::SettingsUpdate &updateBig) {
1534 Expects(update.totalSizeLimit > Database::Settings().maxDataSize);
1535 Expects(update.totalTimeLimit >= 0);
1536 Expects(updateBig.totalSizeLimit > Database::Settings().maxDataSize);
1537 Expects(updateBig.totalTimeLimit >= 0);
1538
1539 if (_cacheTotalSizeLimit == update.totalSizeLimit
1540 && _cacheTotalTimeLimit == update.totalTimeLimit
1541 && _cacheBigFileTotalSizeLimit == updateBig.totalSizeLimit
1542 && _cacheBigFileTotalTimeLimit == updateBig.totalTimeLimit) {
1543 return;
1544 }
1545 _cacheTotalSizeLimit = update.totalSizeLimit;
1546 _cacheTotalTimeLimit = update.totalTimeLimit;
1547 _cacheBigFileTotalSizeLimit = updateBig.totalSizeLimit;
1548 _cacheBigFileTotalTimeLimit = updateBig.totalTimeLimit;
1549 writeSessionSettings();
1550 }
1551
cacheBigFilePath() const1552 QString Account::cacheBigFilePath() const {
1553 Expects(!_databasePath.isEmpty());
1554
1555 return _databasePath + "media_cache";
1556 }
1557
cacheBigFileSettings() const1558 Cache::Database::Settings Account::cacheBigFileSettings() const {
1559 auto result = Cache::Database::Settings();
1560 result.clearOnWrongKey = true;
1561 result.totalSizeLimit = _cacheBigFileTotalSizeLimit;
1562 result.totalTimeLimit = _cacheBigFileTotalTimeLimit;
1563 result.maxDataSize = kMaxFileInMemory;
1564 return result;
1565 }
1566
writeStickerSet(QDataStream & stream,const Data::StickersSet & set)1567 void Account::writeStickerSet(
1568 QDataStream &stream,
1569 const Data::StickersSet &set) {
1570 using SetFlag = Data::StickersSetFlag;
1571 const auto writeInfo = [&](int count) {
1572 stream
1573 << quint64(set.id)
1574 << quint64(set.accessHash)
1575 << quint64(set.hash)
1576 << set.title
1577 << set.shortName
1578 << qint32(count)
1579 << qint32(set.flags)
1580 << qint32(set.installDate);
1581 Serialize::writeImageLocation(stream, set.thumbnailLocation());
1582 };
1583 if (set.flags & SetFlag::NotLoaded) {
1584 writeInfo(-set.count);
1585 return;
1586 } else if (set.stickers.isEmpty()) {
1587 return;
1588 }
1589
1590 writeInfo(set.stickers.size());
1591 for (const auto &sticker : set.stickers) {
1592 Serialize::Document::writeToStream(stream, sticker);
1593 }
1594 stream << qint32(set.dates.size());
1595 if (!set.dates.empty()) {
1596 Assert(set.dates.size() == set.stickers.size());
1597 for (const auto date : set.dates) {
1598 stream << qint32(date);
1599 }
1600 }
1601 stream << qint32(set.emoji.size());
1602 for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) {
1603 stream << j.key()->id() << qint32(j->size());
1604 for (const auto sticker : *j) {
1605 stream << quint64(sticker->id);
1606 }
1607 }
1608 }
1609
1610 // In generic method _writeStickerSets() we look through all the sets and call a
1611 // callback on each set to see, if we write it, skip it or abort the whole write.
1612 enum class StickerSetCheckResult {
1613 Write,
1614 Skip,
1615 Abort,
1616 };
1617
1618 // CheckSet is a functor on Data::StickersSet, which returns a StickerSetCheckResult.
1619 template <typename CheckSet>
writeStickerSets(FileKey & stickersKey,CheckSet checkSet,const Data::StickersSetsOrder & order)1620 void Account::writeStickerSets(
1621 FileKey &stickersKey,
1622 CheckSet checkSet,
1623 const Data::StickersSetsOrder &order) {
1624 using SetFlag = Data::StickersSetFlag;
1625
1626 const auto &sets = _owner->session().data().stickers().sets();
1627 if (sets.empty()) {
1628 if (stickersKey) {
1629 ClearKey(stickersKey, _basePath);
1630 stickersKey = 0;
1631 writeMapDelayed();
1632 }
1633 return;
1634 }
1635
1636 // versionTag + version + count
1637 quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(qint32);
1638
1639 int32 setsCount = 0;
1640 for (const auto &[id, set] : sets) {
1641 const auto raw = set.get();
1642 auto result = checkSet(*raw);
1643 if (result == StickerSetCheckResult::Abort) {
1644 return;
1645 } else if (result == StickerSetCheckResult::Skip) {
1646 continue;
1647 }
1648
1649 // id + accessHash + hash + title + shortName + stickersCount + flags + installDate
1650 size += sizeof(quint64) * 3
1651 + Serialize::stringSize(raw->title)
1652 + Serialize::stringSize(raw->shortName)
1653 + sizeof(qint32) * 3
1654 + Serialize::imageLocationSize(raw->thumbnailLocation());
1655 if (raw->flags & SetFlag::NotLoaded) {
1656 continue;
1657 }
1658
1659 for (const auto sticker : std::as_const(raw->stickers)) {
1660 size += Serialize::Document::sizeInStream(sticker);
1661 }
1662
1663 size += sizeof(qint32); // datesCount
1664 if (!raw->dates.empty()) {
1665 Assert(raw->stickers.size() == raw->dates.size());
1666 size += raw->dates.size() * sizeof(qint32);
1667 }
1668
1669 size += sizeof(qint32); // emojiCount
1670 for (auto j = raw->emoji.cbegin(), e = raw->emoji.cend(); j != e; ++j) {
1671 size += Serialize::stringSize(j.key()->id()) + sizeof(qint32) + (j->size() * sizeof(quint64));
1672 }
1673
1674 ++setsCount;
1675 }
1676 if (!setsCount && order.isEmpty()) {
1677 if (stickersKey) {
1678 ClearKey(stickersKey, _basePath);
1679 stickersKey = 0;
1680 writeMapDelayed();
1681 }
1682 return;
1683 }
1684 size += sizeof(qint32) + (order.size() * sizeof(quint64));
1685
1686 if (!stickersKey) {
1687 stickersKey = GenerateKey(_basePath);
1688 writeMapQueued();
1689 }
1690 EncryptedDescriptor data(size);
1691 data.stream
1692 << quint32(kStickersVersionTag)
1693 << qint32(kStickersSerializeVersion)
1694 << qint32(setsCount);
1695 for (const auto &[id, set] : sets) {
1696 auto result = checkSet(*set);
1697 if (result == StickerSetCheckResult::Abort) {
1698 return;
1699 } else if (result == StickerSetCheckResult::Skip) {
1700 continue;
1701 }
1702 writeStickerSet(data.stream, *set);
1703 }
1704 data.stream << order;
1705
1706 FileWriteDescriptor file(stickersKey, _basePath);
1707 file.writeEncrypted(data, _localKey);
1708 }
1709
readStickerSets(FileKey & stickersKey,Data::StickersSetsOrder * outOrder,Data::StickersSetFlags readingFlags)1710 void Account::readStickerSets(
1711 FileKey &stickersKey,
1712 Data::StickersSetsOrder *outOrder,
1713 Data::StickersSetFlags readingFlags) {
1714 using SetFlag = Data::StickersSetFlag;
1715
1716 FileReadDescriptor stickers;
1717 if (!ReadEncryptedFile(stickers, stickersKey, _basePath, _localKey)) {
1718 ClearKey(stickersKey, _basePath);
1719 stickersKey = 0;
1720 writeMapDelayed();
1721 return;
1722 }
1723
1724 const auto failed = [&] {
1725 ClearKey(stickersKey, _basePath);
1726 stickersKey = 0;
1727 };
1728
1729 auto &sets = _owner->session().data().stickers().setsRef();
1730 if (outOrder) outOrder->clear();
1731
1732 quint32 versionTag = 0;
1733 qint32 version = 0;
1734 stickers.stream >> versionTag >> version;
1735 if (versionTag != kStickersVersionTag
1736 || version != kStickersSerializeVersion) {
1737 // Old data, without sticker set thumbnails.
1738 return failed();
1739 }
1740 qint32 count = 0;
1741 stickers.stream >> count;
1742 if (!CheckStreamStatus(stickers.stream)
1743 || (count < 0)
1744 || (count > kMaxSavedStickerSetsCount)) {
1745 return failed();
1746 }
1747 for (auto i = 0; i != count; ++i) {
1748 quint64 setId = 0, setAccessHash = 0, setHash = 0;
1749 QString setTitle, setShortName;
1750 qint32 scnt = 0;
1751 qint32 setInstallDate = 0;
1752 Data::StickersSetFlags setFlags = 0;
1753 qint32 setFlagsValue = 0;
1754 ImageLocation setThumbnail;
1755
1756 stickers.stream
1757 >> setId
1758 >> setAccessHash
1759 >> setHash
1760 >> setTitle
1761 >> setShortName
1762 >> scnt
1763 >> setFlagsValue
1764 >> setInstallDate;
1765 const auto thumbnail = Serialize::readImageLocation(
1766 stickers.version,
1767 stickers.stream);
1768 if (!thumbnail || !CheckStreamStatus(stickers.stream)) {
1769 return failed();
1770 } else if (thumbnail->valid() && thumbnail->isLegacy()) {
1771 // No thumb_version information in legacy location.
1772 return failed();
1773 } else {
1774 setThumbnail = *thumbnail;
1775 }
1776
1777 setFlags = Data::StickersSetFlags::from_raw(setFlagsValue);
1778 if (setId == Data::Stickers::DefaultSetId) {
1779 setTitle = tr::lng_stickers_default_set(tr::now);
1780 setFlags |= SetFlag::Official | SetFlag::Special;
1781 } else if (setId == Data::Stickers::CustomSetId) {
1782 setTitle = qsl("Custom stickers");
1783 setFlags |= SetFlag::Special;
1784 } else if ((setId == Data::Stickers::CloudRecentSetId)
1785 || (setId == Data::Stickers::CloudRecentAttachedSetId)) {
1786 setTitle = tr::lng_recent_stickers(tr::now);
1787 setFlags |= SetFlag::Special;
1788 } else if (setId == Data::Stickers::FavedSetId) {
1789 setTitle = Lang::Hard::FavedSetTitle();
1790 setFlags |= SetFlag::Special;
1791 } else if (!setId) {
1792 continue;
1793 }
1794
1795 auto it = sets.find(setId);
1796 if (it == sets.cend()) {
1797 // We will set this flags from order lists when reading those stickers.
1798 setFlags &= ~(SetFlag::Installed | SetFlag::Featured);
1799 it = sets.emplace(setId, std::make_unique<Data::StickersSet>(
1800 &_owner->session().data(),
1801 setId,
1802 setAccessHash,
1803 setHash,
1804 setTitle,
1805 setShortName,
1806 0,
1807 setFlags,
1808 setInstallDate)).first;
1809 it->second->setThumbnail(
1810 ImageWithLocation{ .location = setThumbnail });
1811 }
1812 const auto set = it->second.get();
1813 const auto inputSet = set->identifier();
1814 const auto fillStickers = set->stickers.isEmpty();
1815
1816 if (scnt < 0) { // disabled not loaded set
1817 if (!set->count || fillStickers) {
1818 set->count = -scnt;
1819 }
1820 continue;
1821 }
1822
1823 if (fillStickers) {
1824 set->stickers.reserve(scnt);
1825 set->count = 0;
1826 }
1827
1828 Serialize::Document::StickerSetInfo info(
1829 setId,
1830 setAccessHash,
1831 setShortName);
1832 base::flat_set<DocumentId> read;
1833 for (int32 j = 0; j < scnt; ++j) {
1834 auto document = Serialize::Document::readStickerFromStream(
1835 &_owner->session(),
1836 stickers.version,
1837 stickers.stream, info);
1838 if (!CheckStreamStatus(stickers.stream)) {
1839 return failed();
1840 } else if (!document
1841 || !document->sticker()
1842 || read.contains(document->id)) {
1843 continue;
1844 }
1845 read.emplace(document->id);
1846 if (fillStickers) {
1847 set->stickers.push_back(document);
1848 if (!(set->flags & SetFlag::Special)) {
1849 if (!document->sticker()->set.id) {
1850 document->sticker()->set = inputSet;
1851 }
1852 }
1853 ++set->count;
1854 }
1855 }
1856
1857 qint32 datesCount = 0;
1858 stickers.stream >> datesCount;
1859 if (datesCount > 0) {
1860 if (datesCount != scnt) {
1861 return failed();
1862 }
1863 const auto fillDates =
1864 ((set->id == Data::Stickers::CloudRecentSetId)
1865 || (set->id == Data::Stickers::CloudRecentAttachedSetId))
1866 && (set->stickers.size() == datesCount);
1867 if (fillDates) {
1868 set->dates.clear();
1869 set->dates.reserve(datesCount);
1870 }
1871 for (auto i = 0; i != datesCount; ++i) {
1872 qint32 date = 0;
1873 stickers.stream >> date;
1874 if (fillDates) {
1875 set->dates.push_back(TimeId(date));
1876 }
1877 }
1878 }
1879
1880 qint32 emojiCount = 0;
1881 stickers.stream >> emojiCount;
1882 if (!CheckStreamStatus(stickers.stream) || emojiCount < 0) {
1883 return failed();
1884 }
1885 for (int32 j = 0; j < emojiCount; ++j) {
1886 QString emojiString;
1887 qint32 stickersCount;
1888 stickers.stream >> emojiString >> stickersCount;
1889 Data::StickersPack pack;
1890 pack.reserve(stickersCount);
1891 for (int32 k = 0; k < stickersCount; ++k) {
1892 quint64 id;
1893 stickers.stream >> id;
1894 const auto doc = _owner->session().data().document(id);
1895 if (!doc->sticker()) continue;
1896
1897 pack.push_back(doc);
1898 }
1899 if (fillStickers) {
1900 if (auto emoji = Ui::Emoji::Find(emojiString)) {
1901 emoji = emoji->original();
1902 set->emoji.insert(emoji, pack);
1903 }
1904 }
1905 }
1906 }
1907
1908 // Read orders of installed and featured stickers.
1909 if (outOrder) {
1910 auto outOrderCount = quint32();
1911 stickers.stream >> outOrderCount;
1912 if (!CheckStreamStatus(stickers.stream) || outOrderCount > 1000) {
1913 return failed();
1914 }
1915 outOrder->reserve(outOrderCount);
1916 for (auto i = 0; i != outOrderCount; ++i) {
1917 auto value = uint64();
1918 stickers.stream >> value;
1919 if (!CheckStreamStatus(stickers.stream)) {
1920 outOrder->clear();
1921 return failed();
1922 }
1923 outOrder->push_back(value);
1924 }
1925 }
1926 if (!CheckStreamStatus(stickers.stream)) {
1927 return failed();
1928 }
1929
1930 // Set flags that we dropped above from the order.
1931 if (readingFlags && outOrder) {
1932 for (const auto setId : std::as_const(*outOrder)) {
1933 auto it = sets.find(setId);
1934 if (it != sets.cend()) {
1935 const auto set = it->second.get();
1936 set->flags |= readingFlags;
1937 if ((readingFlags == SetFlag::Installed)
1938 && !set->installDate) {
1939 set->installDate = kDefaultStickerInstallDate;
1940 }
1941 }
1942 }
1943 }
1944 }
1945
writeInstalledStickers()1946 void Account::writeInstalledStickers() {
1947 using SetFlag = Data::StickersSetFlag;
1948
1949 writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) {
1950 if (set.id == Data::Stickers::CloudRecentSetId
1951 || set.id == Data::Stickers::FavedSetId
1952 || set.id == Data::Stickers::CloudRecentAttachedSetId) {
1953 // separate files for them
1954 return StickerSetCheckResult::Skip;
1955 } else if (set.flags & SetFlag::Special) {
1956 if (set.stickers.isEmpty()) { // all other special are "installed"
1957 return StickerSetCheckResult::Skip;
1958 }
1959 } else if (!(set.flags & SetFlag::Installed)
1960 || (set.flags & SetFlag::Archived)) {
1961 return StickerSetCheckResult::Skip;
1962 } else if (set.flags & SetFlag::Masks) {
1963 return StickerSetCheckResult::Skip;
1964 } else if (set.flags & SetFlag::NotLoaded) {
1965 // waiting to receive
1966 return StickerSetCheckResult::Abort;
1967 } else if (set.stickers.isEmpty()) {
1968 return StickerSetCheckResult::Skip;
1969 }
1970 return StickerSetCheckResult::Write;
1971 }, _owner->session().data().stickers().setsOrder());
1972 }
1973
writeFeaturedStickers()1974 void Account::writeFeaturedStickers() {
1975 using SetFlag = Data::StickersSetFlag;
1976
1977 writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) {
1978 if (set.id == Data::Stickers::CloudRecentSetId
1979 || set.id == Data::Stickers::FavedSetId
1980 || set.id == Data::Stickers::CloudRecentAttachedSetId) {
1981 // separate files for them
1982 return StickerSetCheckResult::Skip;
1983 } else if (set.flags & SetFlag::Special) {
1984 return StickerSetCheckResult::Skip;
1985 } else if (!(set.flags & SetFlag::Featured)) {
1986 return StickerSetCheckResult::Skip;
1987 } else if (set.flags & SetFlag::NotLoaded) { // waiting to receive
1988 return StickerSetCheckResult::Abort;
1989 } else if (set.stickers.isEmpty()) {
1990 return StickerSetCheckResult::Skip;
1991 }
1992 return StickerSetCheckResult::Write;
1993 }, _owner->session().data().stickers().featuredSetsOrder());
1994 }
1995
writeRecentStickers()1996 void Account::writeRecentStickers() {
1997 writeStickerSets(_recentStickersKey, [](const Data::StickersSet &set) {
1998 if (set.id != Data::Stickers::CloudRecentSetId
1999 || set.stickers.isEmpty()) {
2000 return StickerSetCheckResult::Skip;
2001 }
2002 return StickerSetCheckResult::Write;
2003 }, Data::StickersSetsOrder());
2004 }
2005
writeFavedStickers()2006 void Account::writeFavedStickers() {
2007 writeStickerSets(_favedStickersKey, [](const Data::StickersSet &set) {
2008 if (set.id != Data::Stickers::FavedSetId || set.stickers.isEmpty()) {
2009 return StickerSetCheckResult::Skip;
2010 }
2011 return StickerSetCheckResult::Write;
2012 }, Data::StickersSetsOrder());
2013 }
2014
writeArchivedStickers()2015 void Account::writeArchivedStickers() {
2016 using SetFlag = Data::StickersSetFlag;
2017
2018 writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) {
2019 if (set.flags & SetFlag::Masks) {
2020 return StickerSetCheckResult::Skip;
2021 }
2022 if (!(set.flags & SetFlag::Archived)
2023 || set.stickers.isEmpty()) {
2024 return StickerSetCheckResult::Skip;
2025 }
2026 return StickerSetCheckResult::Write;
2027 }, _owner->session().data().stickers().archivedSetsOrder());
2028 }
2029
writeArchivedMasks()2030 void Account::writeArchivedMasks() {
2031 using SetFlag = Data::StickersSetFlag;
2032
2033 writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) {
2034 if (!(set.flags & SetFlag::Masks)) {
2035 return StickerSetCheckResult::Skip;
2036 }
2037 if (!(set.flags & SetFlag::Archived) || set.stickers.isEmpty()) {
2038 return StickerSetCheckResult::Skip;
2039 }
2040 return StickerSetCheckResult::Write;
2041 }, _owner->session().data().stickers().archivedMaskSetsOrder());
2042 }
2043
writeInstalledMasks()2044 void Account::writeInstalledMasks() {
2045 using SetFlag = Data::StickersSetFlag;
2046
2047 writeStickerSets(_installedMasksKey, [](const Data::StickersSet &set) {
2048 if (!(set.flags & SetFlag::Masks) || set.stickers.isEmpty()) {
2049 return StickerSetCheckResult::Skip;
2050 }
2051 return StickerSetCheckResult::Write;
2052 }, _owner->session().data().stickers().maskSetsOrder());
2053 }
2054
writeRecentMasks()2055 void Account::writeRecentMasks() {
2056 writeStickerSets(_recentMasksKey, [](const Data::StickersSet &set) {
2057 if (set.id != Data::Stickers::CloudRecentAttachedSetId
2058 || set.stickers.isEmpty()) {
2059 return StickerSetCheckResult::Skip;
2060 }
2061 return StickerSetCheckResult::Write;
2062 }, Data::StickersSetsOrder());
2063 }
2064
importOldRecentStickers()2065 void Account::importOldRecentStickers() {
2066 using SetFlag = Data::StickersSetFlag;
2067
2068 if (!_recentStickersKeyOld) {
2069 return;
2070 }
2071
2072 FileReadDescriptor stickers;
2073 if (!ReadEncryptedFile(stickers, _recentStickersKeyOld, _basePath, _localKey)) {
2074 ClearKey(_recentStickersKeyOld, _basePath);
2075 _recentStickersKeyOld = 0;
2076 writeMapDelayed();
2077 return;
2078 }
2079
2080 auto &sets = _owner->session().data().stickers().setsRef();
2081 sets.clear();
2082
2083 auto &order = _owner->session().data().stickers().setsOrderRef();
2084 order.clear();
2085
2086 auto &recent = cRefRecentStickers();
2087 recent.clear();
2088
2089 const auto def = sets.emplace(
2090 Data::Stickers::DefaultSetId,
2091 std::make_unique<Data::StickersSet>(
2092 &_owner->session().data(),
2093 Data::Stickers::DefaultSetId,
2094 uint64(0), // accessHash
2095 uint64(0), // hash
2096 tr::lng_stickers_default_set(tr::now),
2097 QString(),
2098 0, // count
2099 (SetFlag::Official | SetFlag::Installed | SetFlag::Special),
2100 kDefaultStickerInstallDate)).first->second.get();
2101 const auto custom = sets.emplace(
2102 Data::Stickers::CustomSetId,
2103 std::make_unique<Data::StickersSet>(
2104 &_owner->session().data(),
2105 Data::Stickers::CustomSetId,
2106 uint64(0), // accessHash
2107 uint64(0), // hash
2108 qsl("Custom stickers"),
2109 QString(),
2110 0, // count
2111 (SetFlag::Installed | SetFlag::Special),
2112 kDefaultStickerInstallDate)).first->second.get();
2113
2114 QMap<uint64, bool> read;
2115 while (!stickers.stream.atEnd()) {
2116 quint64 id, access;
2117 QString name, mime, alt;
2118 qint32 date, dc, size, width, height, type;
2119 qint16 value;
2120 stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type;
2121 if (stickers.version >= 7021) {
2122 stickers.stream >> alt;
2123 }
2124 if (!value || read.contains(id)) continue;
2125 read.insert(id, true);
2126
2127 QVector<MTPDocumentAttribute> attributes;
2128 if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
2129 if (type == AnimatedDocument) {
2130 attributes.push_back(MTP_documentAttributeAnimated());
2131 } else if (type == StickerDocument) {
2132 attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
2133 }
2134 if (width > 0 && height > 0) {
2135 attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
2136 }
2137
2138 const auto doc = _owner->session().data().document(
2139 id,
2140 access,
2141 QByteArray(),
2142 date,
2143 attributes,
2144 mime,
2145 InlineImageLocation(),
2146 ImageWithLocation(),
2147 ImageWithLocation(),
2148 dc,
2149 size);
2150 if (!doc->sticker()) {
2151 continue;
2152 }
2153
2154 if (value > 0) {
2155 def->stickers.push_back(doc);
2156 ++def->count;
2157 } else {
2158 custom->stickers.push_back(doc);
2159 ++custom->count;
2160 }
2161 if (qAbs(value) > 1
2162 && (recent.size()
2163 < _owner->session().serverConfig().stickersRecentLimit)) {
2164 recent.push_back(qMakePair(doc, qAbs(value)));
2165 }
2166 }
2167 if (def->stickers.isEmpty()) {
2168 sets.remove(Data::Stickers::DefaultSetId);
2169 } else {
2170 order.push_front(Data::Stickers::DefaultSetId);
2171 }
2172 if (custom->stickers.isEmpty()) {
2173 sets.remove(Data::Stickers::CustomSetId);
2174 }
2175
2176 writeInstalledStickers();
2177 writeSessionSettings();
2178
2179 ClearKey(_recentStickersKeyOld, _basePath);
2180 _recentStickersKeyOld = 0;
2181 writeMapDelayed();
2182 }
2183
readInstalledStickers()2184 void Account::readInstalledStickers() {
2185 if (!_installedStickersKey) {
2186 return importOldRecentStickers();
2187 }
2188
2189 _owner->session().data().stickers().setsRef().clear();
2190 readStickerSets(
2191 _installedStickersKey,
2192 &_owner->session().data().stickers().setsOrderRef(),
2193 Data::StickersSetFlag::Installed);
2194 }
2195
readFeaturedStickers()2196 void Account::readFeaturedStickers() {
2197 readStickerSets(
2198 _featuredStickersKey,
2199 &_owner->session().data().stickers().featuredSetsOrderRef(),
2200 Data::StickersSetFlag::Featured);
2201
2202 const auto &sets = _owner->session().data().stickers().sets();
2203 const auto &order = _owner->session().data().stickers().featuredSetsOrder();
2204 int unreadCount = 0;
2205 for (const auto setId : order) {
2206 auto it = sets.find(setId);
2207 if (it != sets.cend()
2208 && (it->second->flags & Data::StickersSetFlag::Unread)) {
2209 ++unreadCount;
2210 }
2211 }
2212 _owner->session().data().stickers().setFeaturedSetsUnreadCount(unreadCount);
2213 }
2214
readRecentStickers()2215 void Account::readRecentStickers() {
2216 readStickerSets(_recentStickersKey);
2217 }
2218
readRecentMasks()2219 void Account::readRecentMasks() {
2220 readStickerSets(_recentMasksKey);
2221 }
2222
readFavedStickers()2223 void Account::readFavedStickers() {
2224 readStickerSets(_favedStickersKey);
2225 }
2226
readArchivedStickers()2227 void Account::readArchivedStickers() {
2228 // TODO: refactor to support for multiple accounts.
2229 static bool archivedStickersRead = false;
2230 if (!archivedStickersRead) {
2231 readStickerSets(
2232 _archivedStickersKey,
2233 &_owner->session().data().stickers().archivedSetsOrderRef());
2234 archivedStickersRead = true;
2235 }
2236 }
2237
readArchivedMasks()2238 void Account::readArchivedMasks() {
2239 // TODO: refactor to support for multiple accounts.
2240 static bool archivedMasksRead = false;
2241 if (!archivedMasksRead) {
2242 readStickerSets(
2243 _archivedMasksKey,
2244 &_owner->session().data().stickers().archivedMaskSetsOrderRef());
2245 archivedMasksRead = true;
2246 }
2247 }
2248
readInstalledMasks()2249 void Account::readInstalledMasks() {
2250 readStickerSets(
2251 _installedMasksKey,
2252 &_owner->session().data().stickers().maskSetsOrderRef(),
2253 Data::StickersSetFlag::Installed);
2254 }
2255
writeSavedGifs()2256 void Account::writeSavedGifs() {
2257 const auto &saved = _owner->session().data().stickers().savedGifs();
2258 if (saved.isEmpty()) {
2259 if (_savedGifsKey) {
2260 ClearKey(_savedGifsKey, _basePath);
2261 _savedGifsKey = 0;
2262 writeMapDelayed();
2263 }
2264 } else {
2265 quint32 size = sizeof(quint32); // count
2266 for (const auto gif : saved) {
2267 size += Serialize::Document::sizeInStream(gif);
2268 }
2269
2270 if (!_savedGifsKey) {
2271 _savedGifsKey = GenerateKey(_basePath);
2272 writeMapQueued();
2273 }
2274 EncryptedDescriptor data(size);
2275 data.stream << quint32(saved.size());
2276 for (const auto gif : saved) {
2277 Serialize::Document::writeToStream(data.stream, gif);
2278 }
2279 FileWriteDescriptor file(_savedGifsKey, _basePath);
2280 file.writeEncrypted(data, _localKey);
2281 }
2282 }
2283
readSavedGifs()2284 void Account::readSavedGifs() {
2285 if (!_savedGifsKey) return;
2286
2287 FileReadDescriptor gifs;
2288 if (!ReadEncryptedFile(gifs, _savedGifsKey, _basePath, _localKey)) {
2289 ClearKey(_savedGifsKey, _basePath);
2290 _savedGifsKey = 0;
2291 writeMapDelayed();
2292 return;
2293 }
2294
2295 auto &saved = _owner->session().data().stickers().savedGifsRef();
2296 const auto failed = [&] {
2297 ClearKey(_savedGifsKey, _basePath);
2298 _savedGifsKey = 0;
2299 saved.clear();
2300 };
2301 saved.clear();
2302
2303 quint32 cnt;
2304 gifs.stream >> cnt;
2305 saved.reserve(cnt);
2306 OrderedSet<DocumentId> read;
2307 for (uint32 i = 0; i < cnt; ++i) {
2308 auto document = Serialize::Document::readFromStream(
2309 &_owner->session(),
2310 gifs.version,
2311 gifs.stream);
2312 if (!CheckStreamStatus(gifs.stream)) {
2313 return failed();
2314 } else if (!document || !document->isGifv()) {
2315 continue;
2316 }
2317
2318 if (read.contains(document->id)) continue;
2319 read.insert(document->id);
2320
2321 saved.push_back(document);
2322 }
2323 }
2324
writeRecentHashtagsAndBots()2325 void Account::writeRecentHashtagsAndBots() {
2326 const auto &write = cRecentWriteHashtags();
2327 const auto &search = cRecentSearchHashtags();
2328 const auto &bots = cRecentInlineBots();
2329
2330 if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) {
2331 readRecentHashtagsAndBots();
2332 }
2333 if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) {
2334 if (_recentHashtagsAndBotsKey) {
2335 ClearKey(_recentHashtagsAndBotsKey, _basePath);
2336 _recentHashtagsAndBotsKey = 0;
2337 writeMapDelayed();
2338 }
2339 return;
2340 }
2341 if (!_recentHashtagsAndBotsKey) {
2342 _recentHashtagsAndBotsKey = GenerateKey(_basePath);
2343 writeMapQueued();
2344 }
2345 quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size();
2346 for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) {
2347 if (!i->first.isEmpty()) {
2348 size += Serialize::stringSize(i->first) + sizeof(quint16);
2349 ++writeCnt;
2350 }
2351 }
2352 for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) {
2353 if (!i->first.isEmpty()) {
2354 size += Serialize::stringSize(i->first) + sizeof(quint16);
2355 ++searchCnt;
2356 }
2357 }
2358 for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
2359 size += Serialize::peerSize(*i);
2360 }
2361
2362 EncryptedDescriptor data(size);
2363 data.stream << quint32(writeCnt) << quint32(searchCnt);
2364 for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) {
2365 if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
2366 }
2367 for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) {
2368 if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
2369 }
2370 data.stream << quint32(botsCnt);
2371 for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
2372 Serialize::writePeer(data.stream, *i);
2373 }
2374 FileWriteDescriptor file(_recentHashtagsAndBotsKey, _basePath);
2375 file.writeEncrypted(data, _localKey);
2376 }
2377
readRecentHashtagsAndBots()2378 void Account::readRecentHashtagsAndBots() {
2379 if (_recentHashtagsAndBotsWereRead) return;
2380 _recentHashtagsAndBotsWereRead = true;
2381
2382 if (!_recentHashtagsAndBotsKey) return;
2383
2384 FileReadDescriptor hashtags;
2385 if (!ReadEncryptedFile(hashtags, _recentHashtagsAndBotsKey, _basePath, _localKey)) {
2386 ClearKey(_recentHashtagsAndBotsKey, _basePath);
2387 _recentHashtagsAndBotsKey = 0;
2388 writeMapDelayed();
2389 return;
2390 }
2391
2392 quint32 writeCount = 0, searchCount = 0, botsCount = 0;
2393 hashtags.stream >> writeCount >> searchCount;
2394
2395 QString tag;
2396 quint16 count;
2397
2398 RecentHashtagPack write, search;
2399 RecentInlineBots bots;
2400 if (writeCount) {
2401 write.reserve(writeCount);
2402 for (uint32 i = 0; i < writeCount; ++i) {
2403 hashtags.stream >> tag >> count;
2404 write.push_back(qMakePair(tag.trimmed(), count));
2405 }
2406 }
2407 if (searchCount) {
2408 search.reserve(searchCount);
2409 for (uint32 i = 0; i < searchCount; ++i) {
2410 hashtags.stream >> tag >> count;
2411 search.push_back(qMakePair(tag.trimmed(), count));
2412 }
2413 }
2414 cSetRecentWriteHashtags(write);
2415 cSetRecentSearchHashtags(search);
2416
2417 if (!hashtags.stream.atEnd()) {
2418 hashtags.stream >> botsCount;
2419 if (botsCount) {
2420 bots.reserve(botsCount);
2421 for (auto i = 0; i < botsCount; ++i) {
2422 const auto peer = Serialize::readPeer(
2423 &_owner->session(),
2424 hashtags.version,
2425 hashtags.stream);
2426 if (!peer) {
2427 return; // Broken data.
2428 } else if (peer->isUser()
2429 && peer->asUser()->isBot()
2430 && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty()
2431 && !peer->asUser()->username.isEmpty()) {
2432 bots.push_back(peer->asUser());
2433 }
2434 }
2435 }
2436 cSetRecentInlineBots(bots);
2437 }
2438 }
2439
saveRecentHashtags(Fn<RecentHashtagPack ()> getPack,const QString & text)2440 std::optional<RecentHashtagPack> Account::saveRecentHashtags(
2441 Fn<RecentHashtagPack()> getPack,
2442 const QString &text) {
2443 auto found = false;
2444 auto m = QRegularExpressionMatch();
2445 auto recent = getPack();
2446 for (auto i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) {
2447 i = m.capturedStart();
2448 next = m.capturedEnd();
2449 if (m.hasMatch()) {
2450 if (!m.capturedView(1).isEmpty()) {
2451 ++i;
2452 }
2453 if (!m.capturedView(2).isEmpty()) {
2454 --next;
2455 }
2456 }
2457 const auto tag = text.mid(i + 1, next - i - 1);
2458 if (TextUtilities::RegExpHashtagExclude().match(tag).hasMatch()) {
2459 continue;
2460 }
2461 if (!found
2462 && cRecentWriteHashtags().isEmpty()
2463 && cRecentSearchHashtags().isEmpty()) {
2464 readRecentHashtagsAndBots();
2465 recent = getPack();
2466 }
2467 found = true;
2468 Local::incrementRecentHashtag(recent, tag);
2469 }
2470 return found ? base::make_optional(recent) : std::nullopt;
2471 }
2472
saveRecentSentHashtags(const QString & text)2473 void Account::saveRecentSentHashtags(const QString &text) {
2474 const auto result = saveRecentHashtags(
2475 [] { return cRecentWriteHashtags(); },
2476 text);
2477 if (result) {
2478 cSetRecentWriteHashtags(*result);
2479 writeRecentHashtagsAndBots();
2480 }
2481 }
2482
saveRecentSearchHashtags(const QString & text)2483 void Account::saveRecentSearchHashtags(const QString &text) {
2484 const auto result = saveRecentHashtags(
2485 [] { return cRecentSearchHashtags(); },
2486 text);
2487 if (result) {
2488 cSetRecentSearchHashtags(*result);
2489 writeRecentHashtagsAndBots();
2490 }
2491 }
2492
writeExportSettings(const Export::Settings & settings)2493 void Account::writeExportSettings(const Export::Settings &settings) {
2494 const auto check = Export::Settings();
2495 if (settings.types == check.types
2496 && settings.fullChats == check.fullChats
2497 && settings.media.types == check.media.types
2498 && settings.media.sizeLimit == check.media.sizeLimit
2499 && settings.path == check.path
2500 && settings.format == check.format
2501 && settings.availableAt == check.availableAt
2502 && !settings.onlySinglePeer()) {
2503 if (_exportSettingsKey) {
2504 ClearKey(_exportSettingsKey, _basePath);
2505 _exportSettingsKey = 0;
2506 writeMapDelayed();
2507 }
2508 return;
2509 }
2510 if (!_exportSettingsKey) {
2511 _exportSettingsKey = GenerateKey(_basePath);
2512 writeMapQueued();
2513 }
2514 quint32 size = sizeof(quint32) * 6
2515 + Serialize::stringSize(settings.path)
2516 + sizeof(qint32) * 2 + sizeof(quint64);
2517 EncryptedDescriptor data(size);
2518 data.stream
2519 << quint32(settings.types)
2520 << quint32(settings.fullChats)
2521 << quint32(settings.media.types)
2522 << quint32(settings.media.sizeLimit)
2523 << quint32(settings.format)
2524 << settings.path
2525 << quint32(settings.availableAt);
2526 settings.singlePeer.match([&](const MTPDinputPeerUser & user) {
2527 data.stream
2528 << kSinglePeerTypeUser
2529 << quint64(user.vuser_id().v)
2530 << quint64(user.vaccess_hash().v);
2531 }, [&](const MTPDinputPeerChat & chat) {
2532 data.stream << kSinglePeerTypeChat << quint64(chat.vchat_id().v);
2533 }, [&](const MTPDinputPeerChannel & channel) {
2534 data.stream
2535 << kSinglePeerTypeChannel
2536 << quint64(channel.vchannel_id().v)
2537 << quint64(channel.vaccess_hash().v);
2538 }, [&](const MTPDinputPeerSelf &) {
2539 data.stream << kSinglePeerTypeSelf;
2540 }, [&](const MTPDinputPeerEmpty &) {
2541 data.stream << kSinglePeerTypeEmpty;
2542 }, [&](const MTPDinputPeerUserFromMessage &) {
2543 Unexpected("From message peer in single peer export settings.");
2544 }, [&](const MTPDinputPeerChannelFromMessage &) {
2545 Unexpected("From message peer in single peer export settings.");
2546 });
2547 data.stream << qint32(settings.singlePeerFrom);
2548 data.stream << qint32(settings.singlePeerTill);
2549
2550 FileWriteDescriptor file(_exportSettingsKey, _basePath);
2551 file.writeEncrypted(data, _localKey);
2552 }
2553
readExportSettings()2554 Export::Settings Account::readExportSettings() {
2555 FileReadDescriptor file;
2556 if (!ReadEncryptedFile(file, _exportSettingsKey, _basePath, _localKey)) {
2557 ClearKey(_exportSettingsKey, _basePath);
2558 _exportSettingsKey = 0;
2559 writeMapDelayed();
2560 return Export::Settings();
2561 }
2562
2563 quint32 types = 0, fullChats = 0;
2564 quint32 mediaTypes = 0, mediaSizeLimit = 0;
2565 quint32 format = 0, availableAt = 0;
2566 QString path;
2567 qint32 singlePeerType = 0, singlePeerBareIdOld = 0;
2568 quint64 singlePeerBareId = 0;
2569 quint64 singlePeerAccessHash = 0;
2570 qint32 singlePeerFrom = 0, singlePeerTill = 0;
2571 file.stream
2572 >> types
2573 >> fullChats
2574 >> mediaTypes
2575 >> mediaSizeLimit
2576 >> format
2577 >> path
2578 >> availableAt;
2579 if (!file.stream.atEnd()) {
2580 file.stream >> singlePeerType;
2581 switch (singlePeerType) {
2582 case kSinglePeerTypeUserOld:
2583 case kSinglePeerTypeChannelOld: {
2584 file.stream >> singlePeerBareIdOld >> singlePeerAccessHash;
2585 } break;
2586 case kSinglePeerTypeChatOld: file.stream >> singlePeerBareIdOld; break;
2587
2588 case kSinglePeerTypeUser:
2589 case kSinglePeerTypeChannel: {
2590 file.stream >> singlePeerBareId >> singlePeerAccessHash;
2591 } break;
2592 case kSinglePeerTypeChat: file.stream >> singlePeerBareId; break;
2593 case kSinglePeerTypeSelf:
2594 case kSinglePeerTypeEmpty: break;
2595 default: return Export::Settings();
2596 }
2597 }
2598 if (!file.stream.atEnd()) {
2599 file.stream >> singlePeerFrom >> singlePeerTill;
2600 }
2601 auto result = Export::Settings();
2602 result.types = Export::Settings::Types::from_raw(types);
2603 result.fullChats = Export::Settings::Types::from_raw(fullChats);
2604 result.media.types = Export::MediaSettings::Types::from_raw(mediaTypes);
2605 result.media.sizeLimit = mediaSizeLimit;
2606 result.format = Export::Output::Format(format);
2607 result.path = path;
2608 result.availableAt = availableAt;
2609 result.singlePeer = [&] {
2610 switch (singlePeerType) {
2611 case kSinglePeerTypeUserOld:
2612 return MTP_inputPeerUser(
2613 MTP_long(singlePeerBareIdOld),
2614 MTP_long(singlePeerAccessHash));
2615 case kSinglePeerTypeChatOld:
2616 return MTP_inputPeerChat(MTP_long(singlePeerBareIdOld));
2617 case kSinglePeerTypeChannelOld:
2618 return MTP_inputPeerChannel(
2619 MTP_long(singlePeerBareIdOld),
2620 MTP_long(singlePeerAccessHash));
2621
2622 case kSinglePeerTypeUser:
2623 return MTP_inputPeerUser(
2624 MTP_long(singlePeerBareId),
2625 MTP_long(singlePeerAccessHash));
2626 case kSinglePeerTypeChat:
2627 return MTP_inputPeerChat(MTP_long(singlePeerBareId));
2628 case kSinglePeerTypeChannel:
2629 return MTP_inputPeerChannel(
2630 MTP_long(singlePeerBareId),
2631 MTP_long(singlePeerAccessHash));
2632 case kSinglePeerTypeSelf:
2633 return MTP_inputPeerSelf();
2634 case kSinglePeerTypeEmpty:
2635 return MTP_inputPeerEmpty();
2636 }
2637 Unexpected("Type in export data single peer.");
2638 }();
2639 result.singlePeerFrom = singlePeerFrom;
2640 result.singlePeerTill = singlePeerTill;
2641 return (file.stream.status() == QDataStream::Ok && result.validate())
2642 ? result
2643 : Export::Settings();
2644 }
2645
writeSelf()2646 void Account::writeSelf() {
2647 writeMapDelayed();
2648 }
2649
readSelf(not_null<Main::Session * > session,const QByteArray & serialized,int32 streamVersion)2650 void Account::readSelf(
2651 not_null<Main::Session*> session,
2652 const QByteArray &serialized,
2653 int32 streamVersion) {
2654 QDataStream stream(serialized);
2655 const auto user = session->user();
2656 const auto wasLoadedStatus = user->loadedStatus();
2657 user->setLoadedStatus(PeerData::LoadedStatus::Not);
2658 const auto self = Serialize::readPeer(
2659 session,
2660 streamVersion,
2661 stream);
2662 if (!self || !self->isSelf() || self != user) {
2663 user->setLoadedStatus(wasLoadedStatus);
2664 return;
2665 }
2666
2667 QString about;
2668 stream >> about;
2669 if (CheckStreamStatus(stream)) {
2670 self->asUser()->setAbout(about);
2671 }
2672 }
2673
writeTrustedBots()2674 void Account::writeTrustedBots() {
2675 if (_trustedBots.empty()) {
2676 if (_trustedBotsKey) {
2677 ClearKey(_trustedBotsKey, _basePath);
2678 _trustedBotsKey = 0;
2679 writeMapDelayed();
2680 }
2681 return;
2682 }
2683 if (!_trustedBotsKey) {
2684 _trustedBotsKey = GenerateKey(_basePath);
2685 writeMapQueued();
2686 }
2687 quint32 size = sizeof(qint32) + _trustedBots.size() * sizeof(quint64);
2688 EncryptedDescriptor data(size);
2689 data.stream << qint32(_trustedBots.size());
2690 for (const auto &[peerId, mask] : _trustedBots) {
2691 // value: 8 bit mask, 56 bit bot peer_id.
2692 auto value = SerializePeerId(peerId);
2693 Assert((value >> 56) == 0);
2694 value |= (quint64(mask) << 56);
2695 data.stream << value;
2696 }
2697
2698 FileWriteDescriptor file(_trustedBotsKey, _basePath);
2699 file.writeEncrypted(data, _localKey);
2700 }
2701
readTrustedBots()2702 void Account::readTrustedBots() {
2703 if (!_trustedBotsKey) return;
2704
2705 FileReadDescriptor trusted;
2706 if (!ReadEncryptedFile(trusted, _trustedBotsKey, _basePath, _localKey)) {
2707 ClearKey(_trustedBotsKey, _basePath);
2708 _trustedBotsKey = 0;
2709 writeMapDelayed();
2710 return;
2711 }
2712
2713 qint32 size = 0;
2714 trusted.stream >> size;
2715 for (int i = 0; i < size; ++i) {
2716 auto value = quint64();
2717 trusted.stream >> value;
2718 const auto mask = base::flags<BotTrustFlag>::from_raw(
2719 uchar(value >> 56));
2720 const auto peerIdSerialized = value & ~(0xFFULL << 56);
2721 const auto peerId = DeserializePeerId(peerIdSerialized);
2722 _trustedBots.emplace(peerId, mask);
2723 }
2724 }
2725
markBotTrustedOpenGame(PeerId botId)2726 void Account::markBotTrustedOpenGame(PeerId botId) {
2727 if (isBotTrustedOpenGame(botId)) {
2728 return;
2729 }
2730 const auto i = _trustedBots.find(botId);
2731 if (i == end(_trustedBots)) {
2732 _trustedBots.emplace(botId, BotTrustFlag());
2733 } else {
2734 i->second &= ~BotTrustFlag::NoOpenGame;
2735 }
2736 writeTrustedBots();
2737 }
2738
isBotTrustedOpenGame(PeerId botId)2739 bool Account::isBotTrustedOpenGame(PeerId botId) {
2740 if (!_trustedBotsRead) {
2741 readTrustedBots();
2742 _trustedBotsRead = true;
2743 }
2744 const auto i = _trustedBots.find(botId);
2745 return (i != end(_trustedBots))
2746 && ((i->second & BotTrustFlag::NoOpenGame) == 0);
2747 }
2748
markBotTrustedPayment(PeerId botId)2749 void Account::markBotTrustedPayment(PeerId botId) {
2750 if (isBotTrustedPayment(botId)) {
2751 return;
2752 }
2753 const auto i = _trustedBots.find(botId);
2754 if (i == end(_trustedBots)) {
2755 _trustedBots.emplace(
2756 botId,
2757 BotTrustFlag::NoOpenGame | BotTrustFlag::Payment);
2758 } else {
2759 i->second |= BotTrustFlag::Payment;
2760 }
2761 writeTrustedBots();
2762 }
2763
isBotTrustedPayment(PeerId botId)2764 bool Account::isBotTrustedPayment(PeerId botId) {
2765 if (!_trustedBotsRead) {
2766 readTrustedBots();
2767 _trustedBotsRead = true;
2768 }
2769 const auto i = _trustedBots.find(botId);
2770 return (i != end(_trustedBots))
2771 && ((i->second & BotTrustFlag::Payment) != 0);
2772 }
2773
encrypt(const void * src,void * dst,uint32 len,const void * key128) const2774 bool Account::encrypt(
2775 const void *src,
2776 void *dst,
2777 uint32 len,
2778 const void *key128) const {
2779 if (!_localKey) {
2780 return false;
2781 }
2782 MTP::aesEncryptLocal(src, dst, len, _localKey, key128);
2783 return true;
2784 }
2785
decrypt(const void * src,void * dst,uint32 len,const void * key128) const2786 bool Account::decrypt(
2787 const void *src,
2788 void *dst,
2789 uint32 len,
2790 const void *key128) const {
2791 if (!_localKey) {
2792 return false;
2793 }
2794 MTP::aesDecryptLocal(src, dst, len, _localKey, key128);
2795 return true;
2796 }
2797
2798 } // namespace Storage
2799