tdlight/td/telegram/files/FileLocation.h
levlam faf9be4231 Better file download debug.
GitOrigin-RevId: f2e33254bca1c1012f2d8f333554b039b47be3ce
2018-05-30 20:50:11 +03:00

1303 lines
38 KiB
C++

//
// 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/net/DcId.h"
#include "td/telegram/SecureStorage.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/Variant.h"
#include <tuple>
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<td_api::FileType> as_td_api(FileType file_type) {
switch (file_type) {
case FileType::Thumbnail:
return make_tl_object<td_api::fileTypeThumbnail>();
case FileType::ProfilePhoto:
return make_tl_object<td_api::fileTypeProfilePhoto>();
case FileType::Photo:
return make_tl_object<td_api::fileTypePhoto>();
case FileType::VoiceNote:
return make_tl_object<td_api::fileTypeVoiceNote>();
case FileType::Video:
return make_tl_object<td_api::fileTypeVideo>();
case FileType::Document:
return make_tl_object<td_api::fileTypeDocument>();
case FileType::Encrypted:
return make_tl_object<td_api::fileTypeSecret>();
case FileType::Temp:
return make_tl_object<td_api::fileTypeUnknown>();
case FileType::Sticker:
return make_tl_object<td_api::fileTypeSticker>();
case FileType::Audio:
return make_tl_object<td_api::fileTypeAudio>();
case FileType::Animation:
return make_tl_object<td_api::fileTypeAnimation>();
case FileType::EncryptedThumbnail:
return make_tl_object<td_api::fileTypeSecretThumbnail>();
case FileType::Wallpaper:
return make_tl_object<td_api::fileTypeWallpaper>();
case FileType::VideoNote:
return make_tl_object<td_api::fileTypeVideoNote>();
case FileType::Secure:
return make_tl_object<td_api::fileTypeSecure>();
case FileType::SecureRaw:
UNREACHABLE();
return make_tl_object<td_api::fileTypeSecure>();
case FileType::None:
return make_tl_object<td_api::fileTypeNone>();
default:
UNREACHABLE();
return nullptr;
}
}
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;
}
}
constexpr int32 file_type_size = static_cast<int32>(FileType::Size);
extern const char *file_type_name[file_type_size];
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_).remove_suffix(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<const UInt256 *>(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<UInt256 *>(&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<int32>(buf) ^ as<int32>(buf + 4);
}
bool empty() const {
return key_iv_.empty();
}
size_t size() const {
return key_iv_.size();
}
template <class StorerT>
void store(StorerT &storer) const {
td::store(key_iv_, storer);
}
template <class ParserT>
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 <class StorerT>
void store(StorerT &storer) const {
}
template <class ParserT>
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 <class StorerT>
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 <class ParserT>
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 <class StorerT>
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 <class ParserT>
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 <class StorerT>
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 <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(url_, storer);
store(access_hash_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
parse(url_, parser);
parse(access_hash_, parser);
}
struct AsKey {
const WebRemoteFileLocation &key;
template <class StorerT>
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 <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(id_, storer);
store(access_hash_, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
parse(id_, parser);
parse(access_hash_, parser);
}
struct AsKey {
const CommonRemoteFileLocation &key;
template <class StorerT>
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<WebRemoteFileLocation, PhotoRemoteFileLocation, CommonRemoteFileLocation> 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<WebRemoteFileLocation>();
}
PhotoRemoteFileLocation &photo() {
return variant_.get<PhotoRemoteFileLocation>();
}
CommonRemoteFileLocation &common() {
return variant_.get<CommonRemoteFileLocation>();
}
const WebRemoteFileLocation &web() const {
return variant_.get<WebRemoteFileLocation>();
}
const PhotoRemoteFileLocation &photo() const {
return variant_.get<PhotoRemoteFileLocation>();
}
const CommonRemoteFileLocation &common() const {
return variant_.get<CommonRemoteFileLocation>();
}
friend StringBuilder &operator<<(StringBuilder &string_builder,
const FullRemoteFileLocation &full_remote_file_location);
int32 full_type() const {
auto type = static_cast<int32>(file_type_);
if (is_web()) {
type |= WEB_LOCATION_FLAG;
}
return type;
}
public:
template <class StorerT>
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 <class ParserT>
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<int32>(FileType::Size)) {
return parser.set_error("Invalid FileType in FullRemoteFileLocation");
}
file_type_ = static_cast<FileType>(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 <class StorerT>
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 {
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();
}
tl_object_ptr<telegram_api::inputWebFileLocation> as_input_web_file_location() const {
CHECK(is_web());
return make_tl_object<telegram_api::inputWebFileLocation>(web().url_, web().access_hash_);
}
tl_object_ptr<telegram_api::InputFileLocation> as_input_file_location() const {
switch (location_type()) {
case LocationType::Photo:
return make_tl_object<telegram_api::inputFileLocation>(photo().volume_id_, photo().local_id_, photo().secret_);
case LocationType::Common:
if (is_encrypted_secret()) {
return make_tl_object<telegram_api::inputEncryptedFileLocation>(common().id_, common().access_hash_);
} else if (is_secure()) {
return make_tl_object<telegram_api::inputSecureFileLocation>(common().id_, common().access_hash_);
} else {
return make_tl_object<telegram_api::inputDocumentFileLocation>(common().id_, common().access_hash_, 0);
}
case LocationType::Web:
case LocationType::None:
default:
UNREACHABLE();
return nullptr;
}
}
tl_object_ptr<telegram_api::InputDocument> as_input_document() const {
CHECK(is_common());
LOG_IF(ERROR, !is_document()) << "Can't call as_input_document on an encrypted file";
return make_tl_object<telegram_api::inputDocument>(common().id_, common().access_hash_);
}
tl_object_ptr<telegram_api::InputPhoto> as_input_photo() const {
CHECK(is_photo());
return make_tl_object<telegram_api::inputPhoto>(photo().id_, photo().access_hash_);
}
tl_object_ptr<telegram_api::InputEncryptedFile> as_input_encrypted_file() const {
CHECK(is_encrypted_secret()) << "Can't call as_input_encrypted_file on a non-encrypted file";
return make_tl_object<telegram_api::inputEncryptedFile>(common().id_, common().access_hash_);
}
tl_object_ptr<telegram_api::InputSecureFile> as_input_secure_file() const {
CHECK(is_secure()) << "Can't call as_input_secure_file on a non-secure file";
return make_tl_object<telegram_api::inputSecureFile>(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, DcId dc_id)
: file_type_(file_type)
, web_location_flag_{true}
, dc_id_(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 << "[" << file_type_name[static_cast<int32>(full_remote_file_location.file_type_)] << ", "
<< full_remote_file_location.get_dc_id() << ", 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<Type>(variant_.get_offset());
}
template <class StorerT>
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 <class ParserT>
void parse(ParserT &parser) {
auto type = static_cast<Type>(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<EmptyRemoteFileLocation, PartialRemoteFileLocation, FullRemoteFileLocation> 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 <class StorerT>
void store(StorerT &storer) const {
}
template <class ParserT>
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_;
string path_;
int32 part_size_;
int32 ready_part_count_;
string iv_;
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(file_type_, storer);
store(path_, storer);
store(part_size_, storer);
store(ready_part_count_, storer);
store(iv_, storer);
}
template <class ParserT>
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);
parse(ready_part_count_, parser);
parse(iv_, parser);
}
};
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.ready_part_count_ == rhs.ready_part_count_ && lhs.iv_ == rhs.iv_;
}
inline bool operator!=(const PartialLocalFileLocation &lhs, const PartialLocalFileLocation &rhs) {
return !(lhs == rhs);
}
struct FullLocalFileLocation {
FileType file_type_;
string path_;
uint64 mtime_nsec_;
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(file_type_, storer);
store(mtime_nsec_, storer);
store(path_, storer);
}
template <class ParserT>
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 << "[" << file_type_name[static_cast<int32>(location.file_type_)] << "] at \"" << location.path_ << '"';
}
class LocalFileLocation {
public:
enum class Type : int32 { Empty, Partial, Full };
Type type() const {
return static_cast<Type>(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 <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(variant_.get_offset(), storer);
variant_.visit([&](auto &&value) {
using td::store;
store(value, storer);
});
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
auto type = static_cast<Type>(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<EmptyLocalFileLocation, PartialLocalFileLocation, FullLocalFileLocation> 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 <class StorerT>
void store(StorerT &storer) const {
using td::store;
store(file_type_, storer);
store(original_path_, storer);
store(conversion_, storer);
}
template <class ParserT>
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", file_type_name[static_cast<int32>(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 <class StorerT>
void store(StorerT &storer) const {
td::store(type_, storer);
switch (type_) {
case Type::Empty:
return;
case Type::Full:
return td::store(full_, storer);
}
}
template <class ParserT>
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<FullGenerateFileLocation> generate_;
int64 size_ = 0;
int64 expected_size_ = 0;
string remote_name_;
string url_;
FileEncryptionKey encryption_key_;
template <class StorerT>
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 <class ParserT>
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_ = std::make_unique<FullGenerateFileLocation>(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 <class T>
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.begin());
storer.store_int(T::KEY_MAGIC);
object.as_key().store(storer);
return key.str();
}
} // namespace td