diff options
author | dartraiden <wowemuh@gmail.com> | 2022-10-19 23:10:42 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2022-10-19 23:10:42 +0300 |
commit | f9842d5d09edeb40f296e9f09be9cc22ac810d41 (patch) | |
tree | a9dd970b81922851986e1611ad9e013894d711d0 /protocols/WhatsApp/src/proto.h | |
parent | f56f50c2dcbd452868520a58c9ae811108545c0e (diff) |
Rename WhatsAppWeb to WhatsApp
Diffstat (limited to 'protocols/WhatsApp/src/proto.h')
-rw-r--r-- | protocols/WhatsApp/src/proto.h | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h new file mode 100644 index 0000000000..d5fb32e80d --- /dev/null +++ b/protocols/WhatsApp/src/proto.h @@ -0,0 +1,425 @@ +/* + +WhatsApp plugin for Miranda NG +Copyright © 2019-22 George Hazan + +*/ + +#if !defined(PROTO_H) +#define PROTO_H + +#define S_WHATSAPP_NET "@s.whatsapp.net" +#define APP_VERSION "2.2230.15" +#define KEY_BUNDLE_TYPE "\x05" + +class WhatsAppProto; +typedef void (WhatsAppProto:: *WA_PKT_HANDLER)(const WANode &node); + +struct WAMSG +{ + union { + uint32_t dwFlags = 0; + struct { + bool bPrivateChat : 1; + bool bGroupChat : 1; + bool bDirectStatus : 1; + bool bOtherStatus : 1; + bool bPeerBroadcast : 1; + bool bOtherBroadcast : 1; + bool bOffline : 1; + }; + }; +}; + +struct WARequest +{ + WARequest(const CMStringA &_1, WA_PKT_HANDLER _2, void *_3 = nullptr) : + szPacketId(_1), + pHandler(_2), + pUserInfo(_3) + {} + + CMStringA szPacketId; + WA_PKT_HANDLER pHandler; + void *pUserInfo; +}; + +struct WADevice +{ + WADevice(const char *_1, int _2) : + jid(_1), + key_index(_2) + {} + + WAJid jid; + int key_index; +}; + +struct WAPersistentHandler +{ + WAPersistentHandler(const char *_1, const char *_2, const char *_3, const char *_4, WA_PKT_HANDLER _5) : + pszTitle(_1), pszType(_2), pszXmlns(_3), pszChild(_4), pHandler(_5) + {} + + const char *pszTitle, *pszType, *pszXmlns, *pszChild; + WA_PKT_HANDLER pHandler; +}; + +struct WAHistoryMessage +{ + CMStringA jid, text; + DWORD timestamp; +}; + +struct WAUser +{ + WAUser(MCONTACT _1, const char *_2, bool _3 = false) : + hContact(_1), + szId(mir_strdup(_2)), + bIsGroupChat(_3), + arHistory(1) + { + } + + ~WAUser() + { + mir_free(szId); + } + + MCONTACT hContact; + DWORD dwModifyTag = 0; + char *szId; + bool bInited = false, bIsGroupChat; + SESSION_INFO *si = 0; + DWORD m_time1 = 0, m_time2 = 0; + OBJLIST<WAHistoryMessage> arHistory; +}; + +struct WAOwnMessage +{ + WAOwnMessage(int _1, MCONTACT _2, const char *_3) : + pktId(_1), + hContact(_2), + szPrefix(_3) + {} + + int pktId; + MCONTACT hContact; + CMStringA szPrefix; +}; + +struct WACollection +{ + WACollection(const char *_1, int _2) : + szName(mir_strdup(_1)), + version(_2) + {} + + ptrA szName; + int version; + + MBinBuffer hash; + std::map<std::string, std::string> indexValueMap; + + void parseRecord(const proto::SyncdRecord &rec, bool bSet); +}; + +class WANoise +{ + friend class WhatsAppProto; + + WhatsAppProto *ppro; + int readCounter = 0, writeCounter = 0; + bool bInitFinished = false, bSendIntro = false; + MBinBuffer salt, encKey, decKey; + uint8_t hash[32]; + + struct { + MBinBuffer priv, pub; + } noiseKeys, ephemeral; + + void deriveKey(const void *pData, size_t cbLen, MBinBuffer &write, MBinBuffer &read); + void mixIntoKey(const void *n, const void *p); + void updateHash(const void *pData, size_t cbLen); + +public: + WANoise(WhatsAppProto *_ppro); + + void finish(); + void init(); + + MBinBuffer decrypt(const void *pData, size_t cbLen); + MBinBuffer encrypt(const void *pData, size_t cbLen); + + size_t decodeFrame(const void *&pData, size_t &cbLen); + MBinBuffer encodeFrame(const void *pData, size_t cbLen); +}; + +class MSignalSession : public MZeroedObject +{ + friend class MSignalStore; + signal_protocol_address address; + session_cipher *cipher = nullptr; + +public: + CMStringA szName; + MBinBuffer sessionData, userData; + + MSignalSession(const CMStringA &_1, int _2); + ~MSignalSession(); + + bool hasAddress(const char *name, size_t name_len) const; + + __forceinline session_cipher* getCipher(void) const { return cipher; } + __forceinline int getDeviceId() const { return address.device_id; } +}; + +class MSignalStore +{ + void init(); + + signal_context *m_pContext; + signal_protocol_store_context *m_pStore; + +public: + PROTO_INTERFACE *pProto; + const char *prefix; + + OBJLIST<MSignalSession> arSessions; + + struct + { + MBinBuffer priv, pub; + } + signedIdentity; + + struct + { + MBinBuffer priv, pub, signature; + uint32_t keyid; + } preKey; + + MSignalStore(PROTO_INTERFACE *_1, const char *_2); + ~MSignalStore(); + + __forceinline signal_context *CTX() const { return m_pContext; } + + MSignalSession *createSession(const CMStringA &szName, int deviceId); + + signal_buffer* decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted); + signal_buffer* decryptGroupSignalProto(const CMStringA &from, const CMStringA &author, const MBinBuffer &encrypted); + + void generatePrekeys(int count); + + void processSenderKeyMessage(const proto::Message_SenderKeyDistributionMessage &msg); +}; + +class WhatsAppProto : public PROTO<WhatsAppProto> +{ + friend class WANoise; + + class CWhatsAppProtoImpl + { + friend class WhatsAppProto; + WhatsAppProto &m_proto; + + CTimer m_keepAlive; + void OnKeepAlive(CTimer *) { + m_proto.SendKeepAlive(); + } + + CWhatsAppProtoImpl(WhatsAppProto &pro) : + m_proto(pro), + m_keepAlive(Miranda_GetSystemWindow(), UINT_PTR(this)) + { + m_keepAlive.OnEvent = Callback(this, &CWhatsAppProtoImpl::OnKeepAlive); + } + } m_impl; + + + bool m_bTerminated, m_bRespawn; + ptrW m_tszDefaultGroup; + + CMStringA m_szJid; + CMStringW m_tszAvatarFolder; + + EVP_PKEY *m_pKeys; // private & public keys + WANoise *m_noise; + + void UploadMorePrekeys(); + + // App state management + OBJLIST<WACollection> m_arCollections; + + void InitCollections(); + void ResyncServer(const OBJLIST<WACollection> &task); + + __forceinline WACollection *FindCollection(const char *pszName) + { return m_arCollections.find((WACollection *)&pszName); + } + + // Contacts management ///////////////////////////////////////////////////////////////// + + mir_cs m_csUsers; + OBJLIST<WAUser> m_arUsers; + + mir_cs m_csOwnMessages; + OBJLIST<WAOwnMessage> m_arOwnMsgs; + + OBJLIST<WADevice> m_arDevices; + + WAUser* FindUser(const char *szId); + WAUser* AddUser(const char *szId, bool bTemporary, bool isChat = false); + + // Group chats ///////////////////////////////////////////////////////////////////////// + + void InitChat(WAUser *pUser); + + // UI ////////////////////////////////////////////////////////////////////////////////// + + void CloseQrDialog(); + bool ShowQrCode(const CMStringA &ref); + + /// Network //////////////////////////////////////////////////////////////////////////// + + time_t m_lastRecvTime; + HNETLIBCONN m_hServerConn; + + mir_cs m_csPacketQueue; + OBJLIST<WARequest> m_arPacketQueue; + + LIST<WAPersistentHandler> m_arPersistent; + WA_PKT_HANDLER FindPersistentHandler(const WANode &node); + + int m_iPacketId; + uint16_t m_wMsgPrefix[2]; + CMStringA GenerateMessageId(); + void ProcessMessage(WAMSG type, const proto::WebMessageInfo &msg); + + bool WSReadPacket(const WSHeader &hdr, MBinBuffer &buf); + int WSSend(const MessageLite &msg); + int WSSendNode(WANode &node, WA_PKT_HANDLER = nullptr); + + MBinBuffer DownloadEncryptedFile(const char *url, const std::string &mediaKeys, const char *pszType); + CMStringW GetTmpFileName(const char *pszClass, const char *addition); + + void OnLoggedIn(void); + void OnLoggedOut(void); + void ServerThreadWorker(void); + void ShutdownSession(void); + + void SendReceipt(const char *pszTo, const char *pszParticipant, const char *pszId, const char *pszType); + void SendKeepAlive(); + void SetServerStatus(int iStatus); + + /// Popups ///////////////////////////////////////////////////////////////////////////// + + HANDLE m_hPopupClass; + CMOption<bool> m_bUsePopups; + + void InitPopups(void); + void Popup(MCONTACT hContact, const wchar_t *szMsg, const wchar_t *szTitle); + + /// Request handlers /////////////////////////////////////////////////////////////////// + + void OnGetAvatarInfo(const JSONNode &node, void*); + void OnGetChatInfo(const JSONNode &node, void*); + void OnSendMessage(const JSONNode &node, void*); + + void OnProcessHandshake(const void *pData, int cbLen); + + void InitPersistentHandlers(); + void OnAccountSync(const WANode &node); + void OnIqBlockList(const WANode &node); + void OnIqCountPrekeys(const WANode &node); + void OnIqDoNothing(const WANode &node); + void OnIqPairDevice(const WANode &node); + void OnIqPairSuccess(const WANode &node); + void OnIqResult(const WANode &node); + void OnIqServerSync(const WANode &node); + void OnNotifyEncrypt(const WANode &node); + void OnReceiveInfo(const WANode &node); + void OnReceiveMessage(const WANode &node); + void OnServerSync(const WANode &node); + void OnStreamError(const WANode &node); + void OnSuccess(const WANode &node); + + // Signal + MSignalStore m_signalStore; + + // Binary packets + void ProcessBinaryPacket(const void *pData, size_t cbLen); + + // unzip operations + MBinBuffer unzip(const MBinBuffer &src); + + /// Avatars //////////////////////////////////////////////////////////////////////////// + CMStringW GetAvatarFileName(MCONTACT hContact); + + INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM); + INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM); + INT_PTR __cdecl GetMyAvatar(WPARAM, LPARAM); + INT_PTR __cdecl SetMyAvatar(WPARAM, LPARAM); + +public: + WhatsAppProto(const char *proto_name, const wchar_t *username); + ~WhatsAppProto(); + + __forceinline bool isOnline() const + { return m_hServerConn != 0; + } + + __forceinline void writeStr(const char *pszSetting, const JSONNode &node) + { + CMStringW str(node.as_mstring()); + if (!str.IsEmpty()) + setWString(pszSetting, str); + } + + class CWhatsAppQRDlg *m_pQRDlg; + + // PROTO_INTERFACE ///////////////////////////////////////////////////////////////////// + + MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr) override; + INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override; + HANDLE SearchBasic(const wchar_t* id) override; + int SendMsg(MCONTACT hContact, int flags, const char* msg) override; + int SetStatus(int iNewStatus) override; + int UserIsTyping(MCONTACT hContact, int type) override; + + void OnModulesLoaded() override; + + // Services //////////////////////////////////////////////////////////////////////////// + + INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); + + // Events ////////////////////////////////////////////////////////////////////////////// + + int __cdecl OnOptionsInit(WPARAM, LPARAM); + int __cdecl OnBuildStatusMenu(WPARAM, LPARAM); + + // Options ///////////////////////////////////////////////////////////////////////////// + + CMOption<wchar_t*> m_wszNick; // your nick name in presence + CMOption<wchar_t*> m_wszDefaultGroup; // clist group to store contacts + CMOption<bool> m_bHideGroupchats; // do not open chat windows on creation + + // Processing Threads ////////////////////////////////////////////////////////////////// + + void __cdecl SearchAckThread(void*); + void __cdecl ServerThread(void*); +}; + +struct CMPlugin : public ACCPROTOPLUGIN<WhatsAppProto> +{ + HNETLIBUSER hAvatarUser = nullptr; + HNETLIBCONN hAvatarConn = nullptr; + bool SaveFile(const char *pszUrl, PROTO_AVATAR_INFORMATION &ai); + + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +#endif |