/* 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_common.h" #include "auth_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" namespace Serialize { void writeStorageImageLocation( QDataStream &stream, const StorageImageLocation &location) { stream << qint32(location.width()) << qint32(location.height()) << qint32(location.dc()) << quint64(location.volume()) << qint32(location.local()) << quint64(location.secret()); stream << location.fileReference(); } StorageImageLocation readStorageImageLocation( int streamAppVersion, QDataStream &stream) { qint32 width, height, dc, local; quint64 volume, secret; QByteArray fileReference; stream >> width >> height >> dc >> volume >> local >> secret; if (streamAppVersion >= 1003013) { stream >> fileReference; } return StorageImageLocation( width, height, dc, volume, local, secret, fileReference); } int storageImageLocationSize(const StorageImageLocation &location) { // width + height + dc + volume + local + secret + fileReference return sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64) + bytearraySize(location.fileReference()); } uint32 peerSize(not_null peer) { uint32 result = sizeof(quint64) + sizeof(quint64) + storageImageLocationSize(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; } void writePeer(QDataStream &stream, PeerData *peer) { stream << quint64(peer->id) << quint64(peer->userpicPhotoId()); writeStorageImageLocation(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->botInfo ? user->botInfo->inlinePlaceholder : QString(); stream << botInlinePlaceholder; } const auto contactSerialized = [&] { switch (user->contactStatus()) { case UserData::ContactStatus::Contact: return 1; case UserData::ContactStatus::CanAdd: return 0; case UserData::ContactStatus::PhoneUnknown: return -1; } Unexpected("contactStatus in _writePeer()"); }(); stream << qint32(user->onlineTill) << qint32(contactSerialized) << qint32(user->botInfo ? user->botInfo->version : -1); } else if (const auto chat = peer->asChat()) { stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version()) << qint32(chat->creator) << qint32(0) << 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(int streamAppVersion, QDataStream &stream) { quint64 peerId = 0, photoId = 0; stream >> peerId >> photoId; if (!peerId) { return nullptr; } auto photoLoc = readStorageImageLocation( streamAppVersion, stream); PeerData *result = Auth().data().peerLoaded(peerId); bool wasLoaded = (result != nullptr); if (!wasLoaded) { result = Auth().data().peer(peerId); result->loadedStatus = PeerData::FullLoaded; } 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; const auto showPhone = !isServiceUser(user->id) && (user->id != Auth().userPeerId()) && (contact <= 0); const auto pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString(); if (!wasLoaded) { user->setPhone(phone); user->setName(first, last, pname, username); user->setFlags(MTPDuser::Flags::from_raw(flags)); user->setAccessHash(access); user->onlineTill = onlineTill; user->setContactStatus((contact > 0) ? UserData::ContactStatus::Contact : (contact == 0) ? UserData::ContactStatus::CanAdd : UserData::ContactStatus::PhoneUnknown); user->setBotInfoVersion(botInfoVersion); if (!inlinePlaceholder.isEmpty() && user->botInfo) { user->botInfo->inlinePlaceholder = inlinePlaceholder; } if (user->id == Auth().userPeerId()) { user->input = MTP_inputPeerSelf(); user->inputUser = MTP_inputUserSelf(); } else { user->input = MTP_inputPeerUser(MTP_int(peerToUser(user->id)), MTP_long(user->accessHash())); user->inputUser = MTP_inputUser(MTP_int(peerToUser(user->id)), MTP_long(user->accessHash())); } } } else if (const auto chat = result->asChat()) { QString name, inviteLink; qint32 count, date, version, creator, oldForbidden; quint32 flagsData, flags; stream >> name >> count >> date >> version >> creator >> oldForbidden >> flagsData >> inviteLink; 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); } if (!wasLoaded) { 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); chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id))); chat->inputChat = MTP_int(peerToChat(chat->id)); } } 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; if (oldForbidden) { flags |= quint32(MTPDchannel_ClientFlag::f_forbidden); } if (!wasLoaded) { 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); channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access)); channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access)); } } if (!wasLoaded) { result->setUserpic( photoId, photoLoc, photoLoc.isNull() ? ImagePtr() : Images::Create(photoLoc)); } return result; } QString peekUserPhone(int streamAppVersion, QDataStream &stream) { quint64 peerId = 0, photoId = 0; stream >> peerId >> photoId; DEBUG_LOG(("peekUserPhone.id: %1").arg(peerId)); if (!peerId || !peerIsUser(peerId)) { return QString(); } const auto photoLoc = readStorageImageLocation( streamAppVersion, stream); QString first, last, phone; stream >> first >> last >> phone; DEBUG_LOG(("peekUserPhone.data: %1 %2 %3" ).arg(first).arg(last).arg(phone)); return phone; } } // namespace Serialize