// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 // // 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/files/FileEncryptionKey.h" #include "td/telegram/net/DcId.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/format.h" #include "td/utils/logging.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/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; } } inline bool is_file_big(FileType file_type, int64 expected_size) { switch (file_type) { case FileType::Thumbnail: case FileType::ProfilePhoto: case FileType::Photo: case FileType::EncryptedThumbnail: return false; default: break; } constexpr int64 SMALL_FILE_MAX_SIZE = 10 << 20; return expected_size > SMALL_FILE_MAX_SIZE; } 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); } inline StringBuilder &operator<<(StringBuilder &sb, const PartialRemoteFileLocation &location) { return sb << '[' << (location.is_big_ ? "Big" : "Small") << " partial remote location with " << location.part_count_ << " parts of size " << location.part_size_ << " with " << location.ready_part_count_ << " ready parts]"; } 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); bool is_empty() const { switch (type()) { case Type::Empty: return true; case Type::Partial: return partial().ready_part_count_ == 0; case Type::Full: return false; default: UNREACHABLE(); return false; } } }; inline bool operator==(const RemoteFileLocation &lhs, const RemoteFileLocation &rhs) { if (lhs.is_empty() && rhs.is_empty()) { return true; } 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); } inline StringBuilder &operator<<(StringBuilder &sb, const PartialLocalFileLocation &location) { return sb << "[partial local location of " << location.file_type_ << " with part size " << location.part_size_ << " and ready parts " << Bitmask(Bitmask::Decode{}, location.ready_bitmask_) << "] at \"" << location.path_ << '"'; } 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 << "[full local location of " << location.file_type_ << "] at \"" << location.path_ << '"'; } struct PartialLocalFileLocationPtr { unique_ptr location_; // must never be equal to nullptr PartialLocalFileLocationPtr() : location_(make_unique()) { } explicit PartialLocalFileLocationPtr(PartialLocalFileLocation location) : location_(make_unique(location)) { } PartialLocalFileLocationPtr(const PartialLocalFileLocationPtr &other) : location_(make_unique(*other.location_)) { } PartialLocalFileLocationPtr &operator=(const PartialLocalFileLocationPtr &other) { *location_ = *other.location_; return *this; } PartialLocalFileLocationPtr(PartialLocalFileLocationPtr &&other) : location_(make_unique(std::move(*other.location_))) { } PartialLocalFileLocationPtr &operator=(PartialLocalFileLocationPtr &&other) { *location_ = std::move(*other.location_); return *this; } ~PartialLocalFileLocationPtr() = default; template void store(StorerT &storer) const { td::store(*location_, storer); } }; inline bool operator==(const PartialLocalFileLocationPtr &lhs, const PartialLocalFileLocationPtr &rhs) { return *lhs.location_ == *rhs.location_; } 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>().location_; } FullLocalFileLocation &full() { return variant_.get<2>(); } const PartialLocalFileLocation &partial() const { return *variant_.get<1>().location_; } 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_ = PartialLocalFileLocationPtr(); 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_(PartialLocalFileLocationPtr(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); } inline StringBuilder &operator<<(StringBuilder &sb, const LocalFileLocation &location) { switch (location.type()) { case LocalFileLocation::Type::Empty: return sb << "[empty local location]"; case LocalFileLocation::Type::Partial: return sb << location.partial(); case LocalFileLocation::Type::Full: return sb << location.full(); default: UNREACHABLE(); return sb; } } 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