summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/ICQ-WIM/src/poll.cpp18
-rw-r--r--protocols/ICQ-WIM/src/proto.cpp36
-rw-r--r--protocols/ICQ-WIM/src/proto.h205
-rw-r--r--protocols/ICQ-WIM/src/utils.cpp35
4 files changed, 179 insertions, 115 deletions
diff --git a/protocols/ICQ-WIM/src/poll.cpp b/protocols/ICQ-WIM/src/poll.cpp
index f48be9b2f9..fbcfd178c0 100644
--- a/protocols/ICQ-WIM/src/poll.cpp
+++ b/protocols/ICQ-WIM/src/poll.cpp
@@ -332,22 +332,8 @@ void CIcqProto::ProcessPresence(const JSONNode &ev)
return;
int iNewStatus = StatusFromPresence(ev, pUser->m_hContact);
- if (iNewStatus == -1)
- iNewStatus = ID_STATUS_OFFLINE;
-
- // major crutch dedicated to the official client behaviour to go offline
- // when its window gets closed. we change the status of a contact to the
- // first chosen one from options and initialize a timer
- if (iNewStatus == ID_STATUS_OFFLINE) {
- if (m_iTimeDiff1) {
- iNewStatus = m_iStatus1;
- pUser->m_timer1 = time(0);
- }
- }
- // if a client returns back online, we clear timers not to play with statuses anymore
- else pUser->m_timer1 = pUser->m_timer2 = 0;
-
- setWord(pUser->m_hContact, "Status", iNewStatus);
+ if (iNewStatus != -1)
+ ProcessStatus(pUser, iNewStatus);
Json2string(pUser->m_hContact, ev, "friendly", "Nick", true);
CheckAvatarChange(pUser->m_hContact, ev);
diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp
index 09496b46c4..f6a605d51e 100644
--- a/protocols/ICQ-WIM/src/proto.cpp
+++ b/protocols/ICQ-WIM/src/proto.cpp
@@ -45,6 +45,7 @@ CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) :
m_arOwnIds(1, PtrKeySortT),
m_arCache(20, &CompareCache),
m_arGroups(10, NumericKeySortT),
+ m_arLastSeenQueue(10, NumericKeySortT),
m_arMarkReadQueue(10, NumericKeySortT),
m_evRequestsQueue(CreateEvent(nullptr, FALSE, FALSE, nullptr)),
m_szOwnId(this, DB_KEY_ID),
@@ -304,6 +305,41 @@ INT_PTR CIcqProto::GotoInbox(WPARAM, LPARAM)
/////////////////////////////////////////////////////////////////////////////////////////
+void CIcqProto::OnLastSeen(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *)
+{
+ RobustReply root(pReply);
+ if (root.error() != 20000)
+ return;
+
+ const JSONNode &results = root.results();
+ for (auto &it : results["entries"]) {
+ if (auto *pUser = FindUser(it["sn"].as_mstring())) {
+ int iLastSeen = it["lastseen"].as_int();
+ if (iLastSeen != 0) {
+ setDword(pUser->m_hContact, DB_KEY_LASTSEEN, iLastSeen);
+ ProcessStatus(pUser, ID_STATUS_OFFLINE);
+ }
+ else ProcessStatus(pUser, ID_STATUS_ONLINE);
+ }
+ }
+}
+
+void CIcqProto::SendLastSeen()
+{
+ mir_cslock lck(m_csLastSeenQueue);
+
+ auto *pReq = new AsyncRapiRequest(this, "getUserLastseen", &CIcqProto::OnLastSeen);
+ JSONNode ids(JSON_ARRAY); ids.set_name("ids");
+ for (auto &it: m_arLastSeenQueue)
+ ids << WCHAR_PARAM("", GetUserId(it->m_hContact));
+ pReq->params.push_back(ids);
+ Push(pReq);
+
+ m_arLastSeenQueue.destroy();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CIcqProto::SendMarkRead()
{
mir_cslock lck(m_csMarkReadQueue);
diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h
index 018a4e1399..a10f27c065 100644
--- a/protocols/ICQ-WIM/src/proto.h
+++ b/protocols/ICQ-WIM/src/proto.h
@@ -170,7 +170,7 @@ class CIcqProto : public PROTO<CIcqProto>
friend class CIcqProto;
CIcqProto &m_proto;
- CTimer m_heartBeat, m_markRead;
+ CTimer m_heartBeat, m_markRead, m_lastSeen;
void OnHeartBeat(CTimer *) {
m_proto.CheckStatus();
@@ -181,12 +181,19 @@ class CIcqProto : public PROTO<CIcqProto>
pTimer->Stop();
}
+ void OnLastSeen(CTimer *pTimer) {
+ m_proto.SendLastSeen();
+ pTimer->Stop();
+ }
+
CIcqProtoImpl(CIcqProto &pro) :
m_proto(pro),
m_markRead(Miranda_GetSystemWindow(), UINT_PTR(this)),
- m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 1)
+ m_lastSeen(Miranda_GetSystemWindow(), UINT_PTR(this) + 1),
+ m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 2)
{
m_markRead.OnEvent = Callback(this, &CIcqProtoImpl::OnMarkRead);
+ m_lastSeen.OnEvent = Callback(this, &CIcqProtoImpl::OnLastSeen);
m_heartBeat.OnEvent = Callback(this, &CIcqProtoImpl::OnHeartBeat);
}
} m_impl;
@@ -200,106 +207,112 @@ class CIcqProto : public PROTO<CIcqProto>
friend AsyncHttpRequest* operator <<(AsyncHttpRequest*, const AIMSID&);
- bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462;
- int m_iTimeShift;
-
- MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove);
- void CheckPassword(void);
- void ConnectionFailed(int iReason, int iErrorCode = 0);
- void EmailNotification(const wchar_t *pwszText);
- void GetPermitDeny();
- wchar_t* GetUIN(MCONTACT hContact);
- void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup);
- bool RetrievePassword();
- void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead);
- void RetrieveUserInfo(MCONTACT hContact);
- void SendMrimLogin(NETLIBHTTPREQUEST *pReply);
- void SetServerStatus(int iNewStatus);
- void ShutdownSession(void);
- void StartSession(void);
-
- void CheckAvatarChange(MCONTACT hContact, const JSONNode&);
- IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile);
- void CheckLastId(MCONTACT hContact, const JSONNode&);
- void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
- void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
- MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false);
- void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime);
- int StatusFromPresence(const JSONNode &presence, MCONTACT hContact);
-
- void OnLoggedIn(void);
- void OnLoggedOut(void);
-
- mir_cs m_csMarkReadQueue;
+ bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462;
+ int m_iTimeShift;
+
+ MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove);
+ void CheckPassword(void);
+ void ConnectionFailed(int iReason, int iErrorCode = 0);
+ void EmailNotification(const wchar_t *pwszText);
+ void GetPermitDeny();
+ wchar_t* GetUIN(MCONTACT hContact);
+ void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup);
+ bool RetrievePassword();
+ void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead);
+ void RetrieveUserInfo(MCONTACT hContact);
+ void SendMrimLogin(NETLIBHTTPREQUEST *pReply);
+ void SetServerStatus(int iNewStatus);
+ void ShutdownSession(void);
+ void StartSession(void);
+
+ void CheckAvatarChange(MCONTACT hContact, const JSONNode&);
+ IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile);
+ void CheckLastId(MCONTACT hContact, const JSONNode&);
+ void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
+ void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial);
+ MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false);
+ void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime);
+ int StatusFromPresence(const JSONNode &presence, MCONTACT hContact);
+ void ProcessStatus(IcqUser *pUser, int iStatus);
+
+ void OnLoggedIn(void);
+ void OnLoggedOut(void);
+
+ mir_cs m_csMarkReadQueue;
LIST<IcqUser> m_arMarkReadQueue;
- void SendMarkRead();
-
- __int64 getId(MCONTACT hContact, const char *szSetting);
- void setId(MCONTACT hContact, const char *szSetting, __int64 iValue);
-
- void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
- void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
-
- void ProcessBuddyList(const JSONNode &pRoot);
- void ProcessDiff(const JSONNode &pRoot);
- void ProcessEvent(const JSONNode &pRoot);
- void ProcessGroupChat(const JSONNode &pRoot);
- void ProcessHistData(const JSONNode &pRoot);
- void ProcessImState(const JSONNode &pRoot);
- void ProcessMyInfo(const JSONNode &pRoot);
- void ProcessNotification(const JSONNode &pRoot);
- void ProcessPermissions(const JSONNode &pRoot);
- void ProcessPresence(const JSONNode &pRoot);
- void ProcessSessionEnd(const JSONNode &pRoot);
- void ProcessTyping(const JSONNode &pRoot);
-
- IcqConn m_ConnPool[CONN_LAST];
- CMStringA m_szPassword;
- CMStringA m_szSessionKey;
- CMStringA m_szAToken;
- CMStringA m_szRToken;
- CMStringA m_fetchBaseURL;
- CMStringA m_aimsid;
- CMStringA m_szMraCookie;
- LONG m_msgId = 1;
- int m_iRClientId;
- HGENMENU m_hUploadGroups;
-
- mir_cs m_csOwnIds;
+ void SendMarkRead();
+
+ mir_cs m_csLastSeenQueue;
+ LIST<IcqUser> m_arLastSeenQueue;
+ void SendLastSeen();
+
+ __int64 getId(MCONTACT hContact, const char *szSetting);
+ void setId(MCONTACT hContact, const char *szSetting, __int64 iValue);
+
+ void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnLastSeen(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+ void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq);
+
+ void ProcessBuddyList(const JSONNode &pRoot);
+ void ProcessDiff(const JSONNode &pRoot);
+ void ProcessEvent(const JSONNode &pRoot);
+ void ProcessGroupChat(const JSONNode &pRoot);
+ void ProcessHistData(const JSONNode &pRoot);
+ void ProcessImState(const JSONNode &pRoot);
+ void ProcessMyInfo(const JSONNode &pRoot);
+ void ProcessNotification(const JSONNode &pRoot);
+ void ProcessPermissions(const JSONNode &pRoot);
+ void ProcessPresence(const JSONNode &pRoot);
+ void ProcessSessionEnd(const JSONNode &pRoot);
+ void ProcessTyping(const JSONNode &pRoot);
+
+ IcqConn m_ConnPool[CONN_LAST];
+ CMStringA m_szPassword;
+ CMStringA m_szSessionKey;
+ CMStringA m_szAToken;
+ CMStringA m_szRToken;
+ CMStringA m_fetchBaseURL;
+ CMStringA m_aimsid;
+ CMStringA m_szMraCookie;
+ LONG m_msgId = 1;
+ int m_iRClientId;
+ HGENMENU m_hUploadGroups;
+
+ mir_cs m_csOwnIds;
OBJLIST<IcqOwnMessage> m_arOwnIds;
OBJLIST<IcqGroup> m_arGroups;
- int m_unreadEmails = -1;
- CMStringA m_szMailBox;
+ int m_unreadEmails = -1;
+ CMStringA m_szMailBox;
- bool m_bIgnoreListEmpty = true;
- bool m_bRememberPwd; // store password in a database
- bool m_bDlgActive;
+ bool m_bIgnoreListEmpty = true;
+ bool m_bRememberPwd; // store password in a database
+ bool m_bDlgActive;
////////////////////////////////////////////////////////////////////////////////////////
// group chats
diff --git a/protocols/ICQ-WIM/src/utils.cpp b/protocols/ICQ-WIM/src/utils.cpp
index 4c5d494115..5adde04e81 100644
--- a/protocols/ICQ-WIM/src/utils.cpp
+++ b/protocols/ICQ-WIM/src/utils.cpp
@@ -281,6 +281,8 @@ bool IsValidType(const JSONNode &n)
return type == "icq" || type == "aim" || type == "interop" || type == "";
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact)
{
CMStringW wszStatus = presence["state"].as_mstring();
@@ -298,17 +300,44 @@ int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact)
else if (wszStatus == L"dnd")
iStatus = ID_STATUS_DND;
else
- iStatus = -1;
+ return -1;
int iLastSeen = presence["lastseen"].as_int();
if (iLastSeen != 0)
setDword(hContact, DB_KEY_LASTSEEN, iLastSeen);
- else if (getDword(hContact, DB_KEY_ONLINETS))
- iStatus = ID_STATUS_ONLINE;
+ else {
+ if (getDword(hContact, DB_KEY_ONLINETS))
+ iStatus = ID_STATUS_ONLINE;
+ else {
+ if (auto *pUser = FindUser(GetUserId(hContact))) {
+ mir_cslock lck(m_csLastSeenQueue);
+ m_arLastSeenQueue.insert(pUser);
+ m_impl.m_lastSeen.Start(500);
+ }
+ return -1;
+ }
+ }
return iStatus;
}
+void CIcqProto::ProcessStatus(IcqUser *pUser, int iStatus)
+{
+ // major crutch dedicated to the official client behaviour to go offline
+ // when its window gets closed. we change the status of a contact to the
+ // first chosen one from options and initialize a timer
+ if (iStatus == ID_STATUS_OFFLINE) {
+ if (m_iTimeDiff1) {
+ iStatus = m_iStatus1;
+ pUser->m_timer1 = time(0);
+ }
+ }
+ // if a client returns back online, we clear timers not to play with statuses anymore
+ else pUser->m_timer1 = pUser->m_timer2 = 0;
+
+ setWord(pUser->m_hContact, "Status", iStatus);
+}
+
/////////////////////////////////////////////////////////////////////////////////////////
__int64 CIcqProto::getId(MCONTACT hContact, const char *szSetting)