diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 64f6a886be..9fe506ab06 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -963,33 +963,66 @@ bool AppendTopPeers(ContactsList &to, const MTPcontacts_TopPeers &data) { } Session ParseSession(const MTPAuthorization &data) { - Expects(data.type() == mtpc_authorization); - - const auto &fields = data.c_authorization(); - auto result = Session(); - result.platform = ParseString(fields.vplatform); - result.deviceModel = ParseString(fields.vdevice_model); - result.systemVersion = ParseString(fields.vsystem_version); - result.applicationName = ParseString(fields.vapp_name); - result.applicationVersion = ParseString(fields.vapp_version); - result.created = fields.vdate_created.v; - result.lastActive = fields.vdate_active.v; - result.ip = ParseString(fields.vip); - result.country = ParseString(fields.vcountry); - result.region = ParseString(fields.vregion); - return result; + return data.match([&](const MTPDauthorization &data) { + auto result = Session(); + result.platform = ParseString(data.vplatform); + result.deviceModel = ParseString(data.vdevice_model); + result.systemVersion = ParseString(data.vsystem_version); + result.applicationName = ParseString(data.vapp_name); + result.applicationVersion = ParseString(data.vapp_version); + result.created = data.vdate_created.v; + result.lastActive = data.vdate_active.v; + result.ip = ParseString(data.vip); + result.country = ParseString(data.vcountry); + result.region = ParseString(data.vregion); + return result; + }); } SessionsList ParseSessionsList(const MTPaccount_Authorizations &data) { - Expects(data.type() == mtpc_account_authorizations); + return data.match([](const MTPDaccount_authorizations &data) { + auto result = SessionsList(); + const auto &list = data.vauthorizations.v; + result.list.reserve(list.size()); + for (const auto &session : list) { + result.list.push_back(ParseSession(session)); + } + return result; + }); +} - auto result = SessionsList(); - const auto &list = data.c_account_authorizations().vauthorizations.v; - result.list.reserve(list.size()); - for (const auto &session : list) { - result.list.push_back(ParseSession(session)); - } - return result; +WebSession ParseWebSession( + const MTPWebAuthorization &data, + const std::map &users) { + return data.match([&](const MTPDwebAuthorization &data) { + auto result = WebSession(); + const auto i = users.find(data.vbot_id.v); + if (i != users.end() && i->second.isBot) { + result.botUsername = i->second.username; + } + result.domain = ParseString(data.vdomain); + result.platform = ParseString(data.vplatform); + result.browser = ParseString(data.vbrowser); + result.created = data.vdate_created.v; + result.lastActive = data.vdate_active.v; + result.ip = ParseString(data.vip); + result.region = ParseString(data.vregion); + return result; + }); +} + +SessionsList ParseWebSessionsList( + const MTPaccount_WebAuthorizations &data) { + return data.match([&](const MTPDaccount_webAuthorizations &data) { + auto result = SessionsList(); + const auto users = ParseUsersList(data.vusers); + const auto &list = data.vauthorizations.v; + result.webList.reserve(list.size()); + for (const auto &session : list) { + result.webList.push_back(ParseWebSession(session, users)); + } + return result; + }); } DialogInfo::Type DialogTypeFromChat(const Chat &chat) { diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 707350d061..3f03c6e3a0 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -237,11 +237,24 @@ struct Session { Utf8String region; }; +struct WebSession { + Utf8String botUsername; + Utf8String domain; + Utf8String browser; + Utf8String platform; + TimeId created = 0; + TimeId lastActive = 0; + Utf8String ip; + Utf8String region; +}; + struct SessionsList { std::vector list; + std::vector webList; }; SessionsList ParseSessionsList(const MTPaccount_Authorizations &data); +SessionsList ParseWebSessionsList(const MTPaccount_WebAuthorizations &data); struct UnsupportedMedia { }; diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index efdc0b8b97..439120abe0 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -729,7 +729,13 @@ void ApiWrap::requestSessions(FnMut done) { mainRequest(MTPaccount_GetAuthorizations( )).done([=, done = std::move(done)]( const MTPaccount_Authorizations &result) mutable { - done(Data::ParseSessionsList(result)); + auto list = Data::ParseSessionsList(result); + mainRequest(MTPaccount_GetWebAuthorizations( + )).done([=, done = std::move(done), list = std::move(list)]( + const MTPaccount_WebAuthorizations &result) mutable { + list.webList = Data::ParseWebSessionsList(result).webList; + done(std::move(list)); + }).send(); }).send(); } diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.cpp b/Telegram/SourceFiles/export/output/export_output_abstract.cpp index bf4044bc15..cad49a6cd8 100644 --- a/Telegram/SourceFiles/export/output/export_output_abstract.cpp +++ b/Telegram/SourceFiles/export/output/export_output_abstract.cpp @@ -14,7 +14,11 @@ namespace Export { namespace Output { std::unique_ptr CreateWriter(Format format) { - return std::make_unique(); + switch (format) { + case Format::Text: return std::make_unique(); + case Format::Json: return std::make_unique(); + } + Unexpected("Format in Export::Output::CreateWriter."); } } // namespace Output diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index b9561867c1..0cd0dbd220 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -688,6 +688,17 @@ Result JsonWriter::writeFrequentContacts(const Data::ContactsList &data) { Result JsonWriter::writeSessionsList(const Data::SessionsList &data) { Expects(_output != nullptr); + if (const auto result = writeSessions(data); !result) { + return result; + } else if (const auto result = writeWebSessions(data); !result) { + return result; + } + return Result::Success(); +} + +Result JsonWriter::writeSessions(const Data::SessionsList &data) { + Expects(_output != nullptr); + auto block = prepareObjectItemStart("sessions"); block.append(pushNesting(Context::kArray)); for (const auto &session : data.list) { @@ -714,6 +725,27 @@ Result JsonWriter::writeSessionsList(const Data::SessionsList &data) { return _output->writeBlock(block + popNesting()); } +Result JsonWriter::writeWebSessions(const Data::SessionsList &data) { + Expects(_output != nullptr); + + auto block = prepareObjectItemStart("web_sessions"); + block.append(pushNesting(Context::kArray)); + for (const auto &session : data.webList) { + block.append(prepareArrayItemStart()); + block.append(SerializeObject(_context, { + { "last_active", SerializeDate(session.lastActive) }, + { "last_ip", SerializeString(session.ip) }, + { "last_region", SerializeString(session.region) }, + { "bot_username", StringAllowNull(session.botUsername) }, + { "domain_name", StringAllowNull(session.domain) }, + { "browser", SerializeString(session.browser) }, + { "platform", SerializeString(session.platform) }, + { "created", SerializeDate(session.created) }, + })); + } + return _output->writeBlock(block + popNesting()); +} + Result JsonWriter::writeDialogsStart(const Data::DialogsInfo &data) { return writeChatsStart(data, "chats"); } diff --git a/Telegram/SourceFiles/export/output/export_output_json.h b/Telegram/SourceFiles/export/output/export_output_json.h index fe34afa7f3..579dab48f7 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.h +++ b/Telegram/SourceFiles/export/output/export_output_json.h @@ -72,6 +72,9 @@ private: Result writeSavedContacts(const Data::ContactsList &data); Result writeFrequentContacts(const Data::ContactsList &data); + Result writeSessions(const Data::SessionsList &data); + Result writeWebSessions(const Data::SessionsList &data); + Result writeChatsStart( const Data::DialogsInfo &data, const QByteArray &listName); diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 3c8ee7e9e6..cbb03794b3 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -621,6 +621,17 @@ Result TextWriter::writeFrequentContacts(const Data::ContactsList &data) { Result TextWriter::writeSessionsList(const Data::SessionsList &data) { Expects(_summary != nullptr); + if (const auto result = writeSessions(data); !result) { + return result; + } else if (const auto result = writeWebSessions(data); !result) { + return result; + } + return Result::Success(); +} + +Result TextWriter::writeSessions(const Data::SessionsList &data) { + Expects(_summary != nullptr); + if (data.list.empty()) { return Result::Success(); } @@ -659,6 +670,51 @@ Result TextWriter::writeSessionsList(const Data::SessionsList &data) { return _summary->writeBlock(header); } +Result TextWriter::writeWebSessions(const Data::SessionsList &data) { + Expects(_summary != nullptr); + + if (data.webList.empty()) { + return Result::Success(); + } + + const auto file = fileWithRelativePath("web_sessions.txt"); + auto list = std::vector(); + list.reserve(data.webList.size()); + for (const auto &session : data.webList) { + list.push_back(SerializeKeyValue({ + { "Last active", Data::FormatDateTime(session.lastActive) }, + { "Last IP address", session.ip }, + { "Last region", session.region }, + { + "Bot username", + (session.botUsername.isEmpty() + ? Data::Utf8String("(unknown)") + : session.botUsername) + }, + { + "Domain name", + (session.domain.isEmpty() + ? Data::Utf8String("(unknown)") + : session.domain) + }, + { "Browser", session.browser }, + { "Platform", session.platform }, + { "Created", Data::FormatDateTime(session.created) }, + })); + } + const auto full = JoinList(kLineBreak, list); + if (const auto result = file->writeBlock(full); !result) { + return result; + } + + const auto header = "Web sessions " + "(" + Data::NumberToString(data.webList.size()) + ")" + " - web_sessions.txt" + + kLineBreak + + kLineBreak; + return _summary->writeBlock(header); +} + Result TextWriter::writeDialogsStart(const Data::DialogsInfo &data) { return writeChatsStart(data, "Chats", "chats.txt"); } diff --git a/Telegram/SourceFiles/export/output/export_output_text.h b/Telegram/SourceFiles/export/output/export_output_text.h index e2f466d3b6..1f87bbaae8 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.h +++ b/Telegram/SourceFiles/export/output/export_output_text.h @@ -53,6 +53,9 @@ private: Result writeSavedContacts(const Data::ContactsList &data); Result writeFrequentContacts(const Data::ContactsList &data); + Result writeSessions(const Data::SessionsList &data); + Result writeWebSessions(const Data::SessionsList &data); + Result writeChatsStart( const Data::DialogsInfo &data, const QByteArray &listName,