// // 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/Global.h" #include "td/telegram/Version.h" #include "td/utils/buffer.h" #include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/Slice.h" #include "td/utils/Status.h" #include "td/utils/StorerBase.h" #include "td/utils/StringBuilder.h" #include "td/utils/tl_helpers.h" #include "td/utils/tl_parsers.h" #include "td/utils/tl_storers.h" #include <type_traits> namespace td { namespace logevent { template <class ParentT> class WithVersion : public ParentT { public: using ParentT::ParentT; void set_version(int32 version) { version_ = version; } int32 version() const { return version_; } private: int32 version_{}; }; template <class ParentT, class ContextT> class WithContext : public ParentT { public: using ParentT::ParentT; void set_context(ContextT context) { context_ = context; } ContextT context() const { return context_; } private: ContextT context_{}; }; class LogEvent { public: LogEvent() = default; LogEvent(const LogEvent &) = delete; LogEvent &operator=(const LogEvent &) = delete; virtual ~LogEvent() = default; enum HandlerType : uint32 { SecretChats = 1, Users = 2, Chats = 3, Channels = 4, SecretChatInfos = 5, WebPages = 0x10, SendMessage = 0x100, DeleteMessage = 0x101, DeleteMessagesFromServer = 0x102, ReadHistoryOnServer = 0x103, ForwardMessages = 0x104, ReadMessageContentsOnServer = 0x105, SendBotStartMessage = 0x106, SendScreenshotTakenNotificationMessage = 0x107, SendInlineQueryResultMessage = 0x108, DeleteDialogHistoryFromServer = 0x109, ReadAllDialogMentionsOnServer = 0x10a, DeleteAllChannelMessagesFromUserOnServer = 0x10b, ToggleDialogIsPinnedOnServer = 0x10c, ReorderPinnedDialogsOnServer = 0x10d, SaveDialogDraftMessageOnServer = 0x10e, UpdateDialogNotificationSettingsOnServer = 0x10f, UpdateScopeNotificationSettingsOnServer = 0x110, ResetAllNotificationSettingsOnServer = 0x111, ChangeDialogReportSpamStateOnServer = 0x112, GetDialogFromServer = 0x113, ReadHistoryInSecretChat = 0x114, ToggleDialogIsMarkedAsUnreadOnServer = 0x115, GetChannelDifference = 0x140, ConfigPmcMagic = 0x1f18, BinlogPmcMagic = 0x4327 }; using Id = uint64; Id logevent_id() const { return logevent_id_; } void set_logevent_id(Id logevent_id) { logevent_id_ = logevent_id; } virtual StringBuilder &print(StringBuilder &sb) const { return sb << "[Logevent " << tag("id", logevent_id()) << "]"; } private: Id logevent_id_{}; }; inline StringBuilder &operator<<(StringBuilder &sb, const LogEvent &log_event) { return log_event.print(sb); } namespace detail { template <class EventT> int32 magic(EventT &event) { return static_cast<int32>(event.get_type()); } template <class EventT, class StorerT> void store(const EventT &event, StorerT &storer) { EventT::downcast_call(event.get_type(), [&](auto *ptr) { static_cast<const std::decay_t<decltype(*ptr)> &>(event).store(storer); }); } template <class DestT, class T> Result<unique_ptr<DestT>> from_parser(T &&parser) { auto version = parser.fetch_int(); parser.set_version(version); parser.set_context(G()); auto magic = static_cast<typename DestT::Type>(parser.fetch_int()); unique_ptr<DestT> event; DestT::downcast_call(magic, [&](auto *ptr) { auto tmp = make_unique<std::decay_t<decltype(*ptr)>>(); tmp->parse(parser); event = std::move(tmp); }); parser.fetch_end(); TRY_STATUS(parser.get_status()); if (event) { return std::move(event); } return Status::Error(PSLICE() << "Unknown SecretChatEvent type: " << format::as_hex(magic)); } template <class DestT> Result<unique_ptr<DestT>> from_buffer_slice(BufferSlice slice) { return from_parser<DestT>(WithVersion<WithContext<TlBufferParser, Global *>>{&slice}); } template <class T> class StorerImpl : public Storer { public: explicit StorerImpl(const T &event) : event_(event) { } size_t size() const override { WithContext<TlStorerCalcLength, Global *> storer; storer.set_context(G()); storer.store_int(T::version()); td::store(magic(event_), storer); td::store(event_, storer); return storer.get_length(); } size_t store(uint8 *ptr) const override { WithContext<TlStorerUnsafe, Global *> storer(ptr); storer.set_context(G()); storer.store_int(T::version()); td::store(magic(event_), storer); td::store(event_, storer); return static_cast<size_t>(storer.get_buf() - ptr); } private: const T &event_; }; } // namespace detail template <class ChildT> class LogEventBase : public LogEvent { public: template <class StorerT> void store(StorerT &storer) const { detail::store(static_cast<const ChildT &>(*this), storer); } static Result<unique_ptr<ChildT>> from_buffer_slice(BufferSlice slice) { return detail::from_buffer_slice<ChildT>(std::move(slice)); } }; template <class ChildT, class ParentT> class LogEventHelper : public ParentT { public: typename ParentT::Type get_type() const override { return ChildT::type; } constexpr int32 magic() const { return static_cast<int32>(get_type()); } }; class LogEventParser : public WithVersion<WithContext<TlParser, Global *>> { public: explicit LogEventParser(Slice data) : WithVersion<WithContext<TlParser, Global *>>(data) { set_version(fetch_int()); LOG_CHECK(version() < static_cast<int32>(Version::Next)) << "Wrong version " << version(); set_context(G()); } }; class LogEventStorerCalcLength : public WithContext<TlStorerCalcLength, Global *> { public: LogEventStorerCalcLength() : WithContext<TlStorerCalcLength, Global *>() { store_int(static_cast<int32>(Version::Next) - 1); set_context(G()); } }; class LogEventStorerUnsafe : public WithContext<TlStorerUnsafe, Global *> { public: explicit LogEventStorerUnsafe(unsigned char *buf) : WithContext<TlStorerUnsafe, Global *>(buf) { store_int(static_cast<int32>(Version::Next) - 1); set_context(G()); } }; } // namespace logevent using LogEvent = logevent::LogEvent; using LogEventParser = logevent::LogEventParser; using LogEventStorerCalcLength = logevent::LogEventStorerCalcLength; using LogEventStorerUnsafe = logevent::LogEventStorerUnsafe; template <class T> Status log_event_parse(T &data, Slice slice) TD_WARN_UNUSED_RESULT; template <class T> Status log_event_parse(T &data, Slice slice) { LogEventParser parser(slice); parse(data, parser); parser.fetch_end(); return parser.get_status(); } template <class T> class LogEventStorerImpl : public Storer { public: explicit LogEventStorerImpl(const T &event) : event_(event) { } size_t size() const override { LogEventStorerCalcLength storer; td::store(event_, storer); return storer.get_length(); } size_t store(uint8 *ptr) const override { LogEventStorerUnsafe storer(ptr); td::store(event_, storer); #ifdef TD_DEBUG T check_result; log_event_parse(check_result, Slice(ptr, storer.get_buf())).ensure(); #endif return static_cast<size_t>(storer.get_buf() - ptr); } private: const T &event_; }; template <class T> BufferSlice log_event_store(const T &data) { LogEventStorerCalcLength storer_calc_length; store(data, storer_calc_length); BufferSlice value_buffer{storer_calc_length.get_length()}; LogEventStorerUnsafe storer_unsafe(value_buffer.as_slice().ubegin()); store(data, storer_unsafe); #ifdef TD_DEBUG T check_result; log_event_parse(check_result, value_buffer.as_slice()).ensure(); #endif return value_buffer; } } // namespace td