diff options
Diffstat (limited to 'protocols/WhatsAppWeb/src/utils.cpp')
-rw-r--r-- | protocols/WhatsAppWeb/src/utils.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/protocols/WhatsAppWeb/src/utils.cpp b/protocols/WhatsAppWeb/src/utils.cpp index bc35b6ef5f..b6877f529a 100644 --- a/protocols/WhatsAppWeb/src/utils.cpp +++ b/protocols/WhatsAppWeb/src/utils.cpp @@ -69,3 +69,265 @@ bool WhatsAppProto::decryptBinaryMessage(size_t cbSize, const void *buf, MBinBuf } return true; } + +///////////////////////////////////////////////////////////////////////////////////////// + +bool WAReader::readAttributes(JSONNode &ret, int count) +{ + if (count == 0) + return true; + + for (int i = 0; i < count; i++) { + CMStringA name = readString(readInt8()); + if (name.IsEmpty()) + return false; + + CMStringA value = readString(readInt8()); + if (value.IsEmpty()) + return false; + + ret << CHAR_PARAM(name, value); + } + return true; +} + +uint32_t WAReader::readInt20() +{ + if (m_limit - m_buf < 3) + return 0; + + int ret = (int(m_buf[0] & 0x0F) << 16) + (int(m_buf[1]) << 8) + int(m_buf[2]); + m_buf += 3; + return ret; +} + +uint32_t WAReader::readIntN(int n) +{ + if (m_limit - m_buf < n) + return 0; + + uint32_t res = 0; + for (int i = 0; i < n; i++, m_buf++) + res = (res <<= 8) + *m_buf; + return res; +} + +bool WAReader::readList(JSONNode &parent, int tag) +{ + int size = readListSize(tag); + if (size == -1) + return false; + + JSONNode list(JSON_ARRAY); list.set_name("$list$"); + for (int i = 0; i < size; i++) { + JSONNode tmp; + if (!readNode(tmp)) + return false; + list << tmp; + } + + parent << list; + return true; +} + +int WAReader::readListSize(int tag) +{ + switch (tag) { + case LIST_EMPTY: + return 0; + case LIST_8: + return readInt8(); + case LIST_16: + return readInt16(); + } + return -1; +} + +bool WAReader::readNode(JSONNode &ret) +{ + int listSize = readListSize(readInt8()); + if (listSize == -1) + return false; + + int descrTag = readInt8(); + if (descrTag == STREAM_END) + return false; + + CMStringA name = readString(descrTag); + if (name.IsEmpty()) + return false; + ret.set_name(name.c_str()); + + if (!readAttributes(ret, (listSize-1)>>1)) + return false; + + if ((listSize % 2) == 1) + return true; + + int size, tag = readInt8(); + switch (tag) { + case LIST_EMPTY: case LIST_8: case LIST_16: + readList(ret, tag); + break; + + case BINARY_8: + size = readInt8(); + +LBL_Binary: + if (m_limit - m_buf < size) + return false; + + ret << CHAR_PARAM("$bin$", ptrA(mir_base64_encode(m_buf, size))); + m_buf += size; + break; + + case BINARY_20: + size = readInt20(); + goto LBL_Binary; + + case BINARY_32: + size = readInt32(); + goto LBL_Binary; + + default: + ret << CHAR_PARAM("$str$", readString(tag).c_str()); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int unpackHex(int val) +{ + if (val < 0 || val > 15) + return -1; + + return (val < 10) ? val + '0' : val - 10 + 'A'; +} + +static int unpackNibble(int val) +{ + if (val < 0 || val > 15) + return -1; + + switch (val) { + case 10: return '-'; + case 11: return '.'; + case 15: return 0; + default: return '0' + val; + } +} + +CMStringA WAReader::readPacked(int tag) +{ + int startByte = readInt8(); + bool bTrim = false; + if (startByte & 0x80) { + startByte &= ~0x80; + bTrim = true; + } + + CMStringA ret; + for (int i = 0; i < startByte; i++) { + BYTE b = readInt8(); + int lower = (tag == NIBBLE_8) ? unpackNibble(b >> 4) : unpackHex(b >> 4); + if (lower == -1) + return ""; + + int higher = (tag == NIBBLE_8) ? unpackNibble(b & 0x0F) : unpackHex(b & 0x0F); + if (higher == -1) + return ""; + + ret.AppendChar(lower); + ret.AppendChar(higher); + } + + return ret; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static char *SingleByteTokens[] = { + "", "", "", "200", "400", "404", "500", "501", "502", "action", "add", + "after", "archive", "author", "available", "battery", "before", "body", + "broadcast", "chat", "clear", "code", "composing", "contacts", "count", + "create", "debug", "delete", "demote", "duplicate", "encoding", "error", + "false", "filehash", "from", "g.us", "group", "groups_v2", "height", "id", + "image", "in", "index", "invis", "item", "jid", "kind", "last", "leave", + "live", "log", "media", "message", "mimetype", "missing", "modify", "name", + "notification", "notify", "out", "owner", "participant", "paused", + "picture", "played", "presence", "preview", "promote", "query", "raw", + "read", "receipt", "received", "recipient", "recording", "relay", + "remove", "response", "resume", "retry", "s.whatsapp.net", "seconds", + "set", "size", "status", "subject", "subscribe", "t", "text", "to", "true", + "type", "unarchive", "unavailable", "url", "user", "value", "web", "width", + "mute", "read_only", "admin", "creator", "short", "update", "powersave", + "checksum", "epoch", "block", "previous", "409", "replaced", "reason", + "spam", "modify_tag", "message_info", "delivery", "emoji", "title", + "description", "canonical-url", "matched-text", "star", "unstar", + "media_key", "filename", "identity", "unread", "page", "page_count", + "search", "media_message", "security", "call_log", "profile", "ciphertext", + "invite", "gif", "vcard", "frequent", "privacy", "blacklist", "whitelist", + "verify", "location", "document", "elapsed", "revoke_invite", "expiration", + "unsubscribe", "disable", "vname", "old_jid", "new_jid", "announcement", + "locked", "prop", "label", "color", "call", "offer", "call-id", + "quick_reply", "sticker", "pay_t", "accept", "reject", "sticker_pack", + "invalid", "canceled", "missed", "connected", "result", "audio", + "video", "recent" }; + +CMStringA WAReader::readString(int tag) +{ + if (tag >= 3 && tag < _countof(SingleByteTokens)) { + CMStringA ret = SingleByteTokens[tag]; + if (ret == "s.whatsapp.net") + return "c.us"; + return ret; + } + + switch (tag) { +// case DICTIONARY_0: return dict0[readInt8()]; +// case DICTIONARY_1: return dict1[readInt8()]; +// case DICTIONARY_2: return dict2[readInt8()]; +// case DICTIONARY_3: return dict3[readInt8()]; + case LIST_EMPTY: + return ""; + + case BINARY_8: + return readStringFromChars(readInt8()); + + case BINARY_20: + return readStringFromChars(readInt20()); + + case BINARY_32: + return readStringFromChars(readInt32()); + + case NIBBLE_8: + case HEX_8: + return readPacked(tag); + + case JID_PAIR: + CMStringA s1 = readString(readInt8()); + if (s1.IsEmpty()) + break; + + CMStringA s2 = readString(readInt8()); + if (s2.IsEmpty()) + break; + + return CMStringA(FORMAT, "%s@%s", s1.c_str(), s2.c_str()); + } + + // error + return ""; +} + +CMStringA WAReader::readStringFromChars(int size) +{ + if (m_limit - m_buf < size) + return ""; + + CMStringA ret((char*)m_buf, size); + m_buf += size; + return ret; +} |