From c47ca004ba979d23a86211393c9e35deadd66c46 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sat, 24 Jan 2015 16:35:14 +0000 Subject: adaptation of WhatsApp for protocol version 1.5/2.0 git-svn-id: http://svn.miranda-ng.org/main/trunk@11898 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- .../WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp | 289 ++++++++++++--------- 1 file changed, 162 insertions(+), 127 deletions(-) (limited to 'protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp') diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp index 388bf10e4a..573afb4680 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp @@ -11,7 +11,32 @@ #include "ProtocolTreeNode.h" #include "utilities.h" -BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize) { +static const char *secondary_dict[] = { + "mpeg4", "wmv", "audio/3gpp", "audio/aac", "audio/amr", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/qcelp", "audio/wav", + "audio/webm", "audio/x-caf", "audio/x-ms-wma", "image/gif", "image/jpeg", "image/png", "video/3gpp", "video/avi", "video/mp4", + "video/mpeg", "video/quicktime", "video/x-flv", "video/x-ms-asf", "302", "400", "401", "402", "403", "404", "405", "406", "407", + "409", "410", "500", "501", "503", "504", "abitrate", "acodec", "app_uptime", "asampfmt", "asampfreq", "audio", "clear", "conflict", + "conn_no_nna", "cost", "currency", "duration", "extend", "file", "fps", "g_notify", "g_sound", "gcm", "gone", "google_play", "hash", + "height", "invalid", "jid-malformed", "latitude", "lc", "lg", "live", "location", "log", "longitude", "max_groups", "max_participants", + "max_subject", "mimetype", "mode", "napi_version", "normalize", "orighash", "origin", "passive", "password", "played", + "policy-violation", "pop_mean_time", "pop_plus_minus", "price", "pricing", "redeem", "Replaced by new connection", "resume", + "signature", "size", "sound", "source", "system-shutdown", "username", "vbitrate", "vcard", "vcodec", "video", "width", + "xml-not-well-formed", "checkmarks", "image_max_edge", "image_max_kbytes", "image_quality", "ka", "ka_grow", "ka_shrink", "newmedia", + "library", "caption", "forward", "c0", "c1", "c2", "c3", "clock_skew", "cts", "k0", "k1", "login_rtt", "m_id", "nna_msg_rtt", + "nna_no_off_count", "nna_offline_ratio", "nna_push_rtt", "no_nna_con_count", "off_msg_rtt", "on_msg_rtt", "stat_name", "sts", + "suspect_conn", "lists", "self", "qr", "web", "w:b", "recipient", "w:stats", "forbidden", "aurora.m4r", "bamboo.m4r", "chord.m4r", + "circles.m4r", "complete.m4r", "hello.m4r", "input.m4r", "keys.m4r", "note.m4r", "popcorn.m4r", "pulse.m4r", "synth.m4r", "filehash", + "max_list_recipients", "en-AU", "en-GB", "es-MX", "pt-PT", "zh-Hans", "zh-Hant", "relayelection", "relaylatency", "interruption", + "Apex.m4r", "Beacon.m4r", "Bulletin.m4r", "By The Seaside.m4r", "Chimes.m4r", "Circuit.m4r", "Constellation.m4r", "Cosmic.m4r", + "Crystals.m4r", "Hillside.m4r", "Illuminate.m4r", "Night Owl.m4r", "Opening.m4r", "Playtime.m4r", "Presto.m4r", "Radar.m4r", + "Radiate.m4r", "Ripples.m4r", "Sencha.m4r", "Signal.m4r", "Silk.m4r", "Slow Rise.m4r", "Stargaze.m4r", "Summit.m4r", "Twinkle.m4r", + "Uplift.m4r", "Waves.m4r", "voip", "eligible", "upgrade", "planned", "current", "future", "disable", "expire", "start", "stop", + "accuracy", "speed", "bearing", "recording", "encrypt", "key", "identity", "w:gp2", "admin", "locked", "unlocked", "new", "battery", + "archive", "adm", "plaintext_size", "compressed_size", "delivered", "msg", "pkmsg", "everyone", "v", "transport", "call-id" +}; + +BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize) +{ this->conn = conn; this->rawIn = connection; this->tokenMap = dictionary; @@ -21,14 +46,16 @@ BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* conn this->buf = new std::vector(BUFFER_SIZE); } -BinTreeNodeReader::~BinTreeNodeReader() { +BinTreeNodeReader::~BinTreeNodeReader() +{ if (this->buf != NULL) delete this->buf; if (this->in != NULL) delete this->in; } -ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { +ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() +{ int b = this->in->read(); int size = readListSize(b); b = this->in->read(); @@ -40,7 +67,7 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { if ((size == 0) || (tag == NULL)) throw WAException("nextTree sees 0 list or null tag", WAException::CORRUPT_STREAM_EX, -1); int attribCount = (size - 2 + size % 2) / 2; - std::map* attribs = readAttributes(attribCount); + std::map* attribs = readAttributes(attribCount); if (size % 2 == 1) { ProtocolTreeNode* ret = new ProtocolTreeNode(*tag, attribs); delete tag; @@ -59,7 +86,8 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { std::string* s = (std::string*) obj->data; data = new std::vector(s->begin(), s->end()); delete s; - } else { + } + else { data = (std::vector*) obj->data; } @@ -69,25 +97,31 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { return ret; } -bool BinTreeNodeReader::isListTag(int b) { +bool BinTreeNodeReader::isListTag(int b) +{ return (b == 248) || (b == 0) || (b == 249); } -void BinTreeNodeReader::decodeStream(int flags, int offset, int length) { +void BinTreeNodeReader::decodeStream(int flags, int offset, int length) +{ if ((flags & 8) != 0) { - if (length < 4) { - throw WAException("invalid length" + length, WAException::CORRUPT_STREAM_EX, 0); - } - offset += 4; + if (length < 4) + throw WAException("invalid length" + length, WAException::CORRUPT_STREAM_EX, 0); + length -= 4; - this->conn->inputKey->decodeMessage(&(*this->buf)[0], offset - 4, offset, length); + + unsigned char *pData = (unsigned char*)&(*this->buf)[0]; + this->conn->inputKey->decodeMessage(pData, offset + length, 0, length); + this->rawIn->dump(pData + offset, length); } + if (this->in != NULL) delete this->in; this->in = new ByteArrayInputStream(this->buf, offset, length); } -std::map* BinTreeNodeReader::readAttributes(int attribCount) { +std::map* BinTreeNodeReader::readAttributes(int attribCount) +{ std::map* attribs = new std::map(); for (int i = 0; i < attribCount; i++) { std::string* key = readStringAsString(); @@ -99,91 +133,106 @@ std::map* BinTreeNodeReader::readAttributes(int attribCount) { return attribs; } -std::vector* BinTreeNodeReader::readList(int token) { +std::vector* BinTreeNodeReader::readList(int token) +{ int size = readListSize(token); std::vector* list = new std::vector(size); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) (*list)[i] = nextTreeInternal(); - } return list; } -int BinTreeNodeReader::readListSize(int token) { - int size; - if (token == 0) { - size = 0; +int BinTreeNodeReader::readListSize(int token) +{ + switch (token) { + case 0: return 0; + case 248: return readInt8(this->in); + case 249: return readInt16(this->in); + default: + throw new WAException("invalid list size in readListSize: token " + token, WAException::CORRUPT_STREAM_EX, 0); } - else { - size = 0; - if (token == 248) { - size = readInt8(this->in); - } - else - { - size = 0; - if (token == 249) - size = readInt16(this->in); - else - throw new WAException("invalid list size in readListSize: token " + token, WAException::CORRUPT_STREAM_EX, 0); - } - } - - return size; + return 0; } -std::vector* BinTreeNodeReader::readList() { +std::vector* BinTreeNodeReader::readList() +{ return readList(this->in->read()); } -ReadData* BinTreeNodeReader::readString() { +ReadData* BinTreeNodeReader::readString() +{ return readString(this->in->read()); } -ReadData* BinTreeNodeReader::readString(int token) { - if (token == -1) { +ReadData* BinTreeNodeReader::readString(int token) +{ + if (token == -1) throw WAException("-1 token in readString", WAException::CORRUPT_STREAM_EX, -1); - } + int bSize; ReadData* ret = new ReadData(); - if ((token > 4) && (token < 245)) { + if (token > 2 && token <= this->tokenmapsize) { + if (token != this->tokenmapsize) + ret->data = new std::string(this->tokenMap[token]); + else { + token = readInt8(this->in); + if (token >= 0 && token < _countof(secondary_dict)) + ret->data = new std::string(secondary_dict[token]); + else + throw WAException("invalid token/length in getToken", WAException::CORRUPT_STREAM_EX, 0); + } + ret->type = STRING; - ret->data = new std::string(getToken(token)); return ret; } - switch(token) { + switch (token) { case 0: return NULL; - case 252: { - int size8 = readInt8(this->in); - std::vector* buf8 = new std::vector(size8); - fillArray(*buf8, size8, this->in); - // std::string* ret = new std::string(buf8->begin(), buf8->end()); - // delete buf8; - ret->type = ARRAY; - ret->data = buf8; - return ret; - } - case 253: { - int size24 = readInt24(this->in); - std::vector* buf24 = new std::vector(size24); - fillArray(*buf24, size24, this->in); - // std::string* ret = new std::string(buf24->begin(), buf24->end()); - // delete buf24; - ret->type = ARRAY; - ret->data = buf24; + case 252: + bSize = readInt8(this->in); + { + std::vector* buf8 = new std::vector(bSize); + fillArray(*buf8, bSize, this->in); + ret->type = ARRAY; + ret->data = buf8; + } return ret; - } - case 254: { - token = (unsigned char) this->in->read(); - ret->type = STRING; - ret->data = new std::string(getToken(245 + token)); + + case 253: + bSize = readInt24(this->in); + { + std::vector* buf24 = new std::vector(bSize); + fillArray(*buf24, bSize, this->in); + ret->type = ARRAY; + ret->data = buf24; + } return ret; - } - case 250: { + + case 255: + bSize = readInt8(this->in); + { + int size = bSize & 0x7f; + int numnibbles = size * 2 - ((bSize & 0x80) ? 1 : 0); + + std::vector tmp(size); + fillArray(tmp, size, this->in); + std::string s; + for (int i = 0; i < numnibbles; i++) { + char c = (tmp[i / 2] >> (4 - ((i & 1) << 2))) & 0xF; + if (c < 10) s += (c + '0'); + else s += (c - 10 + '-'); + } + + ret->type = STRING; + ret->data = new std::string(s); + } + return ret; + + case 250: std::string* user = readStringAsString(); std::string* server = readStringAsString(); if ((user != NULL) && (server != NULL)) { @@ -201,14 +250,13 @@ ReadData* BinTreeNodeReader::readString(int token) { } throw WAException("readString couldn't reconstruct jid", WAException::CORRUPT_STREAM_EX, -1); } - } - throw WAException("readString couldn't match token" + (int) token, WAException::CORRUPT_STREAM_EX, -1); + throw WAException("readString couldn't match token" + (int)token, WAException::CORRUPT_STREAM_EX, -1); } -std::string* BinTreeNodeReader::objectAsString(ReadData* o) { - if (o->type == STRING) { +std::string* BinTreeNodeReader::objectAsString(ReadData* o) +{ + if (o->type == STRING) return (std::string*) o->data; - } if (o->type == ARRAY) { std::vector* v = (std::vector*) o->data; @@ -220,61 +268,44 @@ std::string* BinTreeNodeReader::objectAsString(ReadData* o) { return NULL; } -std::string* BinTreeNodeReader::readStringAsString() { +std::string* BinTreeNodeReader::readStringAsString() +{ ReadData* o = this->readString(); std::string* ret = this->objectAsString(o); delete o; return ret; } -std::string* BinTreeNodeReader::readStringAsString(int token) { +std::string* BinTreeNodeReader::readStringAsString(int token) +{ ReadData* o = this->readString(token); std::string* ret = this->objectAsString(o); delete o; return ret; } - -void BinTreeNodeReader::fillArray(std::vector& buff, int len, ByteArrayInputStream* in) { +void BinTreeNodeReader::fillArray(std::vector& buff, int len, ByteArrayInputStream* in) +{ int count = 0; - while (count < len) { + while (count < len) count += in->read(buff, count, len - count); - } } -void BinTreeNodeReader::fillArray(std::vector& buff, int len, ISocketConnection* in) { +void BinTreeNodeReader::fillArray(std::vector& buff, int len, ISocketConnection* in) +{ int count = 0; - while (count < len) { + while (count < len) count += in->read(buff, count, len - count); - } } +void BinTreeNodeReader::getTopLevelStream() +{ + int stanzaSize = readInt24(this->rawIn); + int flags = (stanzaSize >> 20); + stanzaSize &= 0x0FFFFF; -std::string BinTreeNodeReader::getToken(int token) { - std::string ret; - - if ((token >= 0) && (token < this->tokenmapsize)) - ret = std::string(this->tokenMap[token]); - if (ret.empty()) { - throw WAException("invalid token/length in getToken", WAException::CORRUPT_STREAM_EX, 0); - } - return ret; -} - - -void BinTreeNodeReader::getTopLevelStream() { - int stanzaSize; - int flags; - int byte = readInt8(this->rawIn); - flags = byte >> 4; - int size0 = byte & 15; - int size1 = readInt8(this->rawIn); - int size2 = readInt8(this->rawIn); - - stanzaSize = (size0 << 16) + (size1 << 8) + size2; - - if (this->buf->size() < (size_t) stanzaSize) { - int newsize = max((int) (this->buf->size() * 3 / 2), stanzaSize); + if (this->buf->size() < (size_t)stanzaSize) { + int newsize = max((int)(this->buf->size() * 3 / 2), stanzaSize); delete this->buf; this->buf = new std::vector(newsize); } @@ -283,18 +314,21 @@ void BinTreeNodeReader::getTopLevelStream() { this->decodeStream(flags, 0, stanzaSize); } -int BinTreeNodeReader::readInt8(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt8(ByteArrayInputStream* in) +{ return in->read(); } -int BinTreeNodeReader::readInt16(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt16(ByteArrayInputStream* in) +{ int intTop = in->read(); int intBot = in->read(); int value = (intTop << 8) + intBot; return value; } -int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) +{ int int1 = in->read(); int int2 = in->read(); int int3 = in->read(); @@ -303,12 +337,14 @@ int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) { return value; } -ProtocolTreeNode* BinTreeNodeReader::nextTree() { +ProtocolTreeNode* BinTreeNodeReader::nextTree() +{ this->getTopLevelStream(); return nextTreeInternal(); } -void BinTreeNodeReader::streamStart() { +void BinTreeNodeReader::streamStart() +{ this->getTopLevelStream(); int tag = this->in->read(); @@ -319,26 +355,25 @@ void BinTreeNodeReader::streamStart() { } int attribCount = (size - 2 + size % 2) / 2; - std::map* attributes = readAttributes(attribCount); + std::map* attributes = readAttributes(attribCount); delete attributes; } -int BinTreeNodeReader::readInt8(ISocketConnection* in) { +int BinTreeNodeReader::readInt8(ISocketConnection* in) +{ return in->read(); } -int BinTreeNodeReader::readInt16(ISocketConnection* in) { - int intTop = in->read(); - int intBot = in->read(); - int value = (intTop << 8) + intBot; - return value; +int BinTreeNodeReader::readInt16(ISocketConnection* in) +{ + unsigned char data[2]; + in->read(data, 2); + return (int(data[0]) << 8) + int(data[1]); } -int BinTreeNodeReader::readInt24(ISocketConnection* in) { - int int1 = in->read(); - int int2 = in->read(); - int int3 = in->read(); - int value = (int1 << 16) + (int2 << 8) + int3; - - return value; +int BinTreeNodeReader::readInt24(ISocketConnection* in) +{ + unsigned char data[3]; + in->read(data, 3); + return (int(data[0]) << 16) + (int(data[1]) << 8) + int(data[2]); } -- cgit v1.2.3