// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" #include "td/telegram/DialogId.h" #include "td/telegram/files/FileBitmask.h" #include "td/telegram/net/DcId.h" #include "td/telegram/SecureStorage.h" #include "td/utils/as.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/crypto.h" #include "td/utils/format.h" #include "td/utils/int_types.h" #include "td/utils/logging.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" #include "td/utils/tl_storers.h" #include "td/utils/UInt.h" #include "td/utils/Variant.h" #include namespace td { enum class FileType : int8 { Thumbnail, ProfilePhoto, Photo, VoiceNote, Video, Document, Encrypted, Temp, Sticker, Audio, Animation, EncryptedThumbnail, Wallpaper, VideoNote, SecureRaw, Secure, Size, None }; inline FileType from_td_api(const td_api::FileType &file_type) { switch (file_type.get_id()) { case td_api::fileTypeThumbnail::ID: return FileType::Thumbnail; case td_api::fileTypeProfilePhoto::ID: return FileType::ProfilePhoto; case td_api::fileTypePhoto::ID: return FileType::Photo; case td_api::fileTypeVoiceNote::ID: return FileType::VoiceNote; case td_api::fileTypeVideo::ID: return FileType::Video; case td_api::fileTypeDocument::ID: return FileType::Document; case td_api::fileTypeSecret::ID: return FileType::Encrypted; case td_api::fileTypeUnknown::ID: return FileType::Temp; case td_api::fileTypeSticker::ID: return FileType::Sticker; case td_api::fileTypeAudio::ID: return FileType::Audio; case td_api::fileTypeAnimation::ID: return FileType::Animation; case td_api::fileTypeSecretThumbnail::ID: return FileType::EncryptedThumbnail; case td_api::fileTypeWallpaper::ID: return FileType::Wallpaper; case td_api::fileTypeVideoNote::ID: return FileType::VideoNote; case td_api::fileTypeSecure::ID: return FileType::Secure; case td_api::fileTypeNone::ID: return FileType::None; default: UNREACHABLE(); return FileType::None; } } inline tl_object_ptr as_td_api(FileType file_type) { switch (file_type) { case FileType::Thumbnail: return make_tl_object(); case FileType::ProfilePhoto: return make_tl_object(); case FileType::Photo: return make_tl_object(); case FileType::VoiceNote: return make_tl_object(); case FileType::Video: return make_tl_object(); case FileType::Document: return make_tl_object(); case FileType::Encrypted: return make_tl_object(); case FileType::Temp: return make_tl_object(); case FileType::Sticker: return make_tl_object(); case FileType::Audio: return make_tl_object(); case FileType::Animation: return make_tl_object(); case FileType::EncryptedThumbnail: return make_tl_object(); case FileType::Wallpaper: return make_tl_object(); case FileType::VideoNote: return make_tl_object(); case FileType::Secure: return make_tl_object(); case FileType::SecureRaw: UNREACHABLE(); return make_tl_object(); case FileType::None: return make_tl_object(); default: UNREACHABLE(); return nullptr; } } constexpr int32 file_type_size = static_cast(FileType::Size); extern const char *file_type_name[file_type_size]; inline StringBuilder &operator<<(StringBuilder &string_builder, FileType file_type) { return string_builder << file_type_name[static_cast(file_type)]; } enum class FileDirType : int8 { Secure, Common }; inline FileDirType get_file_dir_type(FileType file_type) { switch (file_type) { case FileType::Thumbnail: case FileType::ProfilePhoto: case FileType::Encrypted: case FileType::Sticker: case FileType::Temp: case FileType::Wallpaper: case FileType::EncryptedThumbnail: case FileType::Secure: case FileType::SecureRaw: return FileDirType::Secure; default: return FileDirType::Common; } } struct FileEncryptionKey { enum class Type : int32 { None, Secret, Secure }; FileEncryptionKey() = default; FileEncryptionKey(Slice key, Slice iv) : key_iv_(key.size() + iv.size(), '\0'), type_(Type::Secret) { if (key.size() != 32 || iv.size() != 32) { LOG(ERROR) << "Wrong key/iv sizes: " << key.size() << " " << iv.size(); type_ = Type::None; return; } CHECK(key_iv_.size() == 64); MutableSlice(key_iv_).copy_from(key); MutableSlice(key_iv_).substr(key.size()).copy_from(iv); } explicit FileEncryptionKey(const secure_storage::Secret &secret) : type_(Type::Secure) { key_iv_ = secret.as_slice().str(); } bool is_secret() const { return type_ == Type::Secret; } bool is_secure() const { return type_ == Type::Secure; } static FileEncryptionKey create() { FileEncryptionKey res; res.key_iv_.resize(64); Random::secure_bytes(res.key_iv_); res.type_ = Type::Secret; return res; } static FileEncryptionKey create_secure_key() { return FileEncryptionKey(secure_storage::Secret::create_new()); } const UInt256 &key() const { CHECK(is_secret()); CHECK(key_iv_.size() == 64); return *reinterpret_cast(key_iv_.data()); } Slice key_slice() const { CHECK(is_secret()); CHECK(key_iv_.size() == 64); return Slice(key_iv_.data(), 32); } secure_storage::Secret secret() const { CHECK(is_secure()); return secure_storage::Secret::create(Slice(key_iv_).truncate(32)).move_as_ok(); } bool has_value_hash() const { CHECK(is_secure()); return key_iv_.size() > secure_storage::Secret::size(); } void set_value_hash(const secure_storage::ValueHash &value_hash) { key_iv_.resize(secure_storage::Secret::size() + value_hash.as_slice().size()); MutableSlice(key_iv_).remove_prefix(secure_storage::Secret::size()).copy_from(value_hash.as_slice()); } secure_storage::ValueHash value_hash() const { CHECK(has_value_hash()); return secure_storage::ValueHash::create(Slice(key_iv_).remove_prefix(secure_storage::Secret::size())).move_as_ok(); } UInt256 &mutable_iv() { CHECK(is_secret()); CHECK(key_iv_.size() == 64); return *reinterpret_cast(&key_iv_[0] + 32); } Slice iv_slice() const { CHECK(is_secret()); CHECK(key_iv_.size() == 64); return Slice(key_iv_.data() + 32, 32); } int32 calc_fingerprint() const { CHECK(is_secret()); char buf[16]; md5(key_iv_, {buf, sizeof(buf)}); return as(buf) ^ as(buf + 4); } bool empty() const { return key_iv_.empty(); } size_t size() const { return key_iv_.size(); } template void store(StorerT &storer) const { td::store(key_iv_, storer); } template void parse(Type type, ParserT &parser) { td::parse(key_iv_, parser); if (key_iv_.empty()) { type_ = Type::None; } else { if (type_ == Type::Secure) { if (key_iv_.size() != 64) { LOG(ERROR) << "Have wrong key size " << key_iv_.size(); } } type_ = type; } } string key_iv_; // TODO wrong alignment is possible Type type_ = Type::None; }; inline bool operator==(const FileEncryptionKey &lhs, const FileEncryptionKey &rhs) { return lhs.key_iv_ == rhs.key_iv_; } inline bool operator!=(const FileEncryptionKey &lhs, const FileEncryptionKey &rhs) { return !(lhs == rhs); } inline StringBuilder &operator<<(StringBuilder &string_builder, const FileEncryptionKey &key) { if (key.is_secret()) { return string_builder << "SecretKey{" << key.size() << "}"; } if (key.is_secret()) { return string_builder << "SecureKey{" << key.size() << "}"; } return string_builder << "NoKey{}"; } struct EmptyRemoteFileLocation { template void store(StorerT &storer) const { } template void parse(ParserT &parser) { } }; inline bool operator==(const EmptyRemoteFileLocation &lhs, const EmptyRemoteFileLocation &rhs) { return true; } inline bool operator!=(const EmptyRemoteFileLocation &lhs, const EmptyRemoteFileLocation &rhs) { return !(lhs == rhs); } struct PartialRemoteFileLocation { int64 file_id_; int32 part_count_; int32 part_size_; int32 ready_part_count_; int32 is_big_; template void store(StorerT &storer) const { using td::store; store(file_id_, storer); store(part_count_, storer); store(part_size_, storer); store(ready_part_count_, storer); store(is_big_, storer); } template void parse(ParserT &parser) { using td::parse; parse(file_id_, parser); parse(part_count_, parser); parse(part_size_, parser); parse(ready_part_count_, parser); parse(is_big_, parser); } }; inline bool operator==(const PartialRemoteFileLocation &lhs, const PartialRemoteFileLocation &rhs) { return lhs.file_id_ == rhs.file_id_ && lhs.part_count_ == rhs.part_count_ && lhs.part_size_ == rhs.part_size_ && lhs.ready_part_count_ == rhs.ready_part_count_ && lhs.is_big_ == rhs.is_big_; } inline bool operator!=(const PartialRemoteFileLocation &lhs, const PartialRemoteFileLocation &rhs) { return !(lhs == rhs); } struct PhotoRemoteFileLocation { int64 id_; int64 access_hash_; int64 volume_id_; int64 secret_; int32 local_id_; template void store(StorerT &storer) const { using td::store; store(id_, storer); store(access_hash_, storer); store(volume_id_, storer); store(secret_, storer); store(local_id_, storer); } template void parse(ParserT &parser) { using td::parse; parse(id_, parser); parse(access_hash_, parser); parse(volume_id_, parser); parse(secret_, parser); parse(local_id_, parser); } struct AsKey { const PhotoRemoteFileLocation &key; template void store(StorerT &storer) const { using td::store; store(key.id_, storer); store(key.volume_id_, storer); store(key.local_id_, storer); } }; AsKey as_key() const { return AsKey{*this}; } bool operator<(const PhotoRemoteFileLocation &other) const { return std::tie(id_, volume_id_, local_id_) < std::tie(other.id_, other.volume_id_, other.local_id_); } bool operator==(const PhotoRemoteFileLocation &other) const { return std::tie(id_, volume_id_, local_id_) == std::tie(other.id_, other.volume_id_, other.local_id_); } }; inline StringBuilder &operator<<(StringBuilder &string_builder, const PhotoRemoteFileLocation &location) { return string_builder << "[id = " << location.id_ << ", access_hash = " << location.access_hash_ << ", volume_id = " << location.volume_id_ << ", local_id = " << location.local_id_ << "]"; } struct WebRemoteFileLocation { string url_; int64 access_hash_; template void store(StorerT &storer) const { using td::store; store(url_, storer); store(access_hash_, storer); } template void parse(ParserT &parser) { using td::parse; parse(url_, parser); parse(access_hash_, parser); } struct AsKey { const WebRemoteFileLocation &key; template void store(StorerT &storer) const { using td::store; store(key.url_, storer); } }; AsKey as_key() const { return AsKey{*this}; } bool operator<(const WebRemoteFileLocation &other) const { return url_ < other.url_; } bool operator==(const WebRemoteFileLocation &other) const { return url_ == other.url_; } }; inline StringBuilder &operator<<(StringBuilder &string_builder, const WebRemoteFileLocation &location) { return string_builder << "[url = " << location.url_ << ", access_hash = " << location.access_hash_ << "]"; } struct CommonRemoteFileLocation { int64 id_; int64 access_hash_; template void store(StorerT &storer) const { using td::store; store(id_, storer); store(access_hash_, storer); } template void parse(ParserT &parser) { using td::parse; parse(id_, parser); parse(access_hash_, parser); } struct AsKey { const CommonRemoteFileLocation &key; template void store(StorerT &storer) const { td::store(key.id_, storer); } }; AsKey as_key() const { return AsKey{*this}; } bool operator<(const CommonRemoteFileLocation &other) const { return std::tie(id_) < std::tie(other.id_); } bool operator==(const CommonRemoteFileLocation &other) const { return std::tie(id_) == std::tie(other.id_); } }; inline StringBuilder &operator<<(StringBuilder &string_builder, const CommonRemoteFileLocation &location) { return string_builder << "[id = " << location.id_ << ", access_hash = " << location.access_hash_ << "]"; } class FullRemoteFileLocation { public: FileType file_type_{FileType::None}; private: static constexpr int32 WEB_LOCATION_FLAG = 1 << 24; bool web_location_flag_{false}; DcId dc_id_; enum class LocationType : int32 { Web, Photo, Common, None }; Variant variant_; LocationType location_type() const { if (is_web()) { return LocationType::Web; } switch (file_type_) { case FileType::Photo: case FileType::ProfilePhoto: case FileType::Thumbnail: case FileType::EncryptedThumbnail: case FileType::Wallpaper: return LocationType::Photo; case FileType::Video: case FileType::VoiceNote: case FileType::Document: case FileType::Sticker: case FileType::Audio: case FileType::Animation: case FileType::Encrypted: case FileType::VideoNote: case FileType::SecureRaw: case FileType::Secure: return LocationType::Common; case FileType::None: case FileType::Size: default: UNREACHABLE(); case FileType::Temp: return LocationType::None; } } WebRemoteFileLocation &web() { return variant_.get(); } PhotoRemoteFileLocation &photo() { return variant_.get(); } CommonRemoteFileLocation &common() { return variant_.get(); } const WebRemoteFileLocation &web() const { return variant_.get(); } const PhotoRemoteFileLocation &photo() const { return variant_.get(); } const CommonRemoteFileLocation &common() const { return variant_.get(); } friend StringBuilder &operator<<(StringBuilder &string_builder, const FullRemoteFileLocation &full_remote_file_location); int32 full_type() const { auto type = static_cast(file_type_); if (is_web()) { type |= WEB_LOCATION_FLAG; } return type; } public: template void store(StorerT &storer) const { using ::td::store; store(full_type(), storer); store(dc_id_.get_value(), storer); variant_.visit([&](auto &&value) { using td::store; store(value, storer); }); } template void parse(ParserT &parser) { using ::td::parse; int32 raw_type; parse(raw_type, parser); web_location_flag_ = (raw_type & WEB_LOCATION_FLAG) != 0; raw_type &= ~WEB_LOCATION_FLAG; if (raw_type < 0 || raw_type >= static_cast(FileType::Size)) { return parser.set_error("Invalid FileType in FullRemoteFileLocation"); } file_type_ = static_cast(raw_type); int32 dc_id_value; parse(dc_id_value, parser); dc_id_ = DcId::from_value(dc_id_value); switch (location_type()) { case LocationType::Web: { variant_ = WebRemoteFileLocation(); return web().parse(parser); } case LocationType::Photo: { variant_ = PhotoRemoteFileLocation(); return photo().parse(parser); } case LocationType::Common: { variant_ = CommonRemoteFileLocation(); return common().parse(parser); } case LocationType::None: { break; } } parser.set_error("Invalid FileType in FullRemoteFileLocation"); } struct AsKey { const FullRemoteFileLocation &key; template void store(StorerT &storer) const { using td::store; store(key.full_type(), storer); key.variant_.visit([&](auto &&value) { using td::store; store(value.as_key(), storer); }); } }; AsKey as_key() const { return AsKey{*this}; } DcId get_dc_id() const { CHECK(!is_web()); return dc_id_; } int64 get_access_hash() const { switch (location_type()) { case LocationType::Photo: return photo().access_hash_; case LocationType::Common: return common().access_hash_; case LocationType::Web: return web().access_hash_; case LocationType::None: default: UNREACHABLE(); return 0; } } int64 get_id() const { switch (location_type()) { case LocationType::Photo: return photo().id_; case LocationType::Common: return common().id_; case LocationType::Web: case LocationType::None: default: UNREACHABLE(); return 0; } } string get_url() const { if (is_web()) { return web().url_; } return string(); } bool is_web() const { return web_location_flag_; } bool is_photo() const { return location_type() == LocationType::Photo; } bool is_common() const { return location_type() == LocationType::Common; } bool is_encrypted_secret() const { return file_type_ == FileType::Encrypted; } bool is_encrypted_secure() const { return file_type_ == FileType::Secure; } bool is_encrypted_any() const { return is_encrypted_secret() || is_encrypted_secure(); } bool is_secure() const { return file_type_ == FileType::SecureRaw || file_type_ == FileType::Secure; } bool is_document() const { return is_common() && !is_secure() && !is_encrypted_secret(); } #define as_input_web_file_location() as_input_web_file_location_impl(__FILE__, __LINE__) tl_object_ptr as_input_web_file_location_impl(const char *file, int line) const { CHECK(is_web()) << file << ' ' << line; return make_tl_object(web().url_, web().access_hash_); } tl_object_ptr as_input_file_location() const { switch (location_type()) { case LocationType::Photo: return make_tl_object(photo().volume_id_, photo().local_id_, photo().secret_); case LocationType::Common: if (is_encrypted_secret()) { return make_tl_object(common().id_, common().access_hash_); } else if (is_secure()) { return make_tl_object(common().id_, common().access_hash_); } else { return make_tl_object(common().id_, common().access_hash_, 0); } case LocationType::Web: case LocationType::None: default: UNREACHABLE(); return nullptr; } } #define as_input_document() as_input_document_impl(__FILE__, __LINE__) tl_object_ptr as_input_document_impl(const char *file, int line) const { CHECK(is_common()) << file << ' ' << line; CHECK(is_document()) << file << ' ' << line; return make_tl_object(common().id_, common().access_hash_); } #define as_input_photo() as_input_photo_impl(__FILE__, __LINE__) tl_object_ptr as_input_photo_impl(const char *file, int line) const { CHECK(is_photo()) << file << ' ' << line; return make_tl_object(photo().id_, photo().access_hash_); } tl_object_ptr as_input_encrypted_file() const { CHECK(is_encrypted_secret()); return make_tl_object(common().id_, common().access_hash_); } #define as_input_secure_file() as_input_secure_file_impl(__FILE__, __LINE__) tl_object_ptr as_input_secure_file_impl(const char *file, int line) const { CHECK(is_secure()) << file << ' ' << line; return make_tl_object(common().id_, common().access_hash_); } // TODO: this constructor is just for immediate unserialize FullRemoteFileLocation() = default; FullRemoteFileLocation(FileType file_type, int64 id, int64 access_hash, int32 local_id, int64 volume_id, int64 secret, DcId dc_id) : file_type_(file_type) , dc_id_(dc_id) , variant_(PhotoRemoteFileLocation{id, access_hash, volume_id, secret, local_id}) { CHECK(is_photo()); } FullRemoteFileLocation(FileType file_type, int64 id, int64 access_hash, DcId dc_id) : file_type_(file_type), dc_id_(dc_id), variant_(CommonRemoteFileLocation{id, access_hash}) { CHECK(is_common()); } FullRemoteFileLocation(FileType file_type, string url, int64 access_hash) : file_type_(file_type) , web_location_flag_{true} , dc_id_() , variant_(WebRemoteFileLocation{std::move(url), access_hash}) { CHECK(is_web()); CHECK(!web().url_.empty()); } bool operator<(const FullRemoteFileLocation &other) const { if (full_type() != other.full_type()) { return full_type() < other.full_type(); } if (dc_id_ != other.dc_id_) { return dc_id_ < other.dc_id_; } switch (location_type()) { case LocationType::Photo: return photo() < other.photo(); case LocationType::Common: return common() < other.common(); case LocationType::Web: return web() < other.web(); case LocationType::None: default: UNREACHABLE(); return false; } } bool operator==(const FullRemoteFileLocation &other) const { if (full_type() != other.full_type()) { return false; } if (dc_id_ != other.dc_id_) { return false; } switch (location_type()) { case LocationType::Photo: return photo() == other.photo(); case LocationType::Common: return common() == other.common(); case LocationType::Web: return web() == other.web(); case LocationType::None: default: UNREACHABLE(); return false; } } static const int32 KEY_MAGIC = 0x64374632; }; inline StringBuilder &operator<<(StringBuilder &string_builder, const FullRemoteFileLocation &full_remote_file_location) { string_builder << "[" << full_remote_file_location.file_type_; if (!full_remote_file_location.is_web()) { string_builder << ", " << full_remote_file_location.get_dc_id(); } string_builder << ", location = "; if (full_remote_file_location.is_web()) { string_builder << full_remote_file_location.web(); } else if (full_remote_file_location.is_photo()) { string_builder << full_remote_file_location.photo(); } else if (full_remote_file_location.is_common()) { string_builder << full_remote_file_location.common(); } return string_builder << "]"; } class RemoteFileLocation { public: enum class Type : int32 { Empty, Partial, Full }; Type type() const { return static_cast(variant_.get_offset()); } template void store(StorerT &storer) const { storer.store_int(variant_.get_offset()); bool ok{false}; variant_.visit([&](auto &&value) { using td::store; store(value, storer); ok = true; }); CHECK(ok); } PartialRemoteFileLocation &partial() { return variant_.get<1>(); } FullRemoteFileLocation &full() { return variant_.get<2>(); } const PartialRemoteFileLocation &partial() const { return variant_.get<1>(); } const FullRemoteFileLocation &full() const { return variant_.get<2>(); } template void parse(ParserT &parser) { auto type = static_cast(parser.fetch_int()); switch (type) { case Type::Empty: { variant_ = EmptyRemoteFileLocation(); return; } case Type::Partial: { variant_ = PartialRemoteFileLocation(); return partial().parse(parser); } case Type::Full: { variant_ = FullRemoteFileLocation(); return full().parse(parser); } } parser.set_error("Invalid type in RemoteFileLocation"); } RemoteFileLocation() : variant_{EmptyRemoteFileLocation{}} { } explicit RemoteFileLocation(const FullRemoteFileLocation &full) : variant_(full) { } explicit RemoteFileLocation(const PartialRemoteFileLocation &partial) : variant_(partial) { } RemoteFileLocation(FileType file_type, int64 id, int64 access_hash, int32 local_id, int64 volume_id, int64 secret, DcId dc_id) : variant_(FullRemoteFileLocation{file_type, id, access_hash, local_id, volume_id, secret, dc_id}) { } RemoteFileLocation(FileType file_type, int64 id, int64 access_hash, DcId dc_id) : variant_(FullRemoteFileLocation{file_type, id, access_hash, dc_id}) { } private: Variant variant_; friend bool operator==(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs); }; inline bool operator==(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs) { return lhs.variant_ == rhs.variant_; } inline bool operator!=(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs) { return !(lhs == rhs); } struct EmptyLocalFileLocation { template void store(StorerT &storer) const { } template void parse(ParserT &parser) { } }; inline bool operator==(const EmptyLocalFileLocation &lhs, const EmptyLocalFileLocation &rhs) { return true; } inline bool operator!=(const EmptyLocalFileLocation &lhs, const EmptyLocalFileLocation &rhs) { return !(lhs == rhs); } struct PartialLocalFileLocation { FileType file_type_; int32 part_size_; string path_; string iv_; string ready_bitmask_; template void store(StorerT &storer) const { using td::store; store(file_type_, storer); store(path_, storer); store(part_size_, storer); int32 deprecated_ready_part_count = -1; store(deprecated_ready_part_count, storer); store(iv_, storer); store(ready_bitmask_, storer); } template void parse(ParserT &parser) { using td::parse; parse(file_type_, parser); if (file_type_ < FileType::Thumbnail || file_type_ >= FileType::Size) { return parser.set_error("Invalid type in PartialLocalFileLocation"); } parse(path_, parser); parse(part_size_, parser); int32 deprecated_ready_part_count; parse(deprecated_ready_part_count, parser); parse(iv_, parser); if (deprecated_ready_part_count == -1) { parse(ready_bitmask_, parser); } else { CHECK(0 <= deprecated_ready_part_count); CHECK(deprecated_ready_part_count <= (1 << 22)); ready_bitmask_ = Bitmask(Bitmask::Ones{}, deprecated_ready_part_count).encode(); } } }; inline bool operator==(const PartialLocalFileLocation &lhs, const PartialLocalFileLocation &rhs) { return lhs.file_type_ == rhs.file_type_ && lhs.path_ == rhs.path_ && lhs.part_size_ == rhs.part_size_ && lhs.iv_ == rhs.iv_ && lhs.ready_bitmask_ == rhs.ready_bitmask_; } inline bool operator!=(const PartialLocalFileLocation &lhs, const PartialLocalFileLocation &rhs) { return !(lhs == rhs); } struct FullLocalFileLocation { FileType file_type_; string path_; uint64 mtime_nsec_; template void store(StorerT &storer) const { using td::store; store(file_type_, storer); store(mtime_nsec_, storer); store(path_, storer); } template void parse(ParserT &parser) { using td::parse; parse(file_type_, parser); if (file_type_ < FileType::Thumbnail || file_type_ >= FileType::Size) { return parser.set_error("Invalid type in FullLocalFileLocation"); } parse(mtime_nsec_, parser); parse(path_, parser); } const FullLocalFileLocation &as_key() const { return *this; } // TODO: remove this constructor FullLocalFileLocation() : file_type_(FileType::Photo) { } FullLocalFileLocation(FileType file_type, string path, uint64 mtime_nsec) : file_type_(file_type), path_(std::move(path)), mtime_nsec_(mtime_nsec) { } static const int32 KEY_MAGIC = 0x84373817; }; inline bool operator<(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) { return std::tie(lhs.file_type_, lhs.mtime_nsec_, lhs.path_) < std::tie(rhs.file_type_, rhs.mtime_nsec_, rhs.path_); } inline bool operator==(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) { return std::tie(lhs.file_type_, lhs.mtime_nsec_, lhs.path_) == std::tie(rhs.file_type_, rhs.mtime_nsec_, rhs.path_); } inline bool operator!=(const FullLocalFileLocation &lhs, const FullLocalFileLocation &rhs) { return !(lhs == rhs); } inline StringBuilder &operator<<(StringBuilder &sb, const FullLocalFileLocation &location) { return sb << '[' << location.file_type_ << "] at \"" << location.path_ << '"'; } class LocalFileLocation { public: enum class Type : int32 { Empty, Partial, Full }; Type type() const { return static_cast(variant_.get_offset()); } PartialLocalFileLocation &partial() { return variant_.get<1>(); } FullLocalFileLocation &full() { return variant_.get<2>(); } const PartialLocalFileLocation &partial() const { return variant_.get<1>(); } const FullLocalFileLocation &full() const { return variant_.get<2>(); } CSlice file_name() const { switch (type()) { case Type::Partial: return partial().path_; case Type::Full: return full().path_; case Type::Empty: default: return CSlice(); } } template void store(StorerT &storer) const { using td::store; store(variant_.get_offset(), storer); variant_.visit([&](auto &&value) { using td::store; store(value, storer); }); } template void parse(ParserT &parser) { using td::parse; auto type = static_cast(parser.fetch_int()); switch (type) { case Type::Empty: variant_ = EmptyLocalFileLocation(); return; case Type::Partial: variant_ = PartialLocalFileLocation(); return parse(partial(), parser); case Type::Full: variant_ = FullLocalFileLocation(); return parse(full(), parser); } return parser.set_error("Invalid type in LocalFileLocation"); } LocalFileLocation() : variant_{EmptyLocalFileLocation()} { } explicit LocalFileLocation(const PartialLocalFileLocation &partial) : variant_(partial) { } explicit LocalFileLocation(const FullLocalFileLocation &full) : variant_(full) { } LocalFileLocation(FileType file_type, string path, uint64 mtime_nsec) : variant_(FullLocalFileLocation{file_type, std::move(path), mtime_nsec}) { } private: Variant variant_; friend bool operator==(const LocalFileLocation &lhs, const LocalFileLocation &rhs); }; inline bool operator==(const LocalFileLocation &lhs, const LocalFileLocation &rhs) { return lhs.variant_ == rhs.variant_; } inline bool operator!=(const LocalFileLocation &lhs, const LocalFileLocation &rhs) { return !(lhs == rhs); } struct FullGenerateFileLocation { FileType file_type_{FileType::None}; string original_path_; string conversion_; static const int32 KEY_MAGIC = 0x8b60a1c8; template void store(StorerT &storer) const { using td::store; store(file_type_, storer); store(original_path_, storer); store(conversion_, storer); } template void parse(ParserT &parser) { using td::parse; parse(file_type_, parser); parse(original_path_, parser); parse(conversion_, parser); } const FullGenerateFileLocation &as_key() const { return *this; } FullGenerateFileLocation() = default; FullGenerateFileLocation(FileType file_type, string original_path, string conversion) : file_type_(file_type), original_path_(std::move(original_path)), conversion_(std::move(conversion)) { } }; inline bool operator<(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) { return std::tie(lhs.file_type_, lhs.original_path_, lhs.conversion_) < std::tie(rhs.file_type_, rhs.original_path_, rhs.conversion_); } inline bool operator==(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) { return std::tie(lhs.file_type_, lhs.original_path_, lhs.conversion_) == std::tie(rhs.file_type_, rhs.original_path_, rhs.conversion_); } inline bool operator!=(const FullGenerateFileLocation &lhs, const FullGenerateFileLocation &rhs) { return !(lhs == rhs); } inline StringBuilder &operator<<(StringBuilder &string_builder, const FullGenerateFileLocation &full_generated_file_location) { return string_builder << '[' << tag("file_type", full_generated_file_location.file_type_) << tag("original_path", full_generated_file_location.original_path_) << tag("conversion", full_generated_file_location.conversion_) << ']'; } class GenerateFileLocation { public: enum class Type : int32 { Empty, Full }; Type type() const { return type_; } FullGenerateFileLocation &full() { CHECK(type_ == Type::Full); return full_; } const FullGenerateFileLocation &full() const { CHECK(type_ == Type::Full); return full_; } template void store(StorerT &storer) const { td::store(type_, storer); switch (type_) { case Type::Empty: return; case Type::Full: return td::store(full_, storer); } } template void parse(ParserT &parser) { td::parse(type_, parser); switch (type_) { case Type::Empty: return; case Type::Full: return td::parse(full_, parser); } return parser.set_error("Invalid type in GenerateFileLocation"); } GenerateFileLocation() : type_(Type::Empty) { } explicit GenerateFileLocation(const FullGenerateFileLocation &full) : type_(Type::Full), full_(full) { } GenerateFileLocation(FileType file_type, string original_path, string conversion) : type_(Type::Full), full_{file_type, std::move(original_path), std::move(conversion)} { } private: Type type_; FullGenerateFileLocation full_; }; inline bool operator==(const GenerateFileLocation &lhs, const GenerateFileLocation &rhs) { if (lhs.type() != rhs.type()) { return false; } switch (lhs.type()) { case GenerateFileLocation::Type::Empty: return true; case GenerateFileLocation::Type::Full: return lhs.full() == rhs.full(); } UNREACHABLE(); return false; } inline bool operator!=(const GenerateFileLocation &lhs, const GenerateFileLocation &rhs) { return !(lhs == rhs); } class FileData { public: DialogId owner_dialog_id_; uint64 pmc_id_ = 0; RemoteFileLocation remote_; LocalFileLocation local_; unique_ptr generate_; int64 size_ = 0; int64 expected_size_ = 0; string remote_name_; string url_; FileEncryptionKey encryption_key_; template void store(StorerT &storer) const { using ::td::store; bool has_owner_dialog_id = owner_dialog_id_.is_valid(); bool has_expected_size = size_ == 0 && expected_size_ != 0; bool encryption_key_is_secure = encryption_key_.is_secure(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_owner_dialog_id); STORE_FLAG(has_expected_size); STORE_FLAG(encryption_key_is_secure); END_STORE_FLAGS(); if (has_owner_dialog_id) { store(owner_dialog_id_, storer); } store(pmc_id_, storer); store(remote_, storer); store(local_, storer); auto generate = generate_ == nullptr ? GenerateFileLocation() : GenerateFileLocation(*generate_); store(generate, storer); if (has_expected_size) { store(expected_size_, storer); } else { store(size_, storer); } store(remote_name_, storer); store(url_, storer); store(encryption_key_, storer); } template void parse(ParserT &parser) { using ::td::parse; bool has_owner_dialog_id; bool has_expected_size; bool encryption_key_is_secure; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_owner_dialog_id); PARSE_FLAG(has_expected_size); PARSE_FLAG(encryption_key_is_secure); END_PARSE_FLAGS_GENERIC(); if (has_owner_dialog_id) { parse(owner_dialog_id_, parser); } parse(pmc_id_, parser); parse(remote_, parser); parse(local_, parser); GenerateFileLocation generate; parse(generate, parser); if (generate.type() == GenerateFileLocation::Type::Full) { generate_ = make_unique(generate.full()); } else { generate_ = nullptr; } if (has_expected_size) { parse(expected_size_, parser); } else { parse(size_, parser); } parse(remote_name_, parser); parse(url_, parser); encryption_key_.parse(encryption_key_is_secure ? FileEncryptionKey::Type::Secure : FileEncryptionKey::Type::Secret, parser); } }; inline StringBuilder &operator<<(StringBuilder &sb, const FileData &file_data) { sb << "[" << tag("remote_name", file_data.remote_name_) << " " << file_data.owner_dialog_id_ << " " << tag("size", file_data.size_) << tag("expected_size", file_data.expected_size_) << " " << file_data.encryption_key_; if (!file_data.url_.empty()) { sb << tag("url", file_data.url_); } if (file_data.local_.type() == LocalFileLocation::Type::Full) { sb << " local " << file_data.local_.full(); } if (file_data.generate_ != nullptr) { sb << " generate " << *file_data.generate_; } if (file_data.remote_.type() == RemoteFileLocation::Type::Full) { sb << " remote " << file_data.remote_.full(); } return sb << "]"; } template string as_key(const T &object) { TlStorerCalcLength calc_length; calc_length.store_int(0); object.as_key().store(calc_length); BufferSlice key_buffer{calc_length.get_length()}; auto key = key_buffer.as_slice(); TlStorerUnsafe storer(key.ubegin()); storer.store_int(T::KEY_MAGIC); object.as_key().store(storer); CHECK(storer.get_buf() == key.uend()); return key.str(); } } // namespace td