summaryrefslogtreecommitdiff
path: root/protocols/Facebook/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-12-23 19:46:00 +0300
committerGeorge Hazan <ghazan@miranda.im>2019-12-23 19:46:00 +0300
commit22a846c74f1903eb9b881b0d3b4615b249960be9 (patch)
tree56d18d9d6cc94a252a1f12703388f2b3e258499b /protocols/Facebook/src
parente016a7802692a2ccfba34c2505dbd4b14a39d715 (diff)
Facebook: presence packet parsing
Diffstat (limited to 'protocols/Facebook/src')
-rw-r--r--protocols/Facebook/src/mqtt.cpp170
-rw-r--r--protocols/Facebook/src/mqtt.h27
-rw-r--r--protocols/Facebook/src/proto.h3
-rw-r--r--protocols/Facebook/src/server.cpp82
-rw-r--r--protocols/Facebook/src/stdafx.h1
-rw-r--r--protocols/Facebook/src/thrift.cpp306
6 files changed, 459 insertions, 130 deletions
diff --git a/protocols/Facebook/src/mqtt.cpp b/protocols/Facebook/src/mqtt.cpp
index 72ed222079..c80544176b 100644
--- a/protocols/Facebook/src/mqtt.cpp
+++ b/protocols/Facebook/src/mqtt.cpp
@@ -20,36 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-static uint8_t encodeType(int type)
-{
- switch (type) {
- case FB_THRIFT_TYPE_BOOL:
- return 2;
- case FB_THRIFT_TYPE_BYTE:
- return 3;
- case FB_THRIFT_TYPE_I16:
- return 4;
- case FB_THRIFT_TYPE_I32:
- return 5;
- case FB_THRIFT_TYPE_I64:
- return 6;
- case FB_THRIFT_TYPE_DOUBLE:
- return 7;
- case FB_THRIFT_TYPE_STRING:
- return 8;
- case FB_THRIFT_TYPE_LIST:
- return 9;
- case FB_THRIFT_TYPE_SET:
- return 10;
- case FB_THRIFT_TYPE_MAP:
- return 11;
- case FB_THRIFT_TYPE_STRUCT:
- return 12;
- default:
- return 0;
- }
-}
-
uint8_t *FacebookProto::doZip(size_t cbData, const void *pData, size_t &cbRes)
{
size_t dataSize = cbData + 100;
@@ -101,91 +71,7 @@ uint8_t *FacebookProto::doUnzip(size_t cbData, const void *pData, size_t &cbRes)
}
/////////////////////////////////////////////////////////////////////////////////////////
-
-FbThrift& FbThrift::operator<<(uint8_t value)
-{
- m_buf.append(&value, 1);
- return *this;
-}
-
-FbThrift& FbThrift::operator<<(const char *str)
-{
- size_t len = mir_strlen(str);
- writeIntV(len);
- m_buf.append((void*)str, len);
- return *this;
-}
-
-void FbThrift::writeBool(bool bValue)
-{
- uint8_t b = (bValue) ? 0x11 : 0x12;
- m_buf.append(&b, 1);
-}
-
-void FbThrift::writeBuf(const void *pData, size_t cbLen)
-{
- m_buf.append((void*)pData, cbLen);
-}
-
-void FbThrift::writeField(int iType)
-{
- uint8_t type = encodeType(iType) + 0x10;
- m_buf.append(&type, 1);
-}
-
-void FbThrift::writeField(int iType, int id, int lastid)
-{
- uint8_t type = encodeType(iType);
- uint8_t diff = uint8_t(id - lastid);
- if (diff > 0x0F) {
- m_buf.append(&type, 1);
- writeInt64(id);
- }
- else {
- type += (diff << 4);
- m_buf.append(&type, 1);
- }
-}
-
-void FbThrift::writeList(int iType, int size)
-{
- uint8_t type = encodeType(iType);
- if (size > 14) {
- writeIntV(size);
- *this << (type | 0xF0);
- }
- else *this << (type | (size << 4));
-}
-
-void FbThrift::writeInt16(uint16_t value)
-{
- value = htons(value);
- m_buf.append(&value, sizeof(value));
-}
-
-void FbThrift::writeInt32(int32_t value)
-{
- writeIntV((value << 1) ^ (value >> 31));
-}
-
-void FbThrift::writeInt64(int64_t value)
-{
- writeIntV((value << 1) ^ (value >> 63));
-}
-
-void FbThrift::writeIntV(uint64_t value)
-{
- bool bLast;
- do {
- bLast = (value & ~0x7F) == 0;
- uint8_t b = value & 0x7F;
- if (!bLast) {
- b |= 0x80;
- value >>= 7;
- }
- m_buf.append(&b, 1);
- } while (!bLast);
-}
+// MqttMessage class members
MqttMessage::MqttMessage() :
m_leadingByte(0)
@@ -197,6 +83,19 @@ MqttMessage::MqttMessage(FbMqttMessageType type, uint8_t flags)
m_leadingByte = ((type & 0x0F) << 4) | (flags & 0x0F);
}
+char* MqttMessage::readStr(const uint8_t *&pData) const
+{
+ u_short len = ntohs(*(u_short *)pData); pData += sizeof(u_short);
+ if (len == 0)
+ return nullptr;
+
+ char *res = (char*)mir_alloc(len + 1);
+ memcpy(res, pData, len);
+ res[len] = 0;
+ pData += len;
+ return res;
+}
+
void MqttMessage::writeStr(const char *str)
{
size_t len = mir_strlen(str);
@@ -224,7 +123,9 @@ bool FacebookProto::MqttConnect()
bool FacebookProto::MqttParse(const MqttMessage &payload)
{
- auto *pData = (const uint8_t *)payload.data();
+ auto *pData = (const uint8_t *)payload.data(), *pBeg = pData;
+ int flags = payload.getFlags();
+ uint16_t mid;
switch (payload.getType()) {
case FB_MQTT_MESSAGE_TYPE_CONNACK:
@@ -236,6 +137,30 @@ bool FacebookProto::MqttParse(const MqttMessage &payload)
OnLoggedIn();
MqttPing();
break;
+
+ case FB_MQTT_MESSAGE_TYPE_PUBREL:
+ mid = ntohs(*(u_short *)pData);
+ {
+ MqttMessage reply(FB_MQTT_MESSAGE_TYPE_PUBCOMP);
+ reply << mid;
+ MqttSend(reply);
+ }
+ break;
+
+ case FB_MQTT_MESSAGE_TYPE_PUBLISH:
+ char *str = payload.readStr(pData);
+
+ if ((flags & FB_MQTT_MESSAGE_FLAG_QOS1) || (flags & FB_MQTT_MESSAGE_FLAG_QOS2)) {
+ mid = ntohs(*(u_short *)pData);
+
+ MqttMessage reply((flags & FB_MQTT_MESSAGE_FLAG_QOS1) ? FB_MQTT_MESSAGE_TYPE_PUBACK : FB_MQTT_MESSAGE_TYPE_PUBREC);
+ reply << mid;
+ MqttSend(reply);
+ }
+
+ OnPublish(str, pData, payload.size() - (pData - pBeg));
+ mir_free(str);
+ break;
}
return true;
@@ -271,19 +196,6 @@ bool FacebookProto::MqttRead(MqttMessage &payload)
payload.writeBuf(buf, res);
remainingBytes -= res;
}
-
- // that might be a zipped buffer
- if (payload.size() >= 2) {
- auto *p = (const uint8_t *)payload.data();
- if ((((p[0] << 8) | p[1]) % 31) == 0 && (p[0] & 0x0F) == 8) { // zip header ok
- size_t dataSize;
- void *pData = doUnzip(payload.size(), payload.data(), dataSize);
- if (pData != nullptr) {
- payload.reset(dataSize, pData);
- mir_free(pData);
- }
- }
- }
}
return true;
diff --git a/protocols/Facebook/src/mqtt.h b/protocols/Facebook/src/mqtt.h
index dc7fcd41fc..57df55a7a2 100644
--- a/protocols/Facebook/src/mqtt.h
+++ b/protocols/Facebook/src/mqtt.h
@@ -62,7 +62,7 @@ public:
__forceinline void* data() const { return m_buf.data(); }
__forceinline size_t size() const { return m_buf.length(); }
- __forceinline void reset(const size_t cbLen, void *pData)
+ __forceinline void reset(size_t cbLen, void *pData)
{ m_buf.assign(pData, cbLen);
}
@@ -80,6 +80,26 @@ public:
void writeList(int iType, int size);
};
+class FbThriftReader : public FbThrift
+{
+ bool m_lastBool = false, m_lastBval = false;
+ size_t offset = 0;
+
+ uint8_t decodeType(int type);
+
+public:
+ bool isStop();
+ bool readBool(bool &bVal);
+ bool readByte(uint8_t &val);
+ bool readField(uint8_t &type, uint16_t &id);
+ bool readInt16(uint16_t &val);
+ bool readInt32(uint32_t &val);
+ bool readInt64(uint64_t &val);
+ bool readIntV(uint64_t &val);
+ bool readList(uint8_t &type, uint32_t &size);
+ bool readStr(char *&val); // val must be freed via mir_free()
+};
+
class MqttMessage : public FbThrift
{
friend class FacebookProto;
@@ -99,6 +119,7 @@ public:
{ return m_leadingByte & 0x0F;
}
+ char* readStr(const uint8_t *&pData) const;
void writeStr(const char *str);
};
@@ -110,3 +131,7 @@ public:
#define FB_MQTT_CONNECT_FLAG_RET 0x0020
#define FB_MQTT_CONNECT_FLAG_PASS 0x0040
#define FB_MQTT_CONNECT_FLAG_USER 0x0080
+
+#define FB_MQTT_MESSAGE_FLAG_QOS0 0x0000
+#define FB_MQTT_MESSAGE_FLAG_QOS1 0x0002
+#define FB_MQTT_MESSAGE_FLAG_QOS2 0x0004
diff --git a/protocols/Facebook/src/proto.h b/protocols/Facebook/src/proto.h
index 01a0bae4f4..ad46f4c454 100644
--- a/protocols/Facebook/src/proto.h
+++ b/protocols/Facebook/src/proto.h
@@ -359,6 +359,9 @@ class FacebookProto : public PROTO<FacebookProto>
bool MqttParse(const MqttMessage &payload);
void MqttSend(const MqttMessage &payload);
+ void OnPublish(const char *str, const uint8_t *payLoad, size_t cbLen);
+ void OnPublishP(FbThriftReader &rdr);
+
HNETLIBCONN m_mqttConn;
// internal data
diff --git a/protocols/Facebook/src/server.cpp b/protocols/Facebook/src/server.cpp
index a2753154e5..573f7a2c85 100644
--- a/protocols/Facebook/src/server.cpp
+++ b/protocols/Facebook/src/server.cpp
@@ -138,3 +138,85 @@ FAIL:
m_iDesiredStatus = m_iStatus = ID_STATUS_OFFLINE;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus);
}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void FacebookProto::OnPublish(const char *topic, const uint8_t *p, size_t cbLen)
+{
+ FbThriftReader rdr;
+
+ // that might be a zipped buffer
+ if (cbLen >= 2) {
+ if ((((p[0] << 8) | p[1]) % 31) == 0 && (p[0] & 0x0F) == 8) { // zip header ok
+ size_t dataSize;
+ void *pData = doUnzip(cbLen, p, dataSize);
+ if (pData != nullptr) {
+ rdr.reset(dataSize, pData);
+ mir_free(pData);
+ }
+ }
+ }
+
+ if (rdr.size() == 0)
+ rdr.reset(cbLen, (void*)p);
+
+ if (!strcmp(topic, "/t_p"))
+ OnPublishP(rdr);
+
+}
+
+void FacebookProto::OnPublishP(FbThriftReader &rdr)
+{
+ char *str;
+ assert(rdr.readStr(str));
+ mir_free(str);
+
+ bool bVal;
+ uint8_t fieldType;
+ uint16_t fieldId;
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_BOOL);
+ assert(fieldId == 1);
+ assert(rdr.readBool(bVal));
+
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_LIST);
+ assert(fieldId == 1);
+
+ uint32_t size;
+ assert(rdr.readList(fieldType, size));
+ assert(fieldType == FB_THRIFT_TYPE_STRUCT);
+
+ for (uint32_t i = 0; i < size; i++) {
+ uint64_t userId, timestamp, voipBits;
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_I64);
+ assert(fieldId == 1);
+ assert(rdr.readInt64(userId));
+
+ uint32_t u32;
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_I32);
+ assert(fieldId == 1);
+ assert(rdr.readInt32(u32));
+
+ debugLogA("Presence from user %lld => %d", userId, u32);
+
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_I64);
+ assert(fieldId == 1);
+ assert(rdr.readInt64(timestamp));
+
+ while (!rdr.isStop()) {
+ assert(rdr.readField(fieldType, fieldId));
+ assert(fieldType == FB_THRIFT_TYPE_I64 || fieldType == FB_THRIFT_TYPE_I16 || fieldType == FB_THRIFT_TYPE_I32);
+ assert(rdr.readIntV(voipBits));
+ }
+
+ assert(rdr.readByte(fieldType));
+ assert(fieldType == FB_THRIFT_TYPE_STOP);
+ }
+
+ assert(rdr.readByte(fieldType));
+ assert(fieldType == FB_THRIFT_TYPE_STOP);
+}
diff --git a/protocols/Facebook/src/stdafx.h b/protocols/Facebook/src/stdafx.h
index 95eb7d76a5..f24195c746 100644
--- a/protocols/Facebook/src/stdafx.h
+++ b/protocols/Facebook/src/stdafx.h
@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
+#include <assert.h>
#include <win2k.h>
#include <newpluginapi.h>
diff --git a/protocols/Facebook/src/thrift.cpp b/protocols/Facebook/src/thrift.cpp
new file mode 100644
index 0000000000..aefc7143a9
--- /dev/null
+++ b/protocols/Facebook/src/thrift.cpp
@@ -0,0 +1,306 @@
+/*
+
+Facebook plugin for Miranda NG
+Copyright © 2019 Miranda NG team
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// FbThrift class members
+
+static uint8_t encodeType(int type)
+{
+ switch (type) {
+ case FB_THRIFT_TYPE_BOOL:
+ return 2;
+ case FB_THRIFT_TYPE_BYTE:
+ return 3;
+ case FB_THRIFT_TYPE_I16:
+ return 4;
+ case FB_THRIFT_TYPE_I32:
+ return 5;
+ case FB_THRIFT_TYPE_I64:
+ return 6;
+ case FB_THRIFT_TYPE_DOUBLE:
+ return 7;
+ case FB_THRIFT_TYPE_STRING:
+ return 8;
+ case FB_THRIFT_TYPE_LIST:
+ return 9;
+ case FB_THRIFT_TYPE_SET:
+ return 10;
+ case FB_THRIFT_TYPE_MAP:
+ return 11;
+ case FB_THRIFT_TYPE_STRUCT:
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+FbThrift& FbThrift::operator<<(uint8_t value)
+{
+ m_buf.append(&value, 1);
+ return *this;
+}
+
+FbThrift& FbThrift::operator<<(const char *str)
+{
+ size_t len = mir_strlen(str);
+ writeIntV(len);
+ m_buf.append((void*)str, len);
+ return *this;
+}
+
+void FbThrift::writeBool(bool bValue)
+{
+ uint8_t b = (bValue) ? 0x11 : 0x12;
+ m_buf.append(&b, 1);
+}
+
+void FbThrift::writeBuf(const void *pData, size_t cbLen)
+{
+ m_buf.append((void*)pData, cbLen);
+}
+
+void FbThrift::writeField(int iType)
+{
+ uint8_t type = encodeType(iType) + 0x10;
+ m_buf.append(&type, 1);
+}
+
+void FbThrift::writeField(int iType, int id, int lastid)
+{
+ uint8_t type = encodeType(iType);
+ uint8_t diff = uint8_t(id - lastid);
+ if (diff > 0x0F) {
+ m_buf.append(&type, 1);
+ writeInt64(id);
+ }
+ else {
+ type += (diff << 4);
+ m_buf.append(&type, 1);
+ }
+}
+
+void FbThrift::writeList(int iType, int size)
+{
+ uint8_t type = encodeType(iType);
+ if (size > 14) {
+ writeIntV(size);
+ *this << (type | 0xF0);
+ }
+ else *this << (type | (size << 4));
+}
+
+void FbThrift::writeInt16(uint16_t value)
+{
+ value = htons(value);
+ m_buf.append(&value, sizeof(value));
+}
+
+void FbThrift::writeInt32(int32_t value)
+{
+ writeIntV((value << 1) ^ (value >> 31));
+}
+
+void FbThrift::writeInt64(int64_t value)
+{
+ writeIntV((value << 1) ^ (value >> 63));
+}
+
+void FbThrift::writeIntV(uint64_t value)
+{
+ bool bLast;
+ do {
+ bLast = (value & ~0x7F) == 0;
+ uint8_t b = value & 0x7F;
+ if (!bLast) {
+ b |= 0x80;
+ value >>= 7;
+ }
+ m_buf.append(&b, 1);
+ } while (!bLast);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// FbThriftReader class members
+
+uint8_t FbThriftReader::decodeType(int type)
+{
+ switch (type) {
+ case 0:
+ return FB_THRIFT_TYPE_STOP;
+ case 1:
+ m_lastBval = m_lastBool = true;
+ return FB_THRIFT_TYPE_BOOL;
+ case 2:
+ m_lastBool = true;
+ m_lastBval = false;
+ return FB_THRIFT_TYPE_BOOL;
+ case 3:
+ return FB_THRIFT_TYPE_BYTE;
+ case 4:
+ return FB_THRIFT_TYPE_I16;
+ case 5:
+ return FB_THRIFT_TYPE_I32;
+ case 6:
+ return FB_THRIFT_TYPE_I64;
+ case 7:
+ return FB_THRIFT_TYPE_DOUBLE;
+ case 8:
+ return FB_THRIFT_TYPE_STRING;
+ case 9:
+ return FB_THRIFT_TYPE_LIST;
+ case 10:
+ return FB_THRIFT_TYPE_SET;
+ case 11:
+ return FB_THRIFT_TYPE_MAP;
+ case 12:
+ return FB_THRIFT_TYPE_STRUCT;
+ default:
+ return 0;
+ }
+}
+
+bool FbThriftReader::isStop()
+{
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ offset--;
+ return b == FB_THRIFT_TYPE_STOP;
+}
+
+bool FbThriftReader::readBool(bool &bVal)
+{
+ if (m_lastBool) {
+ bVal = m_lastBval;
+ m_lastBool = false;
+ return true;
+ }
+
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ bVal = b == 0x11;
+ return true;
+}
+
+bool FbThriftReader::readByte(uint8_t &b)
+{
+ if (offset >= size())
+ return false;
+
+ b = *((uint8_t *)data() + offset);
+ offset++;
+ return true;
+}
+
+bool FbThriftReader::readField(uint8_t &type, uint16_t &id)
+{
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ type = decodeType(b & 0x0F);
+ id = (b >> 4);
+ return (id == 0) ? readInt16(id) : true;
+}
+
+bool FbThriftReader::readIntV(uint64_t &val)
+{
+ uint8_t b;
+ unsigned i = 0;
+ val = 0;
+
+ do {
+ if (!readByte(b))
+ return false;
+
+ val |= ((b & 0x7F) << i);
+ i += 7;
+ } while ((b & 0x80) != 0);
+
+ return true;
+}
+
+bool FbThriftReader::readList(uint8_t &type, uint32_t &size)
+{
+ byte b;
+ if (!readByte(b))
+ return false;
+
+ type = decodeType(b & 0x0F);
+ size = b >> 4;
+ if (size == 0x0F) {
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+ size = (uint32_t)tmp;
+ }
+ return true;
+}
+
+bool FbThriftReader::readStr(char *&val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ uint32_t cbLen = (uint32_t)tmp;
+ if (offset + cbLen >= size())
+ return false;
+
+ if (cbLen > 0) {
+ val = mir_strndup((char *)data() + offset, cbLen);
+ offset += cbLen;
+ }
+ else val = nullptr;
+ return true;
+}
+
+bool FbThriftReader::readInt16(uint16_t &val)
+{
+ if (offset + 2 >= size())
+ return false;
+
+ val = ntohs(*(u_short *)((char *)data() + offset));
+ offset += 2;
+ return true;
+}
+
+bool FbThriftReader::readInt32(uint32_t &val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ val = (uint32_t )tmp;
+ return true;
+}
+
+bool FbThriftReader::readInt64(uint64_t &val)
+{
+ uint64_t tmp;
+ if (!readIntV(tmp))
+ return false;
+
+ val = (tmp >> 0x01) ^ -(tmp & 0x01);
+ return true;
+}