summaryrefslogtreecommitdiff
path: root/protocols/Icq10/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Icq10/src/server.cpp')
-rw-r--r--protocols/Icq10/src/server.cpp211
1 files changed, 154 insertions, 57 deletions
diff --git a/protocols/Icq10/src/server.cpp b/protocols/Icq10/src/server.cpp
index 841706258a..b95ab64b14 100644
--- a/protocols/Icq10/src/server.cpp
+++ b/protocols/Icq10/src/server.cpp
@@ -65,8 +65,10 @@ void CIcqProto::ShutdownSession()
// shutdown all resources
if (m_hWorkerThread)
SetEvent(m_evRequestsQueue);
- if (m_hAPIConnection)
- Netlib_Shutdown(m_hAPIConnection);
+
+ for (auto &it : m_ConnPool)
+ if (it)
+ Netlib_Shutdown(it);
OnLoggedOut();
}
@@ -74,53 +76,11 @@ void CIcqProto::ShutdownSession()
/////////////////////////////////////////////////////////////////////////////////////////
#define CAPS "094613504c7f11d18222444553540000,094613514c7f11d18222444553540000,094613534c7f11d18222444553540000,094613544c7f11d18222444553540000,094613594c7f11d18222444553540000,0946135b4c7f11d18222444553540000,0946135a4c7f11d18222444553540000"
-#define EVENTS "myInfo,presence,buddylist,typing,dataIM,userAddedToBuddyList,service,webrtcMsg,mchat,hist,hiddenChat,diff,permitDeny,imState,notification,apps"
+#define EVENTS "myInfo,presence,buddylist,typing,dataIM,userAddedToBuddyList,webrtcMsg,mchat,hist,hiddenChat,diff,permitDeny,imState,notification,apps"
#define FIELDS "aimId,buddyIcon,bigBuddyIcon,iconId,bigIconId,largeIconId,displayId,friendly,offlineMsg,state,statusMsg,userType,phoneNumber,cellNumber,smsNumber,workNumber,otherNumber,capabilities,ssl,abPhoneNumber,moodIcon,lastName,abPhones,abContactName,lastseen,mute,livechat,official"
-void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+void CIcqProto::StartSession()
{
- if (pReply->resultCode != 200 || pReply->pData == nullptr) {
- ConnectionFailed(LOGINERR_WRONGPROTOCOL);
- return;
- }
-
- JSONROOT root(pReply->pData);
- if (!root) {
- ConnectionFailed(LOGINERR_WRONGPROTOCOL);
- return;
- }
-
- BYTE hashOut[MIR_SHA256_HASH_SIZE];
- unsigned int len;
-
- JSONNode response = (*root)["response"];
- switch (response["statusCode"].as_int()) {
- case 200:
- {
- JSONNode data = response["data"];
- m_szAToken = data["token"]["a"].as_mstring();
- m_szAToken = ptrA(mir_urlDecode(m_szAToken));
- CMStringA m_szSessionSecret = data["sessionSecret"].as_mstring();
-
- ptrA szPassword(getStringA("Password"));
- HMAC(EVP_sha256(), szPassword.get(), (int)mir_strlen(szPassword), (BYTE*)m_szSessionSecret.c_str(), m_szSessionSecret.GetLength(), hashOut, &len);
- m_szSessionKey = ptrA(mir_base64_encode(hashOut, sizeof(hashOut)));
-
- CMStringA szUin = data["loginId"].as_mstring();
- if (szUin)
- setDword("UIN", atoi(szUin));
- }
- break;
-
- case 440:
- ConnectionFailed(LOGINERR_WRONGPASSWORD);
- return;
-
- default:
- ConnectionFailed(LOGINERR_WRONGPROTOCOL);
- return;
- }
-
ptrA szDeviceId(getStringA("DeviceId"));
if (szDeviceId == nullptr) {
UUID deviceId;
@@ -135,7 +95,7 @@ void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
int ts = time(0);
CMStringA nonce(FORMAT, "%d-2", ts);
- auto *pReq = new AsyncHttpRequest(REQUEST_POST, "https://api.icq.net/aim/startSession", &CIcqProto::OnStartSession);
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_POST, "https://api.icq.net/aim/startSession", &CIcqProto::OnStartSession);
RPC_CSTR szId;
UuidToStringA(&pReq->m_reqId, &szId);
@@ -148,6 +108,8 @@ void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
<< INT_PARAM("rawMsg", 0) << INT_PARAM("sessionTimeout", 7776000) << INT_PARAM("ts", ts) << CHAR_PARAM("view", "online");
CMStringA hashData(FORMAT, "POST&%s&%s", ptrA(mir_urlEncode(pReq->m_szUrl)), ptrA(mir_urlEncode(pReq->m_szParam)));
+ unsigned int len;
+ BYTE hashOut[MIR_SHA256_HASH_SIZE];
HMAC(EVP_sha256(), m_szSessionKey, m_szSessionKey.GetLength(), (BYTE*)hashData.c_str(), hashData.GetLength(), hashOut, &len);
pReq << CHAR_PARAM("sig_sha256", ptrA(mir_base64_encode(hashOut, sizeof(hashOut))));
@@ -155,30 +117,165 @@ void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
RpcStringFreeA(&szId);
}
-void CIcqProto::OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
{
- if (pReply->resultCode != 200 || pReply->pData == nullptr) {
- ConnectionFailed(LOGINERR_WRONGPROTOCOL);
+ JsonReply root(pReply);
+ switch (root.error()) {
+ case 200:
+ break;
+
+ case 440:
+ ConnectionFailed(LOGINERR_WRONGPASSWORD);
return;
- }
- JSONROOT root(pReply->pData);
- if (!root) {
+ default:
ConnectionFailed(LOGINERR_WRONGPROTOCOL);
return;
}
- JSONNode response = (*root)["response"];
- switch (response["statusCode"].as_int()) {
+ JSONNode &data = root.data();
+ m_szAToken = data["token"]["a"].as_mstring();
+ m_szAToken = ptrA(mir_urlDecode(m_szAToken));
+ setString("AToken", m_szAToken);
+
+ ptrA szPassword(getStringA("Password"));
+ CMStringA m_szSessionSecret = data["sessionSecret"].as_mstring();
+
+ unsigned int len;
+ BYTE hashOut[MIR_SHA256_HASH_SIZE];
+ HMAC(EVP_sha256(), szPassword.get(), (int)mir_strlen(szPassword), (BYTE*)m_szSessionSecret.c_str(), m_szSessionSecret.GetLength(), hashOut, &len);
+ m_szSessionKey = ptrA(mir_base64_encode(hashOut, sizeof(hashOut)));
+ setString("SessionKey", m_szSessionKey);
+
+ CMStringA szUin = data["loginId"].as_mstring();
+ if (szUin)
+ setDword("UIN", atoi(szUin));
+
+ StartSession();
+}
+
+void CIcqProto::OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ JsonReply root(pReply);
+ switch (root.error()) {
case 200:
- OnLoggedIn();
break;
case 401:
ConnectionFailed(LOGINERR_WRONGPASSWORD);
- break;
+ return;
default:
ConnectionFailed(LOGINERR_WRONGPROTOCOL);
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_fetchBaseURL = data["fetchBaseURL"].as_mstring();
+ m_aimsid = data["aimsid"].as_mstring();
+
+ OnLoggedIn();
+
+ for (auto &it : data["events"])
+ ProcessEvent(it);
+
+ m_fetchBaseURL.Append("&first=1");
+ Push(new AsyncHttpRequest(CONN_FETCH, REQUEST_GET, m_fetchBaseURL, &CIcqProto::OnFetchEvents));
+}
+
+void CIcqProto::OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ MCONTACT hContact = (MCONTACT)pReq->pUserInfo;
+
+ const wchar_t *pwszExtension;
+ PROTO_AVATAR_INFORMATION ai;
+ ai.hContact = hContact;
+ ai.format = ProtoGetBufferFormat(pReply->pData, &pwszExtension);
+ setByte(hContact, "AvatarType", ai.format);
+ GetAvatarFileName(hContact, ai.filename, _countof(ai.filename));
+
+ FILE *out = _wfopen(ai.filename, L"wb");
+ if (out != nullptr) {
+ fwrite(pReply->pData, pReply->dataLength, 1, out);
+ fclose(out);
+
+ ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, HANDLE(&ai), 0);
+ debugLogW(L"Broadcast new avatar: %s", ai.filename);
}
+ else ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, HANDLE(&ai), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::ProcessEvent(const JSONNode &ev)
+{
+ CMStringW szType = ev["type"].as_mstring();
+ if (szType == L"buddylist") {
+ for (auto &it : ev["eventData"]["groups"]) {
+ CMStringW szGroup = it["name"].as_mstring();
+ Clist_GroupCreate(0, szGroup);
+
+ for (auto &buddy : it["buddies"]) {
+ DWORD dwUin = _wtol(buddy["aimId"].as_mstring());
+ MCONTACT hContact = FindContactByUIN(dwUin);
+ if (hContact == 0) {
+ hContact = db_add_contact();
+ Proto_AddToContact(hContact, m_szModuleName);
+ setDword(hContact, "UIN", dwUin);
+ {
+ mir_cslock l(m_csCache);
+ m_arCache.insert(new IcqCacheItem(dwUin, hContact));
+ }
+ }
+
+ CMStringW wszNick(buddy["friendly"].as_mstring());
+ if (!wszNick.IsEmpty())
+ setWString(hContact, "Nick", wszNick);
+
+ setDword(hContact, "Status", StatusFromString(buddy["state"].as_mstring()));
+
+ int lastLogin = buddy["lastseen"].as_int();
+ if (lastLogin)
+ setDword(hContact, "LoginTS", lastLogin);
+
+ CMStringW wszStatus(buddy["statusMsg"].as_mstring());
+ if (wszStatus.IsEmpty())
+ db_unset(hContact, "CList", "StatusMsg");
+ else
+ db_set_ws(hContact, "CList", "StatusMsg", wszStatus);
+
+ CMStringW wszIconId(buddy["iconId"].as_mstring());
+ CMStringW oldIconID(getMStringW(hContact, "IconId"));
+ if (wszIconId != oldIconID) {
+ setWString(hContact, "IconId", wszIconId);
+
+ CMStringA szUrl(buddy["buddyIcon"].as_mstring());
+ auto *p = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, szUrl, &CIcqProto::OnReceiveAvatar);
+ p->pUserInfo = (void*)hContact;
+ Push(p);
+ }
+
+ db_set_ws(hContact, "CList", "Group", szGroup);
+ }
+ }
+ }
+}
+
+void CIcqProto::OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
+{
+ JsonReply root(pReply);
+ if (root.error() != 200) {
+ ShutdownSession();
+ return;
+ }
+
+ JSONNode &data = root.data();
+ m_fetchBaseURL = data["fetchBaseURL"].as_mstring();
+
+ for (auto &it : data["events"])
+ ProcessEvent(it);
+
+ Push(new AsyncHttpRequest(CONN_FETCH, REQUEST_GET, m_fetchBaseURL, &CIcqProto::OnFetchEvents));
}