summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/Facebook/src/mqtt.cpp41
-rw-r--r--protocols/Facebook/src/proto.cpp17
-rw-r--r--protocols/Facebook/src/proto.h23
-rw-r--r--protocols/Facebook/src/server.cpp131
-rw-r--r--protocols/Facebook/src/thrift.cpp2
5 files changed, 141 insertions, 73 deletions
diff --git a/protocols/Facebook/src/mqtt.cpp b/protocols/Facebook/src/mqtt.cpp
index c80544176b..8731e2bcd8 100644
--- a/protocols/Facebook/src/mqtt.cpp
+++ b/protocols/Facebook/src/mqtt.cpp
@@ -31,16 +31,9 @@ uint8_t *FacebookProto::doZip(size_t cbData, const void *pData, size_t &cbRes)
zStreamOut.next_in = (uint8_t *)pData;
zStreamOut.avail_out = (unsigned)dataSize;
zStreamOut.next_out = (uint8_t *)pRes;
-
- switch (deflate(&zStreamOut, Z_FINISH)) {
- case Z_STREAM_END: debugLogA("Deflate: Z_STREAM_END"); break;
- case Z_OK: debugLogA("Deflate: Z_OK"); break;
- case Z_BUF_ERROR: debugLogA("Deflate: Z_BUF_ERROR"); break;
- case Z_DATA_ERROR: debugLogA("Deflate: Z_DATA_ERROR"); break;
- case Z_MEM_ERROR: debugLogA("Deflate: Z_MEM_ERROR"); break;
- }
-
+ deflate(&zStreamOut, Z_FINISH);
deflateEnd(&zStreamOut);
+
cbRes = dataSize - zStreamOut.avail_out;
return pRes;
}
@@ -56,16 +49,9 @@ uint8_t *FacebookProto::doUnzip(size_t cbData, const void *pData, size_t &cbRes)
zStreamOut.next_in = (uint8_t *)pData;
zStreamOut.avail_out = (unsigned)dataSize;
zStreamOut.next_out = (uint8_t *)pRes;
-
- switch (inflate(&zStreamOut, Z_FINISH)) {
- case Z_STREAM_END: debugLogA("Deflate: Z_STREAM_END"); break;
- case Z_OK: debugLogA("Deflate: Z_OK"); break;
- case Z_BUF_ERROR: debugLogA("Deflate: Z_BUF_ERROR"); break;
- case Z_DATA_ERROR: debugLogA("Deflate: Z_DATA_ERROR"); break;
- case Z_MEM_ERROR: debugLogA("Deflate: Z_MEM_ERROR"); break;
- }
-
+ inflate(&zStreamOut, Z_FINISH);
inflateEnd(&zStreamOut);
+
cbRes = dataSize - zStreamOut.avail_out;
return pRes;
}
@@ -99,28 +85,13 @@ char* MqttMessage::readStr(const uint8_t *&pData) const
void MqttMessage::writeStr(const char *str)
{
size_t len = mir_strlen(str);
- writeInt16((int)len);
+ writeInt16((uint16_t)len);
writeBuf(str, len);
}
/////////////////////////////////////////////////////////////////////////////////////////
// MQTT functions
-bool FacebookProto::MqttConnect()
-{
- NETLIBOPENCONNECTION nloc = {};
- nloc.szHost = "mqtt.facebook.com";
- nloc.wPort = 443;
- nloc.flags = NLOCF_SSL | NLOCF_V2;
- m_mqttConn = Netlib_OpenConnection(m_hNetlibUser, &nloc);
- if (m_mqttConn == nullptr) {
- debugLogA("connection failed, exiting");
- return false;
- }
-
- return true;
-}
-
bool FacebookProto::MqttParse(const MqttMessage &payload)
{
auto *pData = (const uint8_t *)payload.data(), *pBeg = pData;
@@ -213,7 +184,7 @@ void FacebookProto::MqttSend(const MqttMessage &payload)
/////////////////////////////////////////////////////////////////////////////////////////
// creates initial MQTT will and sends initialization packet
-void FacebookProto::MqttOpen()
+void FacebookProto::MqttLogin()
{
uint8_t zeroByte = 0;
Utils_GetRandom(&m_iMqttId, sizeof(m_iMqttId) / 2);
diff --git a/protocols/Facebook/src/proto.cpp b/protocols/Facebook/src/proto.cpp
index 0f60c7c420..31b99b2100 100644
--- a/protocols/Facebook/src/proto.cpp
+++ b/protocols/Facebook/src/proto.cpp
@@ -20,9 +20,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
+static int CompareUsers(const FacebookUser *p1, const FacebookUser *p2)
+{
+ if (p1->id == p2->id)
+ return 0;
+
+ return (p1->id < p2->id) ? -1 : 1;
+}
+
FacebookProto::FacebookProto(const char *proto_name, const wchar_t *username) :
- PROTO<FacebookProto>(proto_name, username)
+ PROTO<FacebookProto>(proto_name, username),
+ m_users(50, CompareUsers)
{
+ for (auto &cc : AccContacts()) {
+ CMStringA szId(getMStringA(cc, DBKEY_ID));
+ if (!szId.IsEmpty())
+ m_users.insert(new FacebookUser(_atoi64(szId), cc));
+ }
+
// to upgrade previous settings
if (getByte("Compatibility") < 1) {
setByte("Compatibility", 1);
diff --git a/protocols/Facebook/src/proto.h b/protocols/Facebook/src/proto.h
index ad46f4c454..7d5bedc911 100644
--- a/protocols/Facebook/src/proto.h
+++ b/protocols/Facebook/src/proto.h
@@ -337,6 +337,17 @@ public:
__forceinline int error() const { return m_errorCode; }
};
+struct FacebookUser
+{
+ FacebookUser(__int64 _p1, MCONTACT _p2) :
+ id(_p1),
+ hContact(_p2)
+ {}
+
+ __int64 id;
+ MCONTACT hContact;
+};
+
class FacebookProto : public PROTO<FacebookProto>
{
uint8_t *doZip(size_t cbData, const void *pData, size_t &cbRes);
@@ -347,8 +358,7 @@ class FacebookProto : public PROTO<FacebookProto>
NETLIBHTTPREQUEST *ExecuteRequest(AsyncHttpRequest *pReq);
// MQTT functions
- bool MqttConnect();
- void MqttOpen();
+ void MqttLogin();
void MqttPing();
void MqttPublish(const char *topic, const char *value);
@@ -376,9 +386,18 @@ class FacebookProto : public PROTO<FacebookProto>
CMStringA m_szAuthToken; // calculated
+ OBJLIST<FacebookUser> m_users;
+ FacebookUser *FindUser(__int64 id)
+ {
+ return m_users.find((FacebookUser *)&id);
+ }
+
void OnLoggedIn();
void OnLoggedOut();
+ bool RefreshToken();
+ bool RefreshContacts();
+
void __cdecl ServerThread(void *);
public:
diff --git a/protocols/Facebook/src/server.cpp b/protocols/Facebook/src/server.cpp
index 573f7a2c85..33d6b69f4a 100644
--- a/protocols/Facebook/src/server.cpp
+++ b/protocols/Facebook/src/server.cpp
@@ -41,59 +41,116 @@ void FacebookProto::OnLoggedOut()
m_bOnline = false;
}
-/////////////////////////////////////////////////////////////////////////////////////////
+bool FacebookProto::RefreshContacts()
+{
+ auto *pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS);
+ pReq << CHAR_PARAM("query_params", "{\"0\":[\"user\"],\"1\":\"" FB_API_CONTACTS_COUNT "\"}");
+ pReq->CalcSig();
-void FacebookProto::ServerThread(void *)
+ JsonReply reply(ExecuteRequest(pReq));
+ if (reply.error())
+ return false;
+
+ for (auto &it : reply.data()["viewer"]["messenger_contacts"]["nodes"]) {
+ auto &n = it["represented_profile"];
+ CMStringW wszId(n["id"].as_mstring());
+ __int64 id = _wtoi64(wszId);
+
+ MCONTACT hContact;
+ if (id != m_uid) {
+ auto *pUser = FindUser(id);
+ if (pUser == nullptr) {
+ hContact = db_add_contact();
+ Proto_AddToContact(hContact, m_szModuleName);
+ setWString(hContact, DBKEY_ID, wszId);
+
+ m_users.insert(new FacebookUser(id, hContact));
+ }
+ else hContact = pUser->hContact;
+ }
+ else hContact = 0;
+
+ if (auto &nName = n["structured_name"]) {
+ CMStringW wszName(nName["text"].as_mstring());
+ setWString(hContact, DBKEY_NICK, wszName);
+ for (auto &nn : nName["parts"]) {
+ CMStringW wszPart(nn["part"].as_mstring());
+ int offset = nn["offset"].as_int(), length = nn["length"].as_int();
+ if (wszPart == L"first")
+ setWString(hContact, DBKEY_FIRST_NAME, wszName.Mid(offset, length));
+ else if (wszPart == L"last")
+ setWString(hContact, DBKEY_LAST_NAME, wszName.Mid(offset, length));
+ }
+ }
+
+ if (auto &nBirth = n["birthdate"]) {
+ setDword(hContact, "BirthDay", nBirth["day"].as_int());
+ setDword(hContact, "BirthMonth", nBirth["month"].as_int());
+ }
+
+ if (auto &nCity = n["current_city"])
+ setDword(hContact, "City", nCity["name"].as_int());
+
+ if (auto &nAva = n["smallPictureUrl"])
+ setWString(hContact, "Avatar", nAva["uri"].as_mstring());
+ }
+ return true;
+}
+
+bool FacebookProto::RefreshToken()
{
- m_szAuthToken = getMStringA(DBKEY_TOKEN);
+ auto *pReq = CreateRequest("authenticate", "auth.login");
+ pReq->m_szUrl = FB_API_URL_AUTH;
+ pReq << CHAR_PARAM("email", getMStringA(DBKEY_LOGIN));
+ pReq << CHAR_PARAM("password", getMStringA(DBKEY_PASS));
+ pReq->CalcSig();
- if (m_szAuthToken.IsEmpty()) {
- auto *pReq = CreateRequest("authenticate", "auth.login");
- pReq->m_szUrl = FB_API_URL_AUTH;
+ JsonReply reply(ExecuteRequest(pReq));
+ if (reply.error())
+ return false;
- pReq << CHAR_PARAM("email", getMStringA(DBKEY_LOGIN));
- pReq << CHAR_PARAM("password", getMStringA(DBKEY_PASS));
+ m_szAuthToken = reply.data()["access_token"].as_mstring();
+ setString(DBKEY_TOKEN, m_szAuthToken);
- pReq->CalcSig();
+ m_uid = reply.data()["uid"].as_int();
+ CMStringA m_szUid = reply.data()["uid"].as_mstring();
+ setString(DBKEY_ID, m_szUid);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
- JsonReply reply(ExecuteRequest(pReq));
+void FacebookProto::ServerThread(void *)
+{
+ m_szAuthToken = getMStringA(DBKEY_TOKEN);
- if (reply.error()) {
+ if (m_szAuthToken.IsEmpty()) {
+ if (!RefreshToken()) {
+FAIL:
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)m_iStatus, m_iDesiredStatus);
m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus);
return;
}
-
- m_szAuthToken = reply.data()["access_token"].as_mstring();
- setString(DBKEY_TOKEN, m_szAuthToken);
-
- m_uid = reply.data()["uid"].as_int();
- CMStringA m_szUid = reply.data()["uid"].as_mstring();
- setString(DBKEY_ID, m_szUid);
}
- auto *pReq = CreateRequestGQL(FB_API_QUERY_CONTACTS);
- pReq << CHAR_PARAM("query_params", "{\"0\":[\"user\"],\"1\":\"" FB_API_CONTACTS_COUNT "\"}");
- pReq->CalcSig();
-
- JsonReply reply(ExecuteRequest(pReq));
- if (reply.error()) {
-FAIL:
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_FAILED, (HANDLE)m_iStatus, m_iDesiredStatus);
-
- m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus);
- return;
- }
+ if (!RefreshContacts())
+ goto FAIL;
// connect to MQTT server
- if (!MqttConnect())
+ NETLIBOPENCONNECTION nloc = {};
+ nloc.szHost = "mqtt.facebook.com";
+ nloc.wPort = 443;
+ nloc.flags = NLOCF_SSL | NLOCF_V2;
+ m_mqttConn = Netlib_OpenConnection(m_hNetlibUser, &nloc);
+ if (m_mqttConn == nullptr) {
+ debugLogA("connection failed, exiting");
goto FAIL;
+ }
// send initial packet
- MqttOpen();
+ MqttLogin();
__int64 startTime = GetTickCount64();
@@ -200,7 +257,13 @@ void FacebookProto::OnPublishP(FbThriftReader &rdr)
assert(fieldId == 1);
assert(rdr.readInt32(u32));
- debugLogA("Presence from user %lld => %d", userId, u32);
+ auto *pUser = FindUser(userId);
+ if (pUser == nullptr)
+ debugLogA("Skipping presence from unknown user %lld", userId);
+ else {
+ debugLogA("Presence from user %lld => %d", userId, u32);
+ setWord(pUser->hContact, "Status", (u32 != 0) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE);
+ }
assert(rdr.readField(fieldType, fieldId));
assert(fieldType == FB_THRIFT_TYPE_I64);
diff --git a/protocols/Facebook/src/thrift.cpp b/protocols/Facebook/src/thrift.cpp
index c9b520aa90..202eb31569 100644
--- a/protocols/Facebook/src/thrift.cpp
+++ b/protocols/Facebook/src/thrift.cpp
@@ -235,7 +235,7 @@ bool FbThriftReader::readIntV(uint64_t &val)
if (!readByte(b))
return false;
- val |= ((b & 0x7F) << i);
+ val |= (uint64_t(b & 0x7F) << i);
i += 7;
} while ((b & 0x80) != 0);