2020-06-17 09:36:25 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "storage/serialize_peer.h"
|
|
|
|
|
|
|
|
#include "storage/serialize_common.h"
|
|
|
|
#include "main/main_session.h"
|
|
|
|
#include "data/data_channel.h"
|
|
|
|
#include "data/data_chat.h"
|
|
|
|
#include "data/data_user.h"
|
|
|
|
#include "data/data_session.h"
|
|
|
|
#include "ui/image/image.h"
|
|
|
|
#include "app.h"
|
|
|
|
|
|
|
|
namespace Serialize {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kModernImageLocationTag = std::numeric_limits<qint32>::min();
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
std::optional<StorageImageLocation> readLegacyStorageImageLocationOrTag(
|
|
|
|
int streamAppVersion,
|
|
|
|
QDataStream &stream) {
|
|
|
|
qint32 width, height, dc, local;
|
|
|
|
quint64 volume, secret;
|
|
|
|
QByteArray fileReference;
|
|
|
|
stream >> width;
|
|
|
|
if (width == kModernImageLocationTag) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
stream >> height >> dc >> volume >> local >> secret;
|
|
|
|
if (streamAppVersion >= 1003013) {
|
|
|
|
stream >> fileReference;
|
|
|
|
}
|
|
|
|
if (stream.status() != QDataStream::Ok) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return StorageImageLocation(
|
|
|
|
StorageFileLocation(
|
|
|
|
dc,
|
2021-04-01 21:04:10 +00:00
|
|
|
UserId(0), // self
|
2020-06-17 09:36:25 +00:00
|
|
|
MTP_inputFileLocation(
|
|
|
|
MTP_long(volume),
|
|
|
|
MTP_int(local),
|
|
|
|
MTP_long(secret),
|
|
|
|
MTP_bytes(fileReference))),
|
|
|
|
width,
|
|
|
|
height);
|
|
|
|
}
|
|
|
|
|
|
|
|
int storageImageLocationSize(const StorageImageLocation &location) {
|
|
|
|
// Modern image location tag + (size + content) of the serialization.
|
|
|
|
return sizeof(qint32) * 2 + location.serializeSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void writeStorageImageLocation(
|
|
|
|
QDataStream &stream,
|
|
|
|
const StorageImageLocation &location) {
|
|
|
|
stream << kModernImageLocationTag << location.serialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<StorageImageLocation> readStorageImageLocation(
|
|
|
|
int streamAppVersion,
|
|
|
|
QDataStream &stream) {
|
|
|
|
const auto legacy = readLegacyStorageImageLocationOrTag(
|
|
|
|
streamAppVersion,
|
|
|
|
stream);
|
|
|
|
if (legacy) {
|
|
|
|
return legacy;
|
|
|
|
}
|
|
|
|
auto serialized = QByteArray();
|
|
|
|
stream >> serialized;
|
|
|
|
return (stream.status() == QDataStream::Ok)
|
|
|
|
? StorageImageLocation::FromSerialized(serialized)
|
|
|
|
: std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
int imageLocationSize(const ImageLocation &location) {
|
|
|
|
// Modern image location tag + (size + content) of the serialization.
|
|
|
|
return sizeof(qint32) * 2 + location.serializeSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void writeImageLocation(QDataStream &stream, const ImageLocation &location) {
|
|
|
|
stream << kModernImageLocationTag << location.serialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<ImageLocation> readImageLocation(
|
|
|
|
int streamAppVersion,
|
|
|
|
QDataStream &stream) {
|
|
|
|
const auto legacy = readLegacyStorageImageLocationOrTag(
|
|
|
|
streamAppVersion,
|
|
|
|
stream);
|
|
|
|
if (legacy) {
|
|
|
|
return ImageLocation(
|
|
|
|
DownloadLocation{ legacy->file() },
|
|
|
|
legacy->width(),
|
|
|
|
legacy->height());
|
|
|
|
}
|
|
|
|
auto serialized = QByteArray();
|
|
|
|
stream >> serialized;
|
|
|
|
return (stream.status() == QDataStream::Ok)
|
|
|
|
? ImageLocation::FromSerialized(serialized)
|
|
|
|
: std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 peerSize(not_null<PeerData*> peer) {
|
|
|
|
uint32 result = sizeof(quint64)
|
|
|
|
+ sizeof(quint64)
|
|
|
|
+ imageLocationSize(peer->userpicLocation());
|
|
|
|
if (peer->isUser()) {
|
|
|
|
UserData *user = peer->asUser();
|
|
|
|
|
|
|
|
// first + last + phone + username + access
|
|
|
|
result += stringSize(user->firstName) + stringSize(user->lastName) + stringSize(user->phone()) + stringSize(user->username) + sizeof(quint64);
|
|
|
|
|
|
|
|
// flags
|
|
|
|
if (AppVersion >= 9012) {
|
|
|
|
result += sizeof(qint32);
|
|
|
|
}
|
|
|
|
|
|
|
|
// onlineTill + contact + botInfoVersion
|
|
|
|
result += sizeof(qint32) + sizeof(qint32) + sizeof(qint32);
|
|
|
|
} else if (peer->isChat()) {
|
|
|
|
ChatData *chat = peer->asChat();
|
|
|
|
|
|
|
|
// name + count + date + version + admin + old forbidden + left + inviteLink
|
|
|
|
result += stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint32) + stringSize(chat->inviteLink());
|
|
|
|
} else if (peer->isChannel()) {
|
|
|
|
ChannelData *channel = peer->asChannel();
|
|
|
|
|
|
|
|
// name + access + date + version + old forbidden + flags + inviteLink
|
|
|
|
result += stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint32) + stringSize(channel->inviteLink());
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-04-01 21:04:10 +00:00
|
|
|
void writePeer(QDataStream &stream, not_null<PeerData*> peer) {
|
|
|
|
stream << SerializePeerId(peer->id) << quint64(peer->userpicPhotoId());
|
2020-06-17 09:36:25 +00:00
|
|
|
writeImageLocation(stream, peer->userpicLocation());
|
|
|
|
if (const auto user = peer->asUser()) {
|
|
|
|
stream
|
|
|
|
<< user->firstName
|
|
|
|
<< user->lastName
|
|
|
|
<< user->phone()
|
|
|
|
<< user->username
|
|
|
|
<< quint64(user->accessHash());
|
|
|
|
if (AppVersion >= 9012) {
|
|
|
|
stream << qint32(user->flags());
|
|
|
|
}
|
|
|
|
if (AppVersion >= 9016) {
|
|
|
|
const auto botInlinePlaceholder = user->isBot()
|
|
|
|
? user->botInfo->inlinePlaceholder
|
|
|
|
: QString();
|
|
|
|
stream << botInlinePlaceholder;
|
|
|
|
}
|
|
|
|
stream
|
|
|
|
<< qint32(user->onlineTill)
|
|
|
|
<< qint32(user->isContact() ? 1 : 0)
|
|
|
|
<< qint32(user->isBot() ? user->botInfo->version : -1);
|
|
|
|
} else if (const auto chat = peer->asChat()) {
|
2021-04-01 21:04:10 +00:00
|
|
|
auto field1 = qint32(uint32(chat->creator.bare & 0xFFFFFFFFULL));
|
|
|
|
auto field2 = qint32(uint32(chat->creator.bare >> 32) << 8);
|
2020-06-17 09:36:25 +00:00
|
|
|
stream
|
|
|
|
<< chat->name
|
|
|
|
<< qint32(chat->count)
|
|
|
|
<< qint32(chat->date)
|
|
|
|
<< qint32(chat->version())
|
2021-04-01 21:04:10 +00:00
|
|
|
<< field1
|
|
|
|
<< field2
|
2020-06-17 09:36:25 +00:00
|
|
|
<< quint32(chat->flags())
|
|
|
|
<< chat->inviteLink();
|
|
|
|
} else if (const auto channel = peer->asChannel()) {
|
|
|
|
stream
|
|
|
|
<< channel->name
|
|
|
|
<< quint64(channel->access)
|
|
|
|
<< qint32(channel->date)
|
|
|
|
<< qint32(channel->version())
|
|
|
|
<< qint32(0)
|
|
|
|
<< quint32(channel->flags())
|
|
|
|
<< channel->inviteLink();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PeerData *readPeer(
|
|
|
|
not_null<Main::Session*> session,
|
|
|
|
int streamAppVersion,
|
|
|
|
QDataStream &stream) {
|
2021-04-01 21:04:10 +00:00
|
|
|
quint64 peerIdSerialized = 0, photoId = 0;
|
|
|
|
stream >> peerIdSerialized >> photoId;
|
|
|
|
const auto peerId = DeserializePeerId(peerIdSerialized);
|
2020-06-17 09:36:25 +00:00
|
|
|
if (!peerId) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto userpic = readImageLocation(streamAppVersion, stream);
|
|
|
|
auto userpicAccessHash = uint64(0);
|
|
|
|
if (!userpic) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto selfId = session->userPeerId();
|
|
|
|
const auto loaded = (peerId == selfId)
|
|
|
|
? session->user().get()
|
|
|
|
: session->data().peerLoaded(peerId);
|
2020-09-01 06:44:18 +00:00
|
|
|
const auto apply = !loaded || !loaded->isFullLoaded();
|
2020-06-17 09:36:25 +00:00
|
|
|
const auto result = loaded ? loaded : session->data().peer(peerId).get();
|
2020-06-30 07:16:47 +00:00
|
|
|
if (apply) {
|
2020-09-01 06:44:18 +00:00
|
|
|
result->setLoadedStatus(PeerData::LoadedStatus::Full);
|
2020-06-17 09:36:25 +00:00
|
|
|
}
|
|
|
|
if (const auto user = result->asUser()) {
|
|
|
|
QString first, last, phone, username, inlinePlaceholder;
|
|
|
|
quint64 access;
|
|
|
|
qint32 flags = 0, onlineTill, contact, botInfoVersion;
|
|
|
|
stream >> first >> last >> phone >> username >> access;
|
|
|
|
if (streamAppVersion >= 9012) {
|
|
|
|
stream >> flags;
|
|
|
|
}
|
|
|
|
if (streamAppVersion >= 9016) {
|
|
|
|
stream >> inlinePlaceholder;
|
|
|
|
}
|
|
|
|
stream >> onlineTill >> contact >> botInfoVersion;
|
|
|
|
|
|
|
|
userpicAccessHash = access;
|
|
|
|
|
|
|
|
const auto showPhone = !user->isServiceUser()
|
|
|
|
&& (user->id != selfId)
|
|
|
|
&& (contact <= 0);
|
|
|
|
const auto pname = (showPhone && !phone.isEmpty())
|
|
|
|
? App::formatPhone(phone)
|
|
|
|
: QString();
|
|
|
|
|
2020-06-30 07:16:47 +00:00
|
|
|
if (apply) {
|
2020-06-17 09:36:25 +00:00
|
|
|
user->setPhone(phone);
|
|
|
|
user->setName(first, last, pname, username);
|
|
|
|
|
|
|
|
user->setFlags(MTPDuser::Flags::from_raw(flags));
|
|
|
|
user->setAccessHash(access);
|
|
|
|
user->onlineTill = onlineTill;
|
|
|
|
user->setIsContact(contact == 1);
|
|
|
|
user->setBotInfoVersion(botInfoVersion);
|
|
|
|
if (!inlinePlaceholder.isEmpty() && user->isBot()) {
|
|
|
|
user->botInfo->inlinePlaceholder = inlinePlaceholder;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user->id == selfId) {
|
|
|
|
user->input = MTP_inputPeerSelf();
|
|
|
|
user->inputUser = MTP_inputUserSelf();
|
|
|
|
} else {
|
2021-04-01 21:04:10 +00:00
|
|
|
// #TODO ids
|
|
|
|
user->input = MTP_inputPeerUser(MTP_int(peerToUser(user->id).bare), MTP_long(user->accessHash()));
|
|
|
|
user->inputUser = MTP_inputUser(MTP_int(peerToUser(user->id).bare), MTP_long(user->accessHash()));
|
2020-06-17 09:36:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (const auto chat = result->asChat()) {
|
|
|
|
QString name, inviteLink;
|
2021-04-01 21:04:10 +00:00
|
|
|
qint32 count, date, version, field1, field2;
|
2020-06-17 09:36:25 +00:00
|
|
|
quint32 flagsData, flags;
|
2021-04-01 21:04:10 +00:00
|
|
|
stream >> name >> count >> date >> version >> field1 >> field2 >> flagsData >> inviteLink;
|
|
|
|
|
|
|
|
const auto creator = UserId(
|
|
|
|
BareId(uint32(field1)) | (BareId(uint32(field2) >> 8) << 32));
|
|
|
|
const auto oldForbidden = ((uint32(field2) & 0xFF) == 1);
|
2020-06-17 09:36:25 +00:00
|
|
|
|
|
|
|
if (streamAppVersion >= 9012) {
|
|
|
|
flags = flagsData;
|
|
|
|
} else {
|
|
|
|
// flagsData was haveLeft
|
|
|
|
flags = (flagsData == 1)
|
|
|
|
? MTPDchat::Flags(MTPDchat::Flag::f_left)
|
|
|
|
: MTPDchat::Flags(0);
|
|
|
|
}
|
|
|
|
if (oldForbidden) {
|
|
|
|
flags |= quint32(MTPDchat_ClientFlag::f_forbidden);
|
|
|
|
}
|
2020-06-30 07:16:47 +00:00
|
|
|
if (apply) {
|
2020-06-17 09:36:25 +00:00
|
|
|
chat->setName(name);
|
|
|
|
chat->count = count;
|
|
|
|
chat->date = date;
|
|
|
|
|
|
|
|
// We don't save participants, admin status and banned rights.
|
|
|
|
// So we don't restore the version field, info is still unknown.
|
|
|
|
chat->setVersion(0);
|
|
|
|
|
|
|
|
chat->creator = creator;
|
|
|
|
chat->setFlags(MTPDchat::Flags::from_raw(flags));
|
|
|
|
chat->setInviteLink(inviteLink);
|
|
|
|
|
2021-04-01 21:04:10 +00:00
|
|
|
// #TODO ids
|
|
|
|
chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id).bare));
|
2020-06-17 09:36:25 +00:00
|
|
|
}
|
|
|
|
} else if (const auto channel = result->asChannel()) {
|
|
|
|
QString name, inviteLink;
|
|
|
|
quint64 access;
|
|
|
|
qint32 date, version, oldForbidden;
|
|
|
|
quint32 flags;
|
|
|
|
stream >> name >> access >> date >> version >> oldForbidden >> flags >> inviteLink;
|
|
|
|
|
|
|
|
userpicAccessHash = access;
|
|
|
|
|
|
|
|
if (oldForbidden) {
|
|
|
|
flags |= quint32(MTPDchannel_ClientFlag::f_forbidden);
|
|
|
|
}
|
2020-06-30 07:16:47 +00:00
|
|
|
if (apply) {
|
2020-06-17 09:36:25 +00:00
|
|
|
channel->setName(name, QString());
|
|
|
|
channel->access = access;
|
|
|
|
channel->date = date;
|
|
|
|
|
|
|
|
// We don't save participants, admin status and banned rights.
|
|
|
|
// So we don't restore the version field, info is still unknown.
|
|
|
|
channel->setVersion(0);
|
|
|
|
|
|
|
|
channel->setFlags(MTPDchannel::Flags::from_raw(flags));
|
|
|
|
channel->setInviteLink(inviteLink);
|
|
|
|
|
2021-04-01 21:04:10 +00:00
|
|
|
// #TODO ids
|
|
|
|
channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id).bare), MTP_long(access));
|
|
|
|
channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id).bare), MTP_long(access));
|
2020-06-17 09:36:25 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-30 07:16:47 +00:00
|
|
|
if (apply) {
|
2021-05-19 12:09:07 +00:00
|
|
|
const auto location = userpic->convertToModernPeerPhoto(
|
|
|
|
result->id.value,
|
|
|
|
userpicAccessHash,
|
|
|
|
photoId);
|
2020-06-17 09:36:25 +00:00
|
|
|
result->setUserpic(photoId, location);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString peekUserPhone(int streamAppVersion, QDataStream &stream) {
|
2021-04-01 21:04:10 +00:00
|
|
|
quint64 peerIdSerialized = 0, photoId = 0;
|
|
|
|
stream >> peerIdSerialized >> photoId;
|
|
|
|
const auto peerId = DeserializePeerId(peerIdSerialized);
|
|
|
|
DEBUG_LOG(("peekUserPhone.id: %1").arg(peerId.value));
|
2020-06-17 09:36:25 +00:00
|
|
|
if (!peerId
|
|
|
|
|| !peerIsUser(peerId)
|
2021-05-19 12:09:07 +00:00
|
|
|
|| !readImageLocation(streamAppVersion, stream)) {
|
2020-06-17 09:36:25 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString first, last, phone;
|
|
|
|
stream >> first >> last >> phone;
|
|
|
|
DEBUG_LOG(("peekUserPhone.data: %1 %2 %3"
|
2021-03-13 11:50:34 +00:00
|
|
|
).arg(first, last, phone));
|
2020-06-17 09:36:25 +00:00
|
|
|
return phone;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Serialize
|