summaryrefslogtreecommitdiff
path: root/protocols
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-01-11 19:36:19 +0200
committerGeorge Hazan <ghazan@miranda.im>2019-01-11 19:36:19 +0200
commit506986c49afe013ec1d54f8b8eadcf318bfbab4d (patch)
tree5431d993c83f03201f40e0d629545008a052ef81 /protocols
parent90f88c6910ad50f363fcbf9d6d2b22d7b6a7b34e (diff)
ICQ10: ability to invite users to groupchats
Diffstat (limited to 'protocols')
-rw-r--r--protocols/Icq10/res/resources.rc19
-rw-r--r--protocols/Icq10/src/groupchats.cpp286
-rw-r--r--protocols/Icq10/src/proto.cpp84
-rw-r--r--protocols/Icq10/src/proto.h19
-rw-r--r--protocols/Icq10/src/resource.h2
-rw-r--r--protocols/Icq10/src/server.cpp60
6 files changed, 323 insertions, 147 deletions
diff --git a/protocols/Icq10/res/resources.rc b/protocols/Icq10/res/resources.rc
index 7fd47458c3..db9bcc84e6 100644
--- a/protocols/Icq10/res/resources.rc
+++ b/protocols/Icq10/res/resources.rc
@@ -112,6 +112,16 @@ BEGIN
CONTROL "Hide group chats on startup",IDC_HIDECHATS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,285,10
END
+IDD_GROUPCHAT_INVITE DIALOGEX 0, 0, 215, 263
+STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Send group chat invitation"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x1,7,7,201,231,WS_EX_CLIENTEDGE
+ DEFPUSHBUTTON "&Invite",IDOK,104,243,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,158,243,50,14
+END
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -129,6 +139,10 @@ BEGIN
BEGIN
BOTTOMMARGIN, 78
END
+
+ IDD_GROUPCHAT_INVITE, DIALOG
+ BEGIN
+ END
END
#endif // APSTUDIO_INVOKED
@@ -148,6 +162,11 @@ BEGIN
0
END
+IDD_GROUPCHAT_INVITE AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/Icq10/src/groupchats.cpp b/protocols/Icq10/src/groupchats.cpp
new file mode 100644
index 0000000000..4ba45ac6e4
--- /dev/null
+++ b/protocols/Icq10/src/groupchats.cpp
@@ -0,0 +1,286 @@
+// -----------------------------------------------------------------------------
+// ICQ plugin for Miranda NG
+// -----------------------------------------------------------------------------
+// Copyright © 2018-19 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, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+// -----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+void CIcqProto::LoadChatInfo(SESSION_INFO *si)
+{
+ int memberCount = getDword(si->hContact, "MemberCount");
+ for (int i = 0; i < memberCount; i++) {
+ char buf[100];
+ mir_snprintf(buf, "m%d", i);
+ ptrW szSetting(getWStringA(si->hContact, buf));
+ JSONNode *node = json_parse(T2Utf(szSetting));
+ if (node == nullptr)
+ continue;
+
+ CMStringW nick((*node)["nick"].as_mstring());
+ CMStringW role((*node)["role"].as_mstring());
+ CMStringW sn((*node)["sn"].as_mstring());
+
+ GCEVENT gce = { m_szModuleName, si->ptszID, GC_EVENT_JOIN };
+ gce.ptszNick = nick;
+ gce.ptszUID = sn;
+ gce.time = ::time(0);
+ gce.bIsMe = _wtoi(sn) == (int)m_dwUin;
+ gce.ptszStatus = TranslateW(role);
+ Chat_Event(&gce);
+
+ json_delete(node);
+ }
+}
+
+void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo;
+
+ RobustReply root(pReply);
+ if (root.error() != 20000)
+ return;
+
+ int n = 0;
+ char buf[100];
+ const JSONNode &results = root.results();
+ for (auto &it : results["members"]) {
+ mir_snprintf(buf, "m%d", n++);
+
+ CMStringW friendly = it["friendly"].as_mstring();
+ CMStringW role = it["role"].as_mstring();
+ CMStringW sn = it["sn"].as_mstring();
+
+ JSONNode member;
+ member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn);
+ ptrW text(json_write(&member));
+ setWString(si->hContact, buf, text);
+ }
+
+ setDword(si->hContact, "MemberCount", n);
+ setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring()));
+ setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring()));
+
+ LoadChatInfo(si);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Invitation dialog
+
+class CGroupchatInviteDlg : public CProtoDlgBase<CIcqProto>
+{
+ typedef CProtoDlgBase<CIcqProto> CSuper;
+
+ CCtrlClc m_clc;
+ SESSION_INFO *m_si;
+
+ void FilterList(CCtrlClc*)
+ {
+ for (auto &hContact : Contacts()) {
+ char *proto = GetContactProto(hContact);
+ if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact))
+ if (HANDLE hItem = m_clc.FindContact(hContact))
+ m_clc.DeleteItem(hItem);
+ }
+ }
+
+ void ResetListOptions(CCtrlClc*)
+ {
+ m_clc.SetBkBitmap(0, nullptr);
+ m_clc.SetBkColor(GetSysColor(COLOR_WINDOW));
+ m_clc.SetGreyoutFlags(0);
+ m_clc.SetLeftMargin(4);
+ m_clc.SetIndent(10);
+ m_clc.SetHideEmptyGroups(1);
+ m_clc.SetHideOfflineRoot(1);
+ for (int i = 0; i <= FONTID_MAX; i++)
+ m_clc.SetTextColor(i, GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+public:
+ CGroupchatInviteDlg(CIcqProto *ppro, SESSION_INFO *si) :
+ CSuper(ppro, IDD_GROUPCHAT_INVITE),
+ m_si(si),
+ m_clc(this, IDC_CLIST)
+ {
+ m_clc.OnNewContact =
+ m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList);
+ m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions);
+ }
+
+ bool OnInitDialog() override
+ {
+ SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
+ m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0);
+
+ ResetListOptions(&m_clc);
+ FilterList(&m_clc);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ CMStringA szMembers;
+ for (auto &hContact : m_proto->AccContacts()) {
+ if (m_proto->isChatRoom(hContact))
+ continue;
+
+ if (HANDLE hItem = m_clc.FindContact(hContact)) {
+ if (m_clc.GetCheck(hItem)) {
+ if (!szMembers.IsEmpty())
+ szMembers.AppendChar(',');
+ szMembers.AppendFormat("%d", m_proto->getDword(hContact, DB_KEY_UIN));
+ }
+ }
+ }
+
+ auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/mchat/AddChat");
+ pReq << CHAR_PARAM("f", "json") << WCHAR_PARAM("chat_id", m_si->ptszID) << CHAR_PARAM("aimsid", m_proto->m_aimsid) << CHAR_PARAM("r", pReq->m_reqId) << CHAR_PARAM("members", szMembers);
+ m_proto->Push(pReq);
+ return true;
+ }
+};
+
+void CIcqProto::InviteUserToChat(SESSION_INFO *si)
+{
+ CGroupchatInviteDlg dlg(this, si);
+ if (si->pDlg)
+ dlg.SetParent(((CDlgBase*)si->pDlg)->GetHwnd());
+ dlg.DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Group chats
+
+static gc_item sttLogListItems[] =
+{
+ { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM },
+};
+
+int CIcqProto::GroupchatMenuHook(WPARAM, LPARAM lParam)
+{
+ GCMENUITEMS* gcmi = (GCMENUITEMS*)lParam;
+ if (gcmi == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
+ return 0;
+
+ SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule);
+ if (si == nullptr)
+ return 0;
+
+ if (gcmi->Type == MENU_ON_LOG)
+ Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
+
+ return 0;
+}
+
+int CIcqProto::GroupchatEventHook(WPARAM, LPARAM lParam)
+{
+ GCHOOK *gch = (GCHOOK*)lParam;
+ if (gch == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gch->pszModule, m_szModuleName))
+ return 0;
+
+ SESSION_INFO *si = g_chatApi.SM_FindSession(gch->ptszID, gch->pszModule);
+ if (si == nullptr)
+ return 0;
+
+ switch (gch->iType) {
+ case GC_USER_MESSAGE:
+ rtrimw(gch->ptszText);
+ if (!mir_wstrlen(gch->ptszText))
+ break;
+
+ if (m_bOnline) {
+ wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText);
+ Chat_UnescapeTags(wszText);
+ SendMsg(si->hContact, 0, T2Utf(wszText));
+ }
+ break;
+
+ case GC_USER_PRIVMESS:
+ Chat_SendPrivateMessage(gch);
+ break;
+
+ case GC_USER_LOGMENU:
+ Chat_ProcessLogMenu(si, gch->dwData);
+ break;
+ }
+
+ return 0;
+}
+
+void CIcqProto::Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice)
+{
+ switch (iChoice) {
+ case IDM_INVITE:
+ InviteUserToChat(si);
+ break;
+ }
+}
+
+void CIcqProto::Chat_SendPrivateMessage(GCHOOK *gch)
+{
+ MCONTACT hContact;
+ DWORD dwUin = _wtoi(gch->ptszUID);
+ auto *pCache = FindContactByUIN(dwUin);
+ if (pCache == nullptr) {
+ hContact = CreateContact(dwUin, true);
+ setWString(hContact, "Nick", gch->ptszNick);
+ db_set_b(hContact, "CList", "Hidden", 1);
+ db_set_dw(hContact, "Ignore", "Mask1", 0);
+ }
+ else hContact = pCache->m_hContact;
+
+ CallService(MS_MSG_SENDMESSAGE, hContact, 0);
+}
+
+void CIcqProto::ProcessGroupChat(const JSONNode &ev)
+{
+ for (auto &it : ev["mchats"]) {
+ CMStringW wszId(it["sender"].as_mstring());
+ SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName);
+ if (si == nullptr)
+ continue;
+
+ CMStringW method(it["method"].as_mstring());
+ GCEVENT gce = { m_szModuleName, si->ptszID, (method == "add_members") ? GC_EVENT_JOIN : GC_EVENT_PART };
+
+ int iStart = 0;
+ CMStringW members(it["members"].as_mstring());
+ while (true) {
+ CMStringW member = members.Tokenize(L",", iStart);
+ if (member.IsEmpty())
+ break;
+
+ auto *pCache = FindContactByUIN(_wtoi(member));
+ if (pCache == nullptr)
+ continue;
+
+ gce.ptszNick = Clist_GetContactDisplayName(pCache->m_hContact);
+ gce.ptszUID = member;
+ gce.time = ::time(0);
+ gce.bIsMe = _wtoi(member) == (int)m_dwUin;
+ Chat_Event(&gce);
+ }
+ }
+}
diff --git a/protocols/Icq10/src/proto.cpp b/protocols/Icq10/src/proto.cpp
index 978bf847dc..1d849d68c9 100644
--- a/protocols/Icq10/src/proto.cpp
+++ b/protocols/Icq10/src/proto.cpp
@@ -107,90 +107,6 @@ void CIcqProto::OnContactDeleted(MCONTACT hContact)
}
/////////////////////////////////////////////////////////////////////////////////////////
-// Group chats
-
-static gc_item sttLogListItems[] =
-{
- { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM },
-};
-
-int CIcqProto::GroupchatMenuHook(WPARAM, LPARAM lParam)
-{
- GCMENUITEMS* gcmi = (GCMENUITEMS*)lParam;
- if (gcmi == nullptr)
- return 0;
-
- if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
- return 0;
-
- SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule);
- if (si == nullptr)
- return 0;
-
- if (gcmi->Type == MENU_ON_LOG)
- Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
-
- return 0;
-}
-
-int CIcqProto::GroupchatEventHook(WPARAM, LPARAM lParam)
-{
- GCHOOK *gch = (GCHOOK*)lParam;
- if (gch == nullptr)
- return 0;
-
- if (mir_strcmpi(gch->pszModule, m_szModuleName))
- return 0;
-
- SESSION_INFO *si = g_chatApi.SM_FindSession(gch->ptszID, gch->pszModule);
- if (si == nullptr)
- return 0;
-
- switch (gch->iType) {
- case GC_USER_MESSAGE:
- rtrimw(gch->ptszText);
- if (!mir_wstrlen(gch->ptszText))
- break;
-
- if (m_bOnline) {
- wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText);
- Chat_UnescapeTags(wszText);
- SendMsg(si->hContact, 0, T2Utf(wszText));
- }
- break;
-
- case GC_USER_PRIVMESS:
- SendPrivateMessage(gch);
- break;
-
- case GC_USER_LOGMENU:
- // Chat_ProcessLogMenu(gch);
- break;
-
- case GC_USER_NICKLISTMENU:
- break;
- }
-
- return 0;
-}
-
-void CIcqProto::SendPrivateMessage(GCHOOK *gch)
-{
- MCONTACT hContact;
- DWORD dwUin = _wtoi(gch->ptszUID);
- auto *pCache = FindContactByUIN(dwUin);
- if (pCache == nullptr) {
- hContact = CreateContact(dwUin, true);
- setWString(hContact, "Nick", gch->ptszNick);
- db_set_b(hContact, "CList", "Hidden", 1);
- db_set_dw(hContact, "Ignore", "Mask1", 0);
- }
- else hContact = pCache->m_hContact;
-
- CallService(MS_MSG_SENDMESSAGE, hContact, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
void CIcqProto::MarkReadTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD)
{
diff --git a/protocols/Icq10/src/proto.h b/protocols/Icq10/src/proto.h
index a0fe63fa96..96dc779199 100644
--- a/protocols/Icq10/src/proto.h
+++ b/protocols/Icq10/src/proto.h
@@ -76,6 +76,7 @@ struct IcqConn
class CIcqProto : public PROTO<CIcqProto>
{
friend struct CIcqRegistrationDlg;
+ friend class CGroupchatInviteDlg;
bool m_bOnline = false, m_bTerminated = false;
void CheckAvatarChange(MCONTACT hContact, const JSONNode&);
@@ -96,9 +97,6 @@ class CIcqProto : public PROTO<CIcqProto>
void OnLoggedIn(void);
void OnLoggedOut(void);
- void LoadChatInfo(SESSION_INFO *si);
- void SendPrivateMessage(GCHOOK *gch);
-
mir_cs csMarkReadQueue;
LIST<IcqCacheItem> arMarkReadQueue;
static void CALLBACK MarkReadTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD);
@@ -125,6 +123,7 @@ class CIcqProto : public PROTO<CIcqProto>
void ProcessBuddyList(const JSONNode&);
void ProcessDiff(const JSONNode&);
void ProcessEvent(const JSONNode&);
+ void ProcessGroupChat(const JSONNode&);
void ProcessHistData(const JSONNode&);
void ProcessImState(const JSONNode&);
void ProcessMyInfo(const JSONNode&);
@@ -143,6 +142,18 @@ class CIcqProto : public PROTO<CIcqProto>
OBJLIST<IcqOwnMessage> m_arOwnIds;
////////////////////////////////////////////////////////////////////////////////////////
+ // group chats
+
+ int __cdecl GroupchatEventHook(WPARAM, LPARAM);
+ int __cdecl GroupchatMenuHook(WPARAM, LPARAM);
+
+ void Chat_ProcessLogMenu(SESSION_INFO *si, int);
+ void Chat_SendPrivateMessage(GCHOOK *gch);
+
+ void InviteUserToChat(SESSION_INFO *si);
+ void LoadChatInfo(SESSION_INFO *si);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
// http queue
mir_cs m_csHttpQueue;
@@ -187,8 +198,6 @@ class CIcqProto : public PROTO<CIcqProto>
////////////////////////////////////////////////////////////////////////////////////////
// events
- int __cdecl GroupchatEventHook(WPARAM, LPARAM);
- int __cdecl GroupchatMenuHook(WPARAM, LPARAM);
int __cdecl OnDbEventRead(WPARAM, LPARAM);
int __cdecl OnOptionsInit(WPARAM, LPARAM);
diff --git a/protocols/Icq10/src/resource.h b/protocols/Icq10/src/resource.h
index 964e8a2095..beeac3d899 100644
--- a/protocols/Icq10/src/resource.h
+++ b/protocols/Icq10/src/resource.h
@@ -4,6 +4,7 @@
//
#define IDD_OPTIONS_FULL 101
#define IDD_OPTIONS_ACCMGR 102
+#define IDD_GROUPCHAT_INVITE 103
#define IDD_REGISTER 105
#define IDC_PASSWORD 1001
#define IDC_UIN 1002
@@ -12,6 +13,7 @@
#define IDC_HIDECHATS 1004
#define IDC_REGISTER 1005
#define IDC_PHONE 1006
+#define IDC_CLIST 1007
#define IDC_SENDSMS 1008
#define IDC_CODE 1009
diff --git a/protocols/Icq10/src/server.cpp b/protocols/Icq10/src/server.cpp
index 550f0d6a0e..65a3edda89 100644
--- a/protocols/Icq10/src/server.cpp
+++ b/protocols/Icq10/src/server.cpp
@@ -95,33 +95,6 @@ void CIcqProto::ConnectionFailed(int iReason)
ShutdownSession();
}
-void CIcqProto::LoadChatInfo(SESSION_INFO *si)
-{
- int memberCount = getDword(si->hContact, "MemberCount");
- for (int i = 0; i < memberCount; i++) {
- char buf[100];
- mir_snprintf(buf, "m%d", i);
- ptrW szSetting(getWStringA(si->hContact, buf));
- JSONNode *node = json_parse(T2Utf(szSetting));
- if (node == nullptr)
- continue;
-
- CMStringW nick((*node)["nick"].as_mstring());
- CMStringW role((*node)["role"].as_mstring());
- CMStringW sn((*node)["sn"].as_mstring());
-
- GCEVENT gce = { m_szModuleName, si->ptszID, GC_EVENT_JOIN };
- gce.ptszNick = nick;
- gce.ptszUID = sn;
- gce.time = ::time(0);
- gce.bIsMe = _wtoi(sn) == (int)m_dwUin;
- gce.ptszStatus = TranslateW(role);
- Chat_Event(&gce);
-
- json_delete(node);
- }
-}
-
void CIcqProto::OnLoggedIn()
{
debugLogA("CIcqProto::OnLoggedIn");
@@ -509,37 +482,6 @@ void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
StartSession();
}
-void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
-{
- SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo;
-
- RobustReply root(pReply);
- if (root.error() != 20000)
- return;
-
- int n = 0;
- char buf[100];
- const JSONNode &results = root.results();
- for (auto &it : results["members"]) {
- mir_snprintf(buf, "m%d", n++);
-
- CMStringW friendly = it["friendly"].as_mstring();
- CMStringW role = it["role"].as_mstring();
- CMStringW sn = it["sn"].as_mstring();
-
- JSONNode member;
- member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn);
- ptrW text(json_write(&member));
- setWString(si->hContact, buf, text);
- }
-
- setDword(si->hContact, "MemberCount", n);
- setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring()));
- setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring()));
-
- LoadChatInfo(si);
-}
-
void CIcqProto::OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
{
MCONTACT hContact = (MCONTACT)pReq->pUserInfo;
@@ -755,6 +697,8 @@ void CIcqProto::ProcessEvent(const JSONNode &ev)
ProcessHistData(pData);
else if (szType == L"imState")
ProcessImState(pData);
+ else if (szType == L"mchat")
+ ProcessGroupChat(pData);
else if (szType == L"myInfo")
ProcessMyInfo(pData);
else if (szType == L"presence")