/* 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 */ #pragma once #include #include #include #include #include #include "core/basic_types.h" #include "base/flags.h" #include "base/bytes.h" #include "base/algorithm.h" #include "base/assertion.h" namespace MTP { // type DcId represents actual data center id, while in most cases // we use some shifted ids, like DcId() + X * DCShift using DcId = int32; using ShiftedDcId = int32; constexpr auto kDcShift = ShiftedDcId(10000); constexpr auto kConfigDcShift = 0x01; constexpr auto kLogoutDcShift = 0x02; constexpr auto kUpdaterDcShift = 0x03; constexpr auto kExportDcShift = 0x04; constexpr auto kMaxMediaDcCount = 0x10; constexpr auto kBaseDownloadDcShift = 0x10; constexpr auto kBaseUploadDcShift = 0x20; constexpr auto kDestroyKeyStartDcShift = 0x100; constexpr DcId BareDcId(ShiftedDcId shiftedDcId) { return (shiftedDcId % kDcShift); } constexpr ShiftedDcId ShiftDcId(DcId dcId, int value) { return dcId + kDcShift * value; } constexpr int GetDcIdShift(ShiftedDcId shiftedDcId) { return shiftedDcId / kDcShift; } } // namespace MTP using mtpPrime = int32; using mtpRequestId = int32; using mtpMsgId = uint64; using mtpPingId = uint64; using mtpBuffer = QVector; using mtpTypeId = uint32; class mtpRequestData; class mtpRequest : public std::shared_ptr { public: mtpRequest() = default; explicit mtpRequest(mtpRequestData *ptr) : std::shared_ptr(ptr) { } uint32 innerLength() const; void write(mtpBuffer &to) const; using ResponseType = void; // don't know real response type =( }; class mtpRequestData : public mtpBuffer { public: // in toSend: = 0 - must send in container, > 0 - can send without container // in haveSent: = 0 - container with msgIds, > 0 - when was sent int64 msDate = 0; mtpRequestId requestId = 0; mtpRequest after; bool needsLayer = false; mtpRequestData(bool/* sure*/) { } static mtpRequest prepare(uint32 requestSize, uint32 maxSize = 0); static void padding(mtpRequest &request); template static mtpRequest serialize(const TRequest &request) { const auto requestSize = request.innerLength() >> 2; auto serialized = prepare(requestSize); request.write(*serialized); return serialized; } static uint32 messageSize(const mtpRequest &request) { if (request->size() < 9) return 0; return 4 + (request.innerLength() >> 2); // 2: msg_id, 1: seq_no, q: message_length } static bool isSentContainer(const mtpRequest &request); // "request-like" wrap for msgIds vector static bool isStateRequest(const mtpRequest &request); static bool needAck(const mtpRequest &request) { if (request->size() < 9) return false; return mtpRequestData::needAckByType((*request)[8]); } static bool needAckByType(mtpTypeId type); private: static uint32 _padding(uint32 requestSize); }; using mtpPreRequestMap = QMap; using mtpRequestMap = QMap; using mtpMsgIdsSet = QMap; class mtpRequestIdsMap : public QMap { public: using ParentType = QMap; mtpMsgId min() const { return size() ? cbegin().key() : 0; } mtpMsgId max() const { ParentType::const_iterator e(cend()); return size() ? (--e).key() : 0; } }; class Exception : public std::exception { public: explicit Exception(const QString &msg) noexcept; const char *what() const noexcept override; private: QByteArray _msg; }; class mtpErrorUnexpected : public Exception { public: mtpErrorUnexpected(mtpTypeId typeId, const QString &type) noexcept; }; class mtpErrorInsufficient : public Exception { public: mtpErrorInsufficient() noexcept; }; class mtpErrorBadTypeId : public Exception { public: mtpErrorBadTypeId(mtpTypeId typeId, const QString &type) noexcept; }; namespace MTP { namespace internal { class TypeData { public: TypeData() = default; TypeData(const TypeData &other) = delete; TypeData(TypeData &&other) = delete; TypeData &operator=(const TypeData &other) = delete; TypeData &operator=(TypeData &&other) = delete; virtual ~TypeData() { } private: void incrementCounter() const { _counter.ref(); } bool decrementCounter() const { return _counter.deref(); } friend class TypeDataOwner; mutable QAtomicInt _counter = { 1 }; }; class TypeDataOwner { public: TypeDataOwner(TypeDataOwner &&other) : _data(base::take(other._data)) { } TypeDataOwner(const TypeDataOwner &other) : _data(other._data) { incrementCounter(); } TypeDataOwner &operator=(TypeDataOwner &&other) { if (other._data != _data) { decrementCounter(); _data = base::take(other._data); } return *this; } TypeDataOwner &operator=(const TypeDataOwner &other) { if (other._data != _data) { setData(other._data); incrementCounter(); } return *this; } ~TypeDataOwner() { decrementCounter(); } protected: TypeDataOwner() = default; TypeDataOwner(const TypeData *data) : _data(data) { } void setData(const TypeData *data) { decrementCounter(); _data = data; } // Unsafe cast, type should be checked by the caller. template const DataType &queryData() const { Expects(_data != nullptr); return static_cast(*_data); } template static decltype(auto) VisitData( const Data &data, Callback &&callback, Callbacks &&...callbacks) { if constexpr (rpl::details::is_callable_plain_v) { return std::forward(callback)(data); } else { return VisitData(data, std::forward(callbacks)...); } } private: void incrementCounter() { if (_data) { _data->incrementCounter(); } } void decrementCounter() { if (_data && !_data->decrementCounter()) { delete base::take(_data); } } const TypeData * _data = nullptr; }; } // namespace internal } // namespace MTP enum { // core types mtpc_int = 0xa8509bda, mtpc_long = 0x22076cba, mtpc_int128 = 0x4bb5362b, mtpc_int256 = 0x929c32f, mtpc_double = 0x2210c154, mtpc_string = 0xb5286e24, mtpc_vector = 0x1cb5c415, // layers mtpc_invokeWithLayer1 = 0x53835315, mtpc_invokeWithLayer2 = 0x289dd1f6, mtpc_invokeWithLayer3 = 0xb7475268, mtpc_invokeWithLayer4 = 0xdea0d430, mtpc_invokeWithLayer5 = 0x417a57ae, mtpc_invokeWithLayer6 = 0x3a64d54d, mtpc_invokeWithLayer7 = 0xa5be56d3, mtpc_invokeWithLayer8 = 0xe9abd9fd, mtpc_invokeWithLayer9 = 0x76715a63, mtpc_invokeWithLayer10 = 0x39620c41, mtpc_invokeWithLayer11 = 0xa6b88fdf, mtpc_invokeWithLayer12 = 0xdda60d3c, mtpc_invokeWithLayer13 = 0x427c8ea2, mtpc_invokeWithLayer14 = 0x2b9b08fa, mtpc_invokeWithLayer15 = 0xb4418b64, mtpc_invokeWithLayer16 = 0xcf5f0987, mtpc_invokeWithLayer17 = 0x50858a19, mtpc_invokeWithLayer18 = 0x1c900537, // manually parsed mtpc_rpc_result = 0xf35c6d01, mtpc_msg_container = 0x73f1f8dc, // mtpc_msg_copy = 0xe06046b2, mtpc_gzip_packed = 0x3072cfa1 }; static const mtpTypeId mtpc_bytes = mtpc_string; static const mtpTypeId mtpc_flags = mtpc_int; static const mtpTypeId mtpc_core_message = -1; // undefined type, but is used static const mtpTypeId mtpLayers[] = { mtpTypeId(mtpc_invokeWithLayer1), mtpTypeId(mtpc_invokeWithLayer2), mtpTypeId(mtpc_invokeWithLayer3), mtpTypeId(mtpc_invokeWithLayer4), mtpTypeId(mtpc_invokeWithLayer5), mtpTypeId(mtpc_invokeWithLayer6), mtpTypeId(mtpc_invokeWithLayer7), mtpTypeId(mtpc_invokeWithLayer8), mtpTypeId(mtpc_invokeWithLayer9), mtpTypeId(mtpc_invokeWithLayer10), mtpTypeId(mtpc_invokeWithLayer11), mtpTypeId(mtpc_invokeWithLayer12), mtpTypeId(mtpc_invokeWithLayer13), mtpTypeId(mtpc_invokeWithLayer14), mtpTypeId(mtpc_invokeWithLayer15), mtpTypeId(mtpc_invokeWithLayer16), mtpTypeId(mtpc_invokeWithLayer17), mtpTypeId(mtpc_invokeWithLayer18), }; static const uint32 mtpLayerMaxSingle = sizeof(mtpLayers) / sizeof(mtpLayers[0]); template class MTPBoxed : public bareT { public: using bareT::bareT; MTPBoxed() = default; MTPBoxed(const MTPBoxed &v) = default; MTPBoxed &operator=(const MTPBoxed &v) = default; MTPBoxed(const bareT &v) : bareT(v) { } MTPBoxed &operator=(const bareT &v) { *((bareT*)this) = v; return *this; } uint32 innerLength() const { return sizeof(mtpTypeId) + bareT::innerLength(); } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) { if (from + 1 > end) throw mtpErrorInsufficient(); cons = (mtpTypeId)*(from++); bareT::read(from, end, cons); } void write(mtpBuffer &to) const { to.push_back(bareT::type()); bareT::write(to); } using Unboxed = bareT; }; template class MTPBoxed > { typename T::CantMakeBoxedBoxedType v; }; class MTPint { public: int32 v = 0; MTPint() = default; uint32 innerLength() const { return sizeof(int32); } mtpTypeId type() const { return mtpc_int; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int) { if (from + 1 > end) throw mtpErrorInsufficient(); if (cons != mtpc_int) throw mtpErrorUnexpected(cons, "MTPint"); v = (int32)*(from++); } void write(mtpBuffer &to) const { to.push_back((mtpPrime)v); } private: explicit MTPint(int32 val) : v(val) { } friend MTPint MTP_int(int32 v); }; inline MTPint MTP_int(int32 v) { return MTPint(v); } using MTPInt = MTPBoxed; namespace internal { struct ZeroFlagsHelper { }; } // namespace internal template class MTPflags { public: Flags v = 0; static_assert(sizeof(Flags) == sizeof(int32), "MTPflags are allowed only wrapping int32 flag types!"); MTPflags() = default; MTPflags(internal::ZeroFlagsHelper helper) { } uint32 innerLength() const { return sizeof(Flags); } mtpTypeId type() const { return mtpc_flags; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_flags) { if (from + 1 > end) throw mtpErrorInsufficient(); if (cons != mtpc_flags) throw mtpErrorUnexpected(cons, "MTPflags"); v = Flags::from_raw(static_cast(*(from++))); } void write(mtpBuffer &to) const { to.push_back(static_cast(v.value())); } private: explicit MTPflags(Flags val) : v(val) { } template friend MTPflags> MTP_flags(base::flags v); template friend MTPflags> MTP_flags(T v); }; template inline MTPflags> MTP_flags(base::flags v) { return MTPflags>(v); } template ::value>> inline MTPflags> MTP_flags(T v) { return MTPflags>(v); } inline internal::ZeroFlagsHelper MTP_flags(void(internal::ZeroFlagsHelper::*)()) { return internal::ZeroFlagsHelper(); } template using MTPFlags = MTPBoxed>; inline bool operator==(const MTPint &a, const MTPint &b) { return a.v == b.v; } inline bool operator!=(const MTPint &a, const MTPint &b) { return a.v != b.v; } class MTPlong { public: uint64 v = 0; MTPlong() = default; uint32 innerLength() const { return sizeof(uint64); } mtpTypeId type() const { return mtpc_long; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_long) { if (from + 2 > end) throw mtpErrorInsufficient(); if (cons != mtpc_long) throw mtpErrorUnexpected(cons, "MTPlong"); v = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); from += 2; } void write(mtpBuffer &to) const { to.push_back((mtpPrime)(v & 0xFFFFFFFFL)); to.push_back((mtpPrime)(v >> 32)); } private: explicit MTPlong(uint64 val) : v(val) { } friend MTPlong MTP_long(uint64 v); }; inline MTPlong MTP_long(uint64 v) { return MTPlong(v); } using MTPLong = MTPBoxed; inline bool operator==(const MTPlong &a, const MTPlong &b) { return a.v == b.v; } inline bool operator!=(const MTPlong &a, const MTPlong &b) { return a.v != b.v; } class MTPint128 { public: uint64 l = 0; uint64 h = 0; MTPint128() = default; uint32 innerLength() const { return sizeof(uint64) + sizeof(uint64); } mtpTypeId type() const { return mtpc_int128; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int128) { if (from + 4 > end) throw mtpErrorInsufficient(); if (cons != mtpc_int128) throw mtpErrorUnexpected(cons, "MTPint128"); l = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); h = (uint64)(((uint32*)from)[2]) | ((uint64)(((uint32*)from)[3]) << 32); from += 4; } void write(mtpBuffer &to) const { to.push_back((mtpPrime)(l & 0xFFFFFFFFL)); to.push_back((mtpPrime)(l >> 32)); to.push_back((mtpPrime)(h & 0xFFFFFFFFL)); to.push_back((mtpPrime)(h >> 32)); } private: explicit MTPint128(uint64 low, uint64 high) : l(low), h(high) { } friend MTPint128 MTP_int128(uint64 l, uint64 h); }; inline MTPint128 MTP_int128(uint64 l, uint64 h) { return MTPint128(l, h); } using MTPInt128 = MTPBoxed; inline bool operator==(const MTPint128 &a, const MTPint128 &b) { return a.l == b.l && a.h == b.h; } inline bool operator!=(const MTPint128 &a, const MTPint128 &b) { return a.l != b.l || a.h != b.h; } class MTPint256 { public: MTPint128 l; MTPint128 h; MTPint256() = default; uint32 innerLength() const { return l.innerLength() + h.innerLength(); } mtpTypeId type() const { return mtpc_int256; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_int256) { if (cons != mtpc_int256) throw mtpErrorUnexpected(cons, "MTPint256"); l.read(from, end); h.read(from, end); } void write(mtpBuffer &to) const { l.write(to); h.write(to); } private: explicit MTPint256(MTPint128 low, MTPint128 high) : l(low), h(high) { } friend MTPint256 MTP_int256(const MTPint128 &l, const MTPint128 &h); }; inline MTPint256 MTP_int256(const MTPint128 &l, const MTPint128 &h) { return MTPint256(l, h); } using MTPInt256 = MTPBoxed; inline bool operator==(const MTPint256 &a, const MTPint256 &b) { return a.l == b.l && a.h == b.h; } inline bool operator!=(const MTPint256 &a, const MTPint256 &b) { return a.l != b.l || a.h != b.h; } class MTPdouble { public: float64 v = 0.; MTPdouble() = default; uint32 innerLength() const { return sizeof(float64); } mtpTypeId type() const { return mtpc_double; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_double) { if (from + 2 > end) throw mtpErrorInsufficient(); if (cons != mtpc_double) throw mtpErrorUnexpected(cons, "MTPdouble"); *(uint64*)(&v) = (uint64)(((uint32*)from)[0]) | ((uint64)(((uint32*)from)[1]) << 32); from += 2; } void write(mtpBuffer &to) const { uint64 iv = *(uint64*)(&v); to.push_back((mtpPrime)(iv & 0xFFFFFFFFL)); to.push_back((mtpPrime)(iv >> 32)); } private: explicit MTPdouble(float64 val) : v(val) { } friend MTPdouble MTP_double(float64 v); }; inline MTPdouble MTP_double(float64 v) { return MTPdouble(v); } using MTPDouble = MTPBoxed; inline bool operator==(const MTPdouble &a, const MTPdouble &b) { return a.v == b.v; } inline bool operator!=(const MTPdouble &a, const MTPdouble &b) { return a.v != b.v; } class MTPstring; using MTPbytes = MTPstring; class MTPstring { public: MTPstring() = default; uint32 innerLength() const; mtpTypeId type() const { return mtpc_string; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_string); void write(mtpBuffer &to) const; QByteArray v; private: explicit MTPstring(QByteArray &&data) : v(std::move(data)) { } friend MTPstring MTP_string(const std::string &v); friend MTPstring MTP_string(const QString &v); friend MTPstring MTP_string(const char *v); friend MTPbytes MTP_bytes(const QByteArray &v); friend MTPbytes MTP_bytes(QByteArray &&v); }; using MTPString = MTPBoxed; using MTPBytes = MTPBoxed; inline MTPstring MTP_string(const std::string &v) { return MTPstring(QByteArray(v.data(), v.size())); } inline MTPstring MTP_string(const QString &v) { return MTPstring(v.toUtf8()); } inline MTPstring MTP_string(const char *v) { return MTPstring(QByteArray(v, strlen(v))); } MTPstring MTP_string(const QByteArray &v) = delete; inline MTPbytes MTP_bytes(const QByteArray &v) { return MTPbytes(QByteArray(v)); } inline MTPbytes MTP_bytes(QByteArray &&v) { return MTPbytes(std::move(v)); } inline MTPbytes MTP_bytes(bytes::const_span buffer) { return MTP_bytes(QByteArray( reinterpret_cast(buffer.data()), buffer.size())); } inline MTPbytes MTP_bytes(const bytes::vector &buffer) { return MTP_bytes(bytes::make_span(buffer)); } inline bool operator==(const MTPstring &a, const MTPstring &b) { return a.v == b.v; } inline bool operator!=(const MTPstring &a, const MTPstring &b) { return a.v != b.v; } inline QString qs(const MTPstring &v) { return QString::fromUtf8(v.v); } inline QByteArray qba(const MTPstring &v) { return v.v; } template class MTPvector { public: MTPvector() = default; uint32 innerLength() const { auto result = uint32(sizeof(uint32)); for (const auto &item : v) { result += item.innerLength(); } return result; } mtpTypeId type() const { return mtpc_vector; } void read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = mtpc_vector) { if (from + 1 > end) throw mtpErrorInsufficient(); if (cons != mtpc_vector) throw mtpErrorUnexpected(cons, "MTPvector"); auto count = static_cast(*(from++)); auto vector = QVector(count, T()); for (auto &item : vector) { item.read(from, end); } v = std::move(vector); } void write(mtpBuffer &to) const { to.push_back(v.size()); for (const auto &item : v) { item.write(to); } } QVector v; private: explicit MTPvector(QVector &&data) : v(std::move(data)) { } template friend MTPvector MTP_vector(uint32 count); template friend MTPvector MTP_vector(uint32 count, const U &value); template friend MTPvector MTP_vector(const QVector &v); template friend MTPvector MTP_vector(QVector &&v); }; template inline MTPvector MTP_vector(uint32 count) { return MTPvector(QVector(count)); } template inline MTPvector MTP_vector(uint32 count, const T &value) { return MTPvector(QVector(count, value)); } template inline MTPvector MTP_vector(const QVector &v) { return MTPvector(QVector(v)); } template inline MTPvector MTP_vector(QVector &&v) { return MTPvector(std::move(v)); } template using MTPVector = MTPBoxed>; template inline bool operator==(const MTPvector &a, const MTPvector &b) { return a.c_vector().v == b.c_vector().v; } template inline bool operator!=(const MTPvector &a, const MTPvector &b) { return a.c_vector().v != b.c_vector().v; } // Human-readable text serialization struct MTPStringLogger { static constexpr auto kBufferSize = 1024 * 1024; // 1 mb start size MTPStringLogger() : p(new char[kBufferSize]) , alloced(kBufferSize) { } ~MTPStringLogger() { delete[] p; } MTPStringLogger &add(const QString &data) { auto d = data.toUtf8(); return add(d.constData(), d.size()); } MTPStringLogger &add(const char *data, int32 len = -1) { if (len < 0) len = strlen(data); if (!len) return (*this); ensureLength(len); memcpy(p + size, data, len); size += len; return (*this); } MTPStringLogger &addSpaces(int32 level) { int32 len = level * 2; if (!len) return (*this); ensureLength(len); for (char *ptr = p + size, *end = ptr + len; ptr != end; ++ptr) { *ptr = ' '; } size += len; return (*this); } void ensureLength(int32 add) { if (size + add <= alloced) return; int32 newsize = size + add; if (newsize % kBufferSize) { newsize += kBufferSize - (newsize % kBufferSize); } char *b = new char[newsize]; memcpy(b, p, size); alloced = newsize; delete[] p; p = b; } char *p = nullptr; int size = 0; int alloced = 0; }; void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons = 0, uint32 level = 0, mtpPrime vcons = 0); void mtpTextSerializeCore(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons, uint32 level, mtpPrime vcons = 0); inline QString mtpTextSerialize(const mtpPrime *&from, const mtpPrime *end) { MTPStringLogger to; try { mtpTextSerializeType(to, from, end, mtpc_core_message); } catch (Exception &e) { to.add("[ERROR] (").add(e.what()).add(")"); } return QString::fromUtf8(to.p, to.size); }