image_location.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/image/image_location.h"
  8. #include "ui/image/image.h"
  9. #include "platform/platform_specific.h"
  10. #include "storage/cache/storage_cache_types.h"
  11. #include "storage/serialize_common.h"
  12. #include "data/data_file_origin.h"
  13. #include "base/overload.h"
  14. #include "main/main_session.h"
  15. #include <QtCore/QBuffer>
  16. namespace {
  17. constexpr auto kDocumentBaseCacheTag = 0x0000000000010000ULL;
  18. constexpr auto kDocumentBaseCacheMask = 0x000000000000FF00ULL;
  19. constexpr auto kPhotoBaseCacheTag = 0x0000000000020000ULL;
  20. constexpr auto kPhotoBaseCacheMask = 0x000000000000FF00ULL;
  21. constexpr auto kNonStorageLocationToken = quint8(0x10);
  22. constexpr auto kLegacyInMessagePeerIdFlag = quint8(0x08);
  23. constexpr auto kModernLocationFlag = quint8(0x20);
  24. constexpr auto kInMessageFieldsFlag = quint8(0x40);
  25. enum class NonStorageLocationType : quint8 {
  26. Web,
  27. Geo,
  28. Url,
  29. Memory,
  30. AudioAlbumThumb,
  31. };
  32. MTPInputPeer GenerateInputPeer(
  33. PeerId id,
  34. uint64 accessHash,
  35. PeerId inMessagePeerId,
  36. int32 inMessageId,
  37. UserId self) {
  38. const auto bareId = [&] {
  39. return peerToBareMTPInt(id);
  40. };
  41. if (inMessageId && peerIsUser(inMessagePeerId)) {
  42. return MTP_inputPeerUserFromMessage(
  43. GenerateInputPeer(id, accessHash, 0, 0, self),
  44. MTP_int(inMessageId),
  45. MTP_long(peerToUser(inMessagePeerId).bare));
  46. } else if (inMessageId && peerIsChannel(inMessagePeerId)) {
  47. return MTP_inputPeerChannelFromMessage(
  48. GenerateInputPeer(id, accessHash, 0, 0, self),
  49. MTP_int(inMessageId),
  50. MTP_long(peerToChannel(inMessagePeerId).bare));
  51. } else if (!id) {
  52. return MTP_inputPeerEmpty();
  53. } else if (id == peerFromUser(self)) {
  54. return MTP_inputPeerSelf();
  55. } else if (peerIsUser(id)) {
  56. return MTP_inputPeerUser(bareId(), MTP_long(accessHash));
  57. } else if (peerIsChat(id)) {
  58. return MTP_inputPeerChat(bareId());
  59. } else if (peerIsChannel(id)) {
  60. return MTP_inputPeerChannel(bareId(), MTP_long(accessHash));
  61. } else {
  62. return MTP_inputPeerEmpty();
  63. }
  64. }
  65. } // namespace
  66. WebFileLocation WebFileLocation::Null;
  67. StorageFileLocation::StorageFileLocation(
  68. int32 dcId,
  69. UserId self,
  70. const MTPInputFileLocation &tl)
  71. : _dcId(dcId) {
  72. tl.match([&](const MTPDinputFileLocation &data) {
  73. _type = Type::Legacy;
  74. _volumeId = data.vvolume_id().v;
  75. _localId = data.vlocal_id().v;
  76. _accessHash = data.vsecret().v;
  77. _fileReference = data.vfile_reference().v;
  78. }, [&](const MTPDinputEncryptedFileLocation &data) {
  79. _type = Type::Encrypted;
  80. _id = data.vid().v;
  81. _accessHash = data.vaccess_hash().v;
  82. }, [&](const MTPDinputDocumentFileLocation &data) {
  83. _type = Type::Document;
  84. _id = data.vid().v;
  85. _accessHash = data.vaccess_hash().v;
  86. _fileReference = data.vfile_reference().v;
  87. _sizeLetter = data.vthumb_size().v.isEmpty()
  88. ? uint8(0)
  89. : uint8(data.vthumb_size().v[0]);
  90. }, [&](const MTPDinputSecureFileLocation &data) {
  91. _type = Type::Secure;
  92. _id = data.vid().v;
  93. _accessHash = data.vaccess_hash().v;
  94. }, [&](const MTPDinputTakeoutFileLocation &data) {
  95. _type = Type::Takeout;
  96. }, [&](const MTPDinputPhotoFileLocation &data) {
  97. _type = Type::Photo;
  98. _id = data.vid().v;
  99. _accessHash = data.vaccess_hash().v;
  100. _fileReference = data.vfile_reference().v;
  101. _sizeLetter = data.vthumb_size().v.isEmpty()
  102. ? char(0)
  103. : data.vthumb_size().v[0];
  104. }, [&](const MTPDinputPhotoLegacyFileLocation &data) {
  105. _type = Type::Legacy;
  106. _volumeId = data.vvolume_id().v;
  107. _localId = data.vlocal_id().v;
  108. _accessHash = data.vsecret().v;
  109. _fileReference = data.vfile_reference().v;
  110. }, [&](const MTPDinputPeerPhotoFileLocation &data) {
  111. _type = Type::PeerPhoto;
  112. const auto fillPeer = base::overload([&](
  113. const MTPDinputPeerEmpty &data) {
  114. _id = 0;
  115. }, [&](const MTPDinputPeerSelf &data) {
  116. _id = peerFromUser(self).value;
  117. }, [&](const MTPDinputPeerChat &data) {
  118. _id = peerFromChat(data.vchat_id()).value;
  119. }, [&](const MTPDinputPeerUser &data) {
  120. _id = peerFromUser(data.vuser_id()).value;
  121. _accessHash = data.vaccess_hash().v;
  122. }, [&](const MTPDinputPeerChannel &data) {
  123. _id = peerFromChannel(data.vchannel_id()).value;
  124. _accessHash = data.vaccess_hash().v;
  125. });
  126. data.vpeer().match(fillPeer, [&](
  127. const MTPDinputPeerUserFromMessage &data) {
  128. data.vpeer().match(fillPeer, [&](auto &&) {
  129. // Bad data provided.
  130. _id = _accessHash = 0;
  131. });
  132. _inMessagePeerId = peerFromUser(data.vuser_id());
  133. _inMessageId = data.vmsg_id().v;
  134. }, [&](const MTPDinputPeerChannelFromMessage &data) {
  135. data.vpeer().match(fillPeer, [&](auto &&) {
  136. // Bad data provided.
  137. _id = _accessHash = 0;
  138. });
  139. _inMessagePeerId = peerFromChannel(data.vchannel_id());
  140. _inMessageId = data.vmsg_id().v;
  141. });
  142. _volumeId = data.vphoto_id().v;
  143. _sizeLetter = data.is_big() ? 'c' : 'a';
  144. // _localId place is used in serialization.
  145. Ensures(_localId == 0);
  146. }, [&](const MTPDinputStickerSetThumb &data) {
  147. _type = Type::StickerSetThumb;
  148. data.vstickerset().match([&](const MTPDinputStickerSetEmpty &data) {
  149. _id = 0;
  150. }, [&](const MTPDinputStickerSetID &data) {
  151. _id = data.vid().v;
  152. _accessHash = data.vaccess_hash().v;
  153. }, [&](const auto &data) {
  154. Unexpected("InputStickerSet type in StorageFileLocation.");
  155. });
  156. _volumeId = 0;
  157. _localId = data.vthumb_version().v;
  158. }, [&](const MTPDinputGroupCallStream &data) {
  159. _type = Type::GroupCallStream;
  160. data.vcall().match([&](const MTPDinputGroupCall &data) {
  161. _id = data.vid().v;
  162. _accessHash = data.vaccess_hash().v;
  163. });
  164. _volumeId = data.vtime_ms().v;
  165. _localId = data.vscale().v;
  166. _sizeLetter = uint8(data.vvideo_channel().value_or_empty() & 0x3F)
  167. | uint8((data.vvideo_quality().value_or_empty() & 0x03) << 6);
  168. });
  169. }
  170. StorageFileLocation StorageFileLocation::convertToModernPeerPhoto(
  171. uint64 id,
  172. uint64 accessHash,
  173. uint64 photoId) const {
  174. if (_type != Type::Legacy && _type != Type::PeerPhoto) {
  175. return *this;
  176. } else if (!photoId) {
  177. return StorageFileLocation();
  178. }
  179. auto result = *this;
  180. result._type = Type::PeerPhoto;
  181. result._id = id;
  182. result._accessHash = accessHash;
  183. result._sizeLetter = uint8('a');
  184. result._volumeId = photoId;
  185. result._localId = 0;
  186. result._inMessagePeerId = 0;
  187. result._inMessageId = 0;
  188. return result;
  189. }
  190. int32 StorageFileLocation::dcId() const {
  191. return _dcId;
  192. }
  193. uint64 StorageFileLocation::objectId() const {
  194. return _id;
  195. }
  196. MTPInputFileLocation StorageFileLocation::tl(UserId self) const {
  197. switch (_type) {
  198. case Type::Legacy:
  199. return MTP_inputFileLocation(
  200. MTP_long(_volumeId),
  201. MTP_int(_localId),
  202. MTP_long(_accessHash),
  203. MTP_bytes(_fileReference));
  204. case Type::Encrypted:
  205. return MTP_inputSecureFileLocation(
  206. MTP_long(_id),
  207. MTP_long(_accessHash));
  208. case Type::Document:
  209. return MTP_inputDocumentFileLocation(
  210. MTP_long(_id),
  211. MTP_long(_accessHash),
  212. MTP_bytes(_fileReference),
  213. MTP_string(_sizeLetter
  214. ? std::string(1, char(_sizeLetter))
  215. : std::string()));
  216. case Type::Secure:
  217. return MTP_inputSecureFileLocation(
  218. MTP_long(_id),
  219. MTP_long(_accessHash));
  220. case Type::Takeout:
  221. return MTP_inputTakeoutFileLocation();
  222. case Type::Photo:
  223. return MTP_inputPhotoFileLocation(
  224. MTP_long(_id),
  225. MTP_long(_accessHash),
  226. MTP_bytes(_fileReference),
  227. MTP_string(std::string(1, char(_sizeLetter))));
  228. case Type::PeerPhoto:
  229. return MTP_inputPeerPhotoFileLocation(
  230. MTP_flags((_sizeLetter == 'c')
  231. ? MTPDinputPeerPhotoFileLocation::Flag::f_big
  232. : MTPDinputPeerPhotoFileLocation::Flag(0)),
  233. GenerateInputPeer(
  234. PeerId(_id),
  235. _accessHash,
  236. _inMessagePeerId,
  237. _inMessageId,
  238. self),
  239. MTP_long(_volumeId));
  240. case Type::StickerSetThumb:
  241. return MTP_inputStickerSetThumb(
  242. MTP_inputStickerSetID(MTP_long(_id), MTP_long(_accessHash)),
  243. MTP_int(_localId));
  244. case Type::GroupCallStream:
  245. return MTP_inputGroupCallStream(
  246. MTP_flags((_sizeLetter != 0)
  247. ? (MTPDinputGroupCallStream::Flag::f_video_channel
  248. | MTPDinputGroupCallStream::Flag::f_video_quality)
  249. : MTPDinputGroupCallStream::Flag(0)),
  250. MTP_inputGroupCall(MTP_long(_id), MTP_long(_accessHash)),
  251. MTP_long(_volumeId),
  252. MTP_int(_localId),
  253. MTP_int(_sizeLetter & 0x3F),
  254. MTP_int((_sizeLetter >> 6) & 0x03));
  255. }
  256. Unexpected("Type in StorageFileLocation::tl.");
  257. }
  258. QByteArray StorageFileLocation::serialize() const {
  259. auto result = QByteArray();
  260. if (valid()) {
  261. result.reserve(serializeSize());
  262. auto buffer = QBuffer(&result);
  263. buffer.open(QIODevice::WriteOnly);
  264. auto stream = QDataStream(&buffer);
  265. stream.setVersion(QDataStream::Qt_5_1);
  266. Assert(!(quint8(_type) & kModernLocationFlag)
  267. && !(quint8(_type) & kInMessageFieldsFlag));
  268. auto typeWithFlags = quint8(_type);
  269. typeWithFlags |= kModernLocationFlag;
  270. auto field1 = qint32(_localId);
  271. auto field2 = qint32(0);
  272. if (_inMessagePeerId != 0) {
  273. Assert(field1 == 0);
  274. typeWithFlags |= kInMessageFieldsFlag;
  275. field1 = qint32(uint32(_inMessagePeerId.value >> 32));
  276. field2 = qint32(uint32(_inMessagePeerId.value & 0xFFFFFFFFULL));
  277. }
  278. Assert(typeWithFlags != kNonStorageLocationToken);
  279. stream
  280. << quint16(_dcId)
  281. << typeWithFlags
  282. << quint8(_sizeLetter)
  283. << field1
  284. << quint64(_id)
  285. << quint64(_accessHash)
  286. << quint64(_volumeId)
  287. << field2
  288. << qint32(_inMessageId)
  289. << _fileReference;
  290. }
  291. return result;
  292. }
  293. int StorageFileLocation::serializeSize() const {
  294. return valid()
  295. ? int(sizeof(uint64) * 5 + Serialize::bytearraySize(_fileReference))
  296. : 0;
  297. }
  298. std::optional<StorageFileLocation> StorageFileLocation::FromSerialized(
  299. const QByteArray &serialized) {
  300. if (serialized.isEmpty()) {
  301. return StorageFileLocation();
  302. }
  303. quint16 dcId = 0;
  304. quint8 typeWithFlags = 0;
  305. quint8 sizeLetter = 0;
  306. qint32 field1 = 0;
  307. quint64 id = 0;
  308. quint64 accessHash = 0;
  309. quint64 volumeId = 0;
  310. qint32 field2 = 0;
  311. qint32 inMessageId = 0;
  312. QByteArray fileReference;
  313. auto stream = QDataStream(serialized);
  314. stream.setVersion(QDataStream::Qt_5_1);
  315. stream
  316. >> dcId
  317. >> typeWithFlags;
  318. if (typeWithFlags == kNonStorageLocationToken) {
  319. return std::nullopt;
  320. }
  321. stream
  322. >> sizeLetter
  323. >> field1
  324. >> id
  325. >> accessHash
  326. >> volumeId;
  327. const auto modern = ((typeWithFlags & kModernLocationFlag) != 0);
  328. const auto inMessageFields
  329. = ((typeWithFlags & kInMessageFieldsFlag) != 0);
  330. if (modern) {
  331. stream >> field2 >> inMessageId;
  332. typeWithFlags &= ~kModernLocationFlag;
  333. if (inMessageFields) {
  334. typeWithFlags &= ~kInMessageFieldsFlag;
  335. }
  336. } else if (typeWithFlags & kLegacyInMessagePeerIdFlag) {
  337. typeWithFlags &= ~kLegacyInMessagePeerIdFlag;
  338. stream >> field2 >> inMessageId;
  339. }
  340. stream >> fileReference;
  341. auto result = StorageFileLocation();
  342. result._dcId = dcId;
  343. result._type = Type(typeWithFlags);
  344. result._sizeLetter = sizeLetter;
  345. result._accessHash = accessHash;
  346. result._volumeId = volumeId;
  347. result._inMessageId = inMessageId;
  348. result._fileReference = fileReference;
  349. if (modern) {
  350. result._id = id;
  351. if (inMessageFields) {
  352. result._localId = 0;
  353. result._inMessagePeerId = PeerId(
  354. (uint64(uint32(field1)) << 32) | uint64(uint32(field2)));
  355. } else {
  356. result._localId = field1;
  357. result._inMessagePeerId = 0;
  358. }
  359. } else {
  360. result._id = (result._type == Type::PeerPhoto)
  361. ? DeserializePeerId(id).value
  362. : id;
  363. result._localId = (result._type == Type::PeerPhoto)
  364. ? 0
  365. : field1;
  366. result._inMessagePeerId = (field2 && result._type == Type::PeerPhoto)
  367. ? ((field2 > 0)
  368. ? peerFromUser(UserId(field2))
  369. : peerFromChannel(ChannelId(-field2)))
  370. : PeerId();
  371. }
  372. if (result._type == Type::StickerSetThumb && result._volumeId != 0) {
  373. // Legacy field values that cannot be converted to modern.
  374. // No information about thumb_version, which is required.
  375. return std::nullopt;
  376. }
  377. return (stream.status() == QDataStream::Ok && result.valid())
  378. ? std::make_optional(result)
  379. : std::nullopt;
  380. }
  381. StorageFileLocation::Type StorageFileLocation::type() const {
  382. return _type;
  383. }
  384. bool StorageFileLocation::valid() const {
  385. switch (_type) {
  386. case Type::Legacy:
  387. return (_dcId != 0) && (_volumeId != 0) && (_localId != 0);
  388. case Type::Encrypted:
  389. case Type::Secure:
  390. case Type::Document:
  391. return (_dcId != 0) && (_id != 0);
  392. case Type::Photo:
  393. return (_dcId != 0) && (_id != 0) && (_sizeLetter != 0);
  394. case Type::Takeout:
  395. return true;
  396. case Type::PeerPhoto:
  397. case Type::StickerSetThumb:
  398. return (_dcId != 0) && (_id != 0);
  399. case Type::GroupCallStream:
  400. return (_dcId != 0) && (_id != 0) && (_volumeId != 0);
  401. }
  402. return false;
  403. }
  404. bool StorageFileLocation::isLegacy() const {
  405. return (_type == Type::Legacy);
  406. }
  407. bool StorageFileLocation::isDocumentThumbnail() const {
  408. return (_type == Type::Document) && (_sizeLetter != 0);
  409. }
  410. Storage::Cache::Key StorageFileLocation::cacheKey() const {
  411. using Key = Storage::Cache::Key;
  412. // Skip '1' for legacy document cache keys.
  413. // Skip '2' because it is used for good (fullsize) document thumbnails.
  414. const auto shifted = ((uint64(_type) + 3) << 8);
  415. const auto sliced = uint64(_dcId) & 0xFFULL;
  416. switch (_type) {
  417. case Type::Legacy:
  418. case Type::PeerPhoto:
  419. case Type::StickerSetThumb:
  420. return Key{
  421. shifted | sliced | (uint64(uint32(_localId)) << 16),
  422. _volumeId };
  423. case Type::Encrypted:
  424. case Type::Secure:
  425. return Key{ shifted | sliced, _id };
  426. case Type::Document:
  427. // Keep old cache keys for documents.
  428. if (_sizeLetter == 0) {
  429. return Data::DocumentCacheKey(_dcId, _id);
  430. //return Key{ 0x100ULL | sliced, _id };
  431. }
  432. [[fallthrough]];
  433. case Type::Photo:
  434. return Key{ shifted | sliced | (uint64(_sizeLetter) << 16), _id };
  435. case Type::Takeout:
  436. return Key{ shifted, 0 };
  437. case Type::GroupCallStream:
  438. return Key{
  439. (shifted
  440. | sliced
  441. | (uint32(_localId) << 16)
  442. | (_volumeId << 20)
  443. | (uint64(_sizeLetter) << 56)),
  444. _id };
  445. }
  446. return Key();
  447. }
  448. Storage::Cache::Key StorageFileLocation::bigFileBaseCacheKey() const {
  449. switch (_type) {
  450. case Type::Document: {
  451. const auto high = kDocumentBaseCacheTag
  452. | ((uint64(_dcId) << 16) & kDocumentBaseCacheMask)
  453. | (_id >> 48);
  454. const auto low = (_id << 16);
  455. Ensures((low & 0x1FFULL) == 0);
  456. return Storage::Cache::Key{ high, low };
  457. }
  458. case Type::StickerSetThumb: {
  459. const auto high = (uint64(uint32(_localId)) << 24)
  460. | ((uint64(_type) + 1) << 16)
  461. | ((uint64(_dcId) & 0xFFULL) << 8)
  462. | (_volumeId >> 56);
  463. const auto low = (_volumeId << 8);
  464. Ensures((low & 0xFFULL) == 0);
  465. return Storage::Cache::Key{ high, low };
  466. }
  467. case Type::Photo: {
  468. const auto high = kPhotoBaseCacheTag
  469. | ((uint64(_dcId) << 16) & kPhotoBaseCacheMask)
  470. | (_id >> 48);
  471. const auto low = (_id << 16);
  472. Ensures((low & 0xFFULL) == 0);
  473. return Storage::Cache::Key{ high, low };
  474. }
  475. case Type::Legacy:
  476. case Type::PeerPhoto:
  477. case Type::Encrypted:
  478. case Type::Secure:
  479. case Type::Takeout:
  480. case Type::GroupCallStream:
  481. Unexpected("Not implemented file location type.");
  482. };
  483. Unexpected("Invalid file location type.");
  484. }
  485. QByteArray StorageFileLocation::fileReference() const {
  486. return _fileReference;
  487. }
  488. bool StorageFileLocation::refreshFileReference(
  489. const Data::UpdatedFileReferences &updates) {
  490. const auto i = (_type == Type::Document)
  491. ? updates.data.find(Data::DocumentFileLocationId{ _id })
  492. : (_type == Type::Photo)
  493. ? updates.data.find(Data::PhotoFileLocationId{ _id })
  494. : end(updates.data);
  495. return (i != end(updates.data))
  496. ? refreshFileReference(i->second)
  497. : false;
  498. }
  499. bool StorageFileLocation::refreshFileReference(const QByteArray &data) {
  500. if (data.isEmpty() || _fileReference == data) {
  501. return false;
  502. }
  503. _fileReference = data;
  504. return true;
  505. }
  506. const StorageFileLocation &StorageFileLocation::Invalid() {
  507. static auto result = StorageFileLocation();
  508. return result;
  509. }
  510. bool operator==(const StorageFileLocation &a, const StorageFileLocation &b) {
  511. const auto valid = a.valid();
  512. if (valid != b.valid()) {
  513. return false;
  514. } else if (!valid) {
  515. return true;
  516. }
  517. const auto type = a._type;
  518. if (type != b._type) {
  519. return false;
  520. }
  521. using Type = StorageFileLocation::Type;
  522. switch (type) {
  523. case Type::Legacy:
  524. return (a._dcId == b._dcId)
  525. && (a._volumeId == b._volumeId)
  526. && (a._localId == b._localId);
  527. case Type::Encrypted:
  528. case Type::Secure:
  529. return (a._dcId == b._dcId) && (a._id == b._id);
  530. case Type::Photo:
  531. case Type::Document:
  532. return (a._dcId == b._dcId)
  533. && (a._id == b._id)
  534. && (a._sizeLetter == b._sizeLetter);
  535. case Type::Takeout:
  536. return true;
  537. case Type::PeerPhoto:
  538. return (a._dcId == b._dcId)
  539. && (a._volumeId == b._volumeId)
  540. && (a._localId == b._localId)
  541. && (a._id == b._id)
  542. && (a._sizeLetter == b._sizeLetter);
  543. case Type::StickerSetThumb:
  544. return (a._dcId == b._dcId)
  545. && (a._volumeId == b._volumeId)
  546. && (a._localId == b._localId)
  547. && (a._id == b._id);
  548. case Type::GroupCallStream:
  549. return (a._dcId == b._dcId)
  550. && (a._id == b._id)
  551. && (a._localId == b._localId)
  552. && (a._sizeLetter == b._sizeLetter);
  553. };
  554. Unexpected("Type in StorageFileLocation::operator==.");
  555. }
  556. bool operator<(const StorageFileLocation &a, const StorageFileLocation &b) {
  557. const auto valid = a.valid();
  558. if (valid != b.valid()) {
  559. return !valid;
  560. } else if (!valid) {
  561. return false;
  562. }
  563. const auto type = a._type;
  564. if (type != b._type) {
  565. return (type < b._type);
  566. }
  567. using Type = StorageFileLocation::Type;
  568. switch (type) {
  569. case Type::Legacy:
  570. return std::tie(a._localId, a._volumeId, a._dcId)
  571. < std::tie(b._localId, b._volumeId, b._dcId);
  572. case Type::Encrypted:
  573. case Type::Secure:
  574. return std::tie(a._id, a._dcId) < std::tie(b._id, b._dcId);
  575. case Type::Photo:
  576. case Type::Document:
  577. return std::tie(a._id, a._dcId, a._sizeLetter)
  578. < std::tie(b._id, b._dcId, b._sizeLetter);
  579. case Type::Takeout:
  580. return false;
  581. case Type::PeerPhoto:
  582. return std::tie(
  583. a._id,
  584. a._sizeLetter,
  585. a._localId,
  586. a._volumeId,
  587. a._dcId)
  588. < std::tie(
  589. b._id,
  590. b._sizeLetter,
  591. b._localId,
  592. b._volumeId,
  593. b._dcId);
  594. case Type::StickerSetThumb:
  595. return std::tie(a._id, a._localId, a._volumeId, a._dcId)
  596. < std::tie(b._id, b._localId, b._volumeId, b._dcId);
  597. case Type::GroupCallStream:
  598. return std::tie(a._id, a._localId, a._dcId, a._sizeLetter)
  599. < std::tie(b._id, b._localId, b._dcId, b._sizeLetter);
  600. };
  601. Unexpected("Type in StorageFileLocation::operator==.");
  602. }
  603. InMemoryKey inMemoryKey(const StorageFileLocation &location) {
  604. const auto key = location.cacheKey();
  605. return { key.high, key.low };
  606. }
  607. InMemoryKey inMemoryKey(const WebFileLocation &location) {
  608. auto result = InMemoryKey();
  609. const auto &url = location.url();
  610. const auto sha = hashSha1(url.data(), url.size());
  611. bytes::copy(
  612. bytes::object_as_span(&result),
  613. bytes::make_span(sha).subspan(0, sizeof(result)));
  614. return result;
  615. }
  616. InMemoryKey inMemoryKey(const GeoPointLocation &location) {
  617. return InMemoryKey(
  618. (uint64(base::SafeRound(
  619. std::abs(location.lat + 360.) * 1000000)) << 32)
  620. | uint64(base::SafeRound(std::abs(location.lon + 360.) * 1000000)),
  621. (uint64(location.width) << 32) | uint64(location.height));
  622. }
  623. InMemoryKey inMemoryKey(const PlainUrlLocation &location) {
  624. auto result = InMemoryKey();
  625. const auto &url = location.url;
  626. const auto sha = hashSha1(url.data(), url.size() * sizeof(QChar));
  627. bytes::copy(
  628. bytes::object_as_span(&result),
  629. bytes::make_span(sha).subspan(0, sizeof(result)));
  630. return result;
  631. }
  632. InMemoryKey inMemoryKey(const AudioAlbumThumbLocation &location) {
  633. const auto key = Data::AudioAlbumThumbCacheKey(location);
  634. return { key.high, key.low };
  635. }
  636. InMemoryKey inMemoryKey(const InMemoryLocation &location) {
  637. auto result = InMemoryKey();
  638. const auto &data = location.bytes;
  639. const auto sha = hashSha1(data.data(), data.size());
  640. bytes::copy(
  641. bytes::object_as_span(&result),
  642. bytes::make_span(sha).subspan(0, sizeof(result)));
  643. return result;
  644. }
  645. InMemoryKey inMemoryKey(const DownloadLocation &location) {
  646. return v::match(location.data, [](const auto &data) {
  647. return inMemoryKey(data);
  648. });
  649. }
  650. StorageImageLocation::StorageImageLocation(
  651. const StorageFileLocation &file,
  652. int width,
  653. int height)
  654. : _file(file)
  655. , _width(width)
  656. , _height(height) {
  657. }
  658. QByteArray StorageImageLocation::serialize() const {
  659. auto result = _file.serialize();
  660. if (!result.isEmpty() || (_width > 0) || (_height > 0)) {
  661. result.reserve(result.size() + 2 * sizeof(qint32));
  662. auto buffer = QBuffer(&result);
  663. buffer.open(QIODevice::Append);
  664. auto stream = QDataStream(&buffer);
  665. stream.setVersion(QDataStream::Qt_5_1);
  666. stream << qint32(_width) << qint32(_height);
  667. }
  668. return result;
  669. }
  670. int StorageImageLocation::serializeSize() const {
  671. const auto partial = _file.serializeSize();
  672. return (partial > 0 || _width > 0 || _height > 0)
  673. ? (partial + 2 * sizeof(qint32))
  674. : 0;
  675. }
  676. std::optional<StorageImageLocation> StorageImageLocation::FromSerialized(
  677. const QByteArray &serialized) {
  678. if (const auto file = StorageFileLocation::FromSerialized(serialized)) {
  679. const auto my = 2 * sizeof(qint32);
  680. const auto full = serialized.size();
  681. if (!full) {
  682. return StorageImageLocation(*file, 0, 0);
  683. } else if (full >= my) {
  684. qint32 width = 0;
  685. qint32 height = 0;
  686. const auto dimensions = QByteArray::fromRawData(
  687. serialized.data() + full - my, my);
  688. auto stream = QDataStream(dimensions);
  689. stream.setVersion(QDataStream::Qt_5_1);
  690. stream >> width >> height;
  691. return (stream.status() == QDataStream::Ok)
  692. ? StorageImageLocation(*file, width, height)
  693. : std::optional<StorageImageLocation>();
  694. }
  695. }
  696. return std::nullopt;
  697. }
  698. QByteArray DownloadLocation::serialize() const {
  699. if (!valid() || v::is<StorageFileLocation>(data)) {
  700. return v::get<StorageFileLocation>(data).serialize();
  701. }
  702. auto result = QByteArray();
  703. auto buffer = QBuffer(&result);
  704. buffer.open(QIODevice::WriteOnly);
  705. auto stream = QDataStream(&buffer);
  706. stream.setVersion(QDataStream::Qt_5_1);
  707. stream << quint16(0) << kNonStorageLocationToken;
  708. v::match(data, [&](const StorageFileLocation &data) {
  709. Unexpected("Variant in DownloadLocation::serialize.");
  710. }, [&](const WebFileLocation &data) {
  711. stream
  712. << quint8(NonStorageLocationType::Web)
  713. << data.url()
  714. << quint64(data.accessHash());
  715. }, [&](const GeoPointLocation &data) {
  716. stream
  717. << quint8(NonStorageLocationType::Geo)
  718. << qreal(data.lat)
  719. << qreal(data.lon)
  720. << quint64(data.access)
  721. << qint32(data.width)
  722. << qint32(data.height)
  723. << qint32(data.zoom)
  724. << qint32(data.scale);
  725. }, [&](const AudioAlbumThumbLocation &data) {
  726. stream
  727. << quint8(NonStorageLocationType::AudioAlbumThumb)
  728. << quint64(data.documentId);
  729. }, [&](const PlainUrlLocation &data) {
  730. stream << quint8(NonStorageLocationType::Url) << data.url.toUtf8();
  731. }, [&](const InMemoryLocation &data) {
  732. stream << quint8(NonStorageLocationType::Memory) << data.bytes;
  733. });
  734. buffer.close();
  735. return result;
  736. }
  737. int DownloadLocation::serializeSize() const {
  738. if (!valid() || v::is<StorageFileLocation>(data)) {
  739. return v::get<StorageFileLocation>(data).serializeSize();
  740. }
  741. auto result = sizeof(quint16) + sizeof(quint8) + sizeof(quint8);
  742. v::match(data, [&](const StorageFileLocation &data) {
  743. Unexpected("Variant in DownloadLocation::serializeSize.");
  744. }, [&](const WebFileLocation &data) {
  745. result += Serialize::bytearraySize(data.url()) + sizeof(quint64);
  746. }, [&](const GeoPointLocation &data) {
  747. result += 2 * sizeof(qreal) + sizeof(quint64) + 4 * sizeof(qint32);
  748. }, [&](const PlainUrlLocation &data) {
  749. result += Serialize::bytearraySize(data.url.toUtf8());
  750. }, [&](const AudioAlbumThumbLocation &data) {
  751. result += sizeof(quint64);
  752. }, [&](const InMemoryLocation &data) {
  753. result += Serialize::bytearraySize(data.bytes);
  754. });
  755. return result;
  756. }
  757. std::optional<DownloadLocation> DownloadLocation::FromSerialized(
  758. const QByteArray &serialized) {
  759. quint16 dcId = 0;
  760. quint8 token = 0;
  761. auto stream = QDataStream(serialized);
  762. stream.setVersion(QDataStream::Qt_5_1);
  763. stream >> dcId >> token;
  764. if (dcId != 0 || token != kNonStorageLocationToken) {
  765. const auto storage = StorageFileLocation::FromSerialized(serialized);
  766. return storage
  767. ? std::make_optional(DownloadLocation{ *storage })
  768. : std::nullopt;
  769. }
  770. quint8 type = 0;
  771. stream >> type;
  772. switch (NonStorageLocationType(type)) {
  773. case NonStorageLocationType::Web: {
  774. QByteArray url;
  775. quint64 accessHash = 0;
  776. stream >> url >> accessHash;
  777. return (stream.status() == QDataStream::Ok)
  778. ? std::make_optional(
  779. DownloadLocation{ WebFileLocation(url, accessHash) })
  780. : std::nullopt;
  781. } break;
  782. case NonStorageLocationType::Geo: {
  783. qreal lat = 0.;
  784. qreal lon = 0.;
  785. quint64 access = 0;
  786. qint32 width = 0;
  787. qint32 height = 0;
  788. qint32 zoom = 0;
  789. qint32 scale = 0;
  790. stream >> lat >> lon >> access >> width >> height >> zoom >> scale;
  791. return (stream.status() == QDataStream::Ok)
  792. ? std::make_optional(
  793. DownloadLocation{ GeoPointLocation{
  794. .lat = lat,
  795. .lon = lon,
  796. .access = access,
  797. .width = width,
  798. .height = height,
  799. .zoom = zoom,
  800. .scale = scale } })
  801. : std::nullopt;
  802. } break;
  803. case NonStorageLocationType::AudioAlbumThumb: {
  804. quint64 id = 0;
  805. stream >> id;
  806. return (stream.status() == QDataStream::Ok)
  807. ? std::make_optional(DownloadLocation{
  808. AudioAlbumThumbLocation{ id } })
  809. : std::nullopt;
  810. } break;
  811. case NonStorageLocationType::Url: {
  812. QByteArray utf;
  813. stream >> utf;
  814. const auto url = base::FromUtf8Safe(utf);
  815. return (stream.status() == QDataStream::Ok)
  816. ? std::make_optional(DownloadLocation{ PlainUrlLocation{ url } })
  817. : std::nullopt;
  818. } break;
  819. case NonStorageLocationType::Memory: {
  820. QByteArray bytes;
  821. stream >> bytes;
  822. return (stream.status() == QDataStream::Ok)
  823. ? std::make_optional(
  824. DownloadLocation{ InMemoryLocation{ bytes } })
  825. : std::nullopt;
  826. } break;
  827. }
  828. return std::nullopt;
  829. }
  830. DownloadLocation DownloadLocation::convertToModernPeerPhoto(
  831. uint64 id,
  832. uint64 accessHash,
  833. uint64 photoId) const {
  834. if (!v::is<StorageFileLocation>(data)) {
  835. return *this;
  836. }
  837. auto &file = v::get<StorageFileLocation>(data);
  838. return DownloadLocation{
  839. file.convertToModernPeerPhoto(id, accessHash, photoId)
  840. };
  841. }
  842. Storage::Cache::Key DownloadLocation::cacheKey() const {
  843. return v::match(data, [](const GeoPointLocation &data) {
  844. return Data::GeoPointCacheKey(data);
  845. }, [](const StorageFileLocation &data) {
  846. return data.valid()
  847. ? data.cacheKey()
  848. : Storage::Cache::Key();
  849. }, [](const WebFileLocation &data) {
  850. return data.isNull()
  851. ? Storage::Cache::Key()
  852. : Data::WebDocumentCacheKey(data);
  853. }, [](const PlainUrlLocation &data) {
  854. return data.url.isEmpty()
  855. ? Storage::Cache::Key()
  856. : Data::UrlCacheKey(data.url);
  857. }, [](const AudioAlbumThumbLocation &data) {
  858. return Data::AudioAlbumThumbCacheKey(data);
  859. }, [](const InMemoryLocation &data) {
  860. return Storage::Cache::Key();
  861. });
  862. }
  863. Storage::Cache::Key DownloadLocation::bigFileBaseCacheKey() const {
  864. return v::is<StorageFileLocation>(data)
  865. ? v::get<StorageFileLocation>(data).bigFileBaseCacheKey()
  866. : Storage::Cache::Key();
  867. }
  868. bool DownloadLocation::valid() const {
  869. return v::match(data, [](const GeoPointLocation &data) {
  870. return true;
  871. }, [](const StorageFileLocation &data) {
  872. return data.valid();
  873. }, [](const WebFileLocation &data) {
  874. return !data.isNull();
  875. }, [](const PlainUrlLocation &data) {
  876. return !data.url.isEmpty();
  877. }, [](const AudioAlbumThumbLocation &data) {
  878. return data.documentId != 0;
  879. }, [](const InMemoryLocation &data) {
  880. return !data.bytes.isEmpty();
  881. });
  882. }
  883. bool DownloadLocation::isLegacy() const {
  884. return v::is<StorageFileLocation>(data)
  885. ? v::get<StorageFileLocation>(data).isLegacy()
  886. : false;
  887. }
  888. QByteArray DownloadLocation::fileReference() const {
  889. if (!v::is<StorageFileLocation>(data)) {
  890. return QByteArray();
  891. }
  892. return v::get<StorageFileLocation>(data).fileReference();
  893. }
  894. bool DownloadLocation::refreshFileReference(const QByteArray &data) {
  895. if (!v::is<StorageFileLocation>(this->data)) {
  896. return false;
  897. }
  898. auto &file = v::get<StorageFileLocation>(this->data);
  899. return file.refreshFileReference(data);
  900. }
  901. bool DownloadLocation::refreshFileReference(
  902. const Data::UpdatedFileReferences &updates) {
  903. if (!v::is<StorageFileLocation>(data)) {
  904. return false;
  905. }
  906. auto &file = v::get<StorageFileLocation>(data);
  907. return file.refreshFileReference(updates);
  908. }
  909. ImageLocation::ImageLocation(
  910. const DownloadLocation &file,
  911. int width,
  912. int height)
  913. : _file(file)
  914. , _width(width)
  915. , _height(height) {
  916. }
  917. QByteArray ImageLocation::serialize() const {
  918. auto result = _file.serialize();
  919. if (!result.isEmpty() || (_width > 0) || (_height > 0)) {
  920. result.reserve(result.size() + 2 * sizeof(qint32));
  921. auto buffer = QBuffer(&result);
  922. buffer.open(QIODevice::Append);
  923. auto stream = QDataStream(&buffer);
  924. stream.setVersion(QDataStream::Qt_5_1);
  925. stream << qint32(_width) << qint32(_height);
  926. }
  927. return result;
  928. }
  929. int ImageLocation::serializeSize() const {
  930. const auto partial = _file.serializeSize();
  931. return (partial > 0 || _width > 0 || _height > 0)
  932. ? (partial + 2 * sizeof(qint32))
  933. : 0;
  934. }
  935. std::optional<ImageLocation> ImageLocation::FromSerialized(
  936. const QByteArray &serialized) {
  937. if (const auto file = DownloadLocation::FromSerialized(serialized)) {
  938. const auto my = 2 * sizeof(qint32);
  939. const auto full = serialized.size();
  940. if (!full) {
  941. return ImageLocation(*file, 0, 0);
  942. } else if (full >= my) {
  943. qint32 width = 0;
  944. qint32 height = 0;
  945. const auto dimensions = QByteArray::fromRawData(
  946. serialized.data() + full - my, my);
  947. auto stream = QDataStream(dimensions);
  948. stream.setVersion(QDataStream::Qt_5_1);
  949. stream >> width >> height;
  950. return (stream.status() == QDataStream::Ok)
  951. ? ImageLocation(*file, width, height)
  952. : std::optional<ImageLocation>();
  953. }
  954. }
  955. return std::nullopt;
  956. }