/*
Copyright (c) 2015 Miranda NG project (http://miranda-ng.org)
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 version 2
of the License.
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 .
*/
#include "common.h"
static LPCTSTR sttStatuses[] = { LPGENT("User"), LPGENT("Admin") };
void CSkypeProto::InitGroupChatModule()
{
GCREGISTER gcr = { sizeof(gcr) };
gcr.iMaxText = 0;
gcr.ptszDispName = m_tszUserName;
gcr.pszModule = m_szModuleName;
CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr);
HookProtoEvent(ME_GC_EVENT, &CSkypeProto::OnGroupChatEventHook);
HookProtoEvent(ME_GC_BUILDMENU, &CSkypeProto::OnGroupChatMenuHook);
CreateProtoService(PS_JOINCHAT, &CSkypeProto::OnJoinChatRoom);
CreateProtoService(PS_LEAVECHAT, &CSkypeProto::OnLeaveChatRoom);
}
void CSkypeProto::CloseAllChatChatSessions()
{
GC_INFO gci = { 0 };
gci.Flags = GCF_BYINDEX | GCF_ID | GCF_DATA;
gci.pszModule = m_szModuleName;
int count = CallServiceSync(MS_GC_GETSESSIONCOUNT, 0, (LPARAM)m_szModuleName);
for (int i = 0; i < count; i++)
{
gci.iItem = i;
if (!CallServiceSync(MS_GC_GETINFO, 0, (LPARAM)&gci))
{
GCDEST gcd = { m_szModuleName, gci.pszID, GC_EVENT_CONTROL };
GCEVENT gce = { sizeof(gce), &gcd };
CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce);
CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce);
}
}
}
MCONTACT CSkypeProto::FindChatRoom(const char *chatname)
{
MCONTACT hContact = NULL;
for (hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName))
{
if (!isChatRoom(hContact))
continue;
ptrA cChatname(getStringA(hContact, "ChatRoomID"));
if (!mir_strcmpi(chatname, cChatname))
break;
}
return hContact;
}
int CSkypeProto::OnGroupChatEventHook(WPARAM, LPARAM lParam)
{
GCHOOK *gch = (GCHOOK*)lParam;
if (!gch)
{
return 1;
}
else if (strcmp(gch->pDest->pszModule, m_szModuleName) != 0)
{
return 0;
}
ptrA chat_id(mir_t2a(gch->pDest->ptszID));
switch (gch->pDest->iType)
{
case GC_USER_MESSAGE:
{
ptrA msg(mir_t2a(gch->ptszText));
if (IsOnline()) {
debugLogA(" > Chat - Outgoing message");
SendRequest(new SendChatMessageRequest(RegToken, chat_id, time(NULL), msg, Server));
}
break;
}
case GC_USER_PRIVMESS:
{
MCONTACT hContact = FindContact(_T2A(gch->ptszUID));
if (hContact == NULL)
{
hContact = AddContact(_T2A(gch->ptszUID), true);
setWord(hContact, "Status", ID_STATUS_ONLINE);
db_set_b(hContact, "CList", "Hidden", 1);
setTString(hContact, "Nick", gch->ptszUID);
db_set_dw(hContact, "Ignore", "Mask1", 0);
}
CallService(MS_MSG_SENDMESSAGET, hContact, 0);
break;
}
case GC_USER_LOGMENU:
{
switch(gch->dwData)
{
case 10: {
MCONTACT hContact = (MCONTACT)DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_GC_INVITE), NULL, InviteDlgProc, (LPARAM)this);
if (hContact != NULL)
{
ptrA username(db_get_sa(hContact, m_szModuleName, SKYPE_SETTINGS_ID));
SendRequest(new InviteUserToChatRequest(RegToken, chat_id, username, "User", Server));
}
break;
}
case 20:
//chat_leave(id);
break;
case 30:
break;
}
break;
}
case GC_USER_NICKLISTMENU:
{
ptrA user_id;
if (gch->dwData == 10 || gch->dwData == 30 || gch->dwData == 40)
{
user_id = mir_t2a_cp(gch->ptszUID, CP_UTF8);
}
switch (gch->dwData)
{
case 10:
SendRequest(new KickUserRequest(RegToken, chat_id, user_id, Server));
break;
case 30:
SendRequest(new InviteUserToChatRequest(RegToken, chat_id, user_id, "Admin", Server));
break;
case 40:
SendRequest(new InviteUserToChatRequest(RegToken, chat_id, user_id, "User", Server));
break;
}
break;
}
}
return 0;
}
void CSkypeProto::StartChatRoom(const TCHAR *tid, const TCHAR *tname)
{
// Create the group chat session
GCSESSION gcw = { sizeof(gcw) };
gcw.iType = GCW_PRIVMESS;
gcw.ptszID = tid;
gcw.pszModule = m_szModuleName;
gcw.ptszName = tname;
CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
// Send setting events
GCDEST gcd = { m_szModuleName, tid, GC_EVENT_ADDGROUP };
GCEVENT gce = { sizeof(gce), &gcd };
// Create a user statuses
gce.ptszStatus = TranslateT("Admin");
CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast(&gce));
gce.ptszStatus = TranslateT("User");
CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast(&gce));
// Finish initialization
gcd.iType = GC_EVENT_CONTROL;
gce.time = time(NULL);
gce.pDest = &gcd;
bool hideChats = getBool("HideChats", 1);
// Add self contact
//AddChatContact(tid, facy.self_.user_id.c_str(), facy.self_.real_name.c_str());
CallServiceSync(MS_GC_EVENT, (hideChats ? WINDOW_HIDDEN : SESSION_INITDONE), reinterpret_cast(&gce));
CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, reinterpret_cast(&gce));
SendRequest(new GetChatInfoRequest(RegToken, ptrA(mir_t2a(tid)), Server), &CSkypeProto::OnGetChatInfo);
}
int CSkypeProto::OnGroupChatMenuHook(WPARAM, LPARAM lParam)
{
GCMENUITEMS *gcmi = (GCMENUITEMS*)lParam;
if (stricmp(gcmi->pszModule, m_szModuleName)) return 0;
if (gcmi->Type == MENU_ON_LOG)
{
static const struct gc_item Items[] =
{
{ LPGENT("&Invite user..."), 10, MENU_ITEM, FALSE },
{ LPGENT("&Leave chat session"), 20, MENU_ITEM, FALSE },
{ LPGENT("&Change topic"), 30, MENU_ITEM, FALSE }
};
gcmi->nItems = SIZEOF(Items);
gcmi->Item = (gc_item*)Items;
}
else if (gcmi->Type == MENU_ON_NICKLIST)
{
if (IsMe(_T2A(gcmi->pszUID)))
{
gcmi->nItems = 0;
gcmi->Item = NULL;
}
else
{
static const struct gc_item Items[] =
{
{ LPGENT("Kick &user"), 10, MENU_ITEM },
{ NULL, 0, MENU_ITEM },
{ LPGENT("Set &role"), 20, MENU_NEWPOPUP },
{ LPGENT("&Admin"), 30, MENU_POPUPITEM },
{ LPGENT("&User"), 40, MENU_POPUPITEM }
};
gcmi->nItems = SIZEOF(Items);
gcmi->Item = (gc_item*)Items;
}
}
return 0;
}
INT_PTR CSkypeProto::OnJoinChatRoom(WPARAM hContact, LPARAM)
{
return 0;
}
INT_PTR CSkypeProto::OnLeaveChatRoom(WPARAM hContact, LPARAM)
{
if (hContact)
{
ptrT idT(getTStringA(hContact, "ChatRoomID"));
GCDEST gcd = { m_szModuleName, NULL, GC_EVENT_CONTROL };
gcd.ptszID = idT;
GCEVENT gce = { sizeof(gce), &gcd };
gce.time = ::time(NULL);
CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, reinterpret_cast(&gce));
CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, reinterpret_cast(&gce));
}
return 0;
}
/* CHAT EVENT */
void CSkypeProto::OnChatEvent(JSONNODE *node)
{
ptrA clientMsgId(mir_t2a(ptrT(json_as_string(json_get(node, "clientmessageid")))));
ptrA skypeEditedId(mir_t2a(ptrT(json_as_string(json_get(node, "skypeeditedid")))));
ptrA from(mir_t2a(ptrT(json_as_string(json_get(node, "from")))));
time_t timestamp = IsoToUnixTime(ptrT(json_as_string(json_get(node, "composetime"))));
ptrA content(mir_t2a(ptrT(json_as_string(json_get(node, "content")))));
//int emoteOffset = json_as_int(json_get(node, "skypeemoteoffset"));
ptrA conversationLink(mir_t2a(ptrT(json_as_string(json_get(node, "conversationLink")))));
ptrA chatname(ChatUrlToName(conversationLink));
ptrT topic(json_as_string(json_get(node, "threadtopic")));
//StartChatRoom(_A2T(chatname), topic);
ptrA messageType(mir_t2a(ptrT(json_as_string(json_get(node, "messagetype")))));
if (!mir_strcmpi(messageType, "Text") || !mir_strcmpi(messageType, "RichText"))
{
GCDEST gcd = { m_szModuleName, ptrT(mir_a2t(chatname)), GC_EVENT_MESSAGE };
GCEVENT gce = { sizeof(GCEVENT), &gcd };
ptrA contactName(ContactUrlToName(from));
ptrT tszName(mir_a2t(contactName));
ptrA szHtml(RemoveHtml(content));
ptrT tszHtml(mir_a2t(szHtml));
gce.bIsMe = IsMe(contactName);
gce.ptszUID = tszName;
gce.time = timestamp;
gce.ptszNick = tszName;
gce.ptszText = tszHtml;
gce.dwFlags = GCEF_ADDTOLOG;
CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
}
else if (!mir_strcmpi(messageType, "ThreadActivity/AddMember"))
{
ptrA xinitiator, xtarget, initiator, target;
//content = 14291862291648:initiator8:user
HXML xml = xi.parseString(ptrT(mir_a2t(content)), 0, _T("addmember"));
if (xml != NULL) {
HXML xmlNode = xi.getChildByPath(xml, _T("target"), 0);
xtarget = xmlNode != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xi.destroyNode(xml);
}
target = ParseUrl(xtarget, "8:");
AddChatContact(_A2T(chatname), target, target, L"User");
}
else if (!mir_strcmpi(messageType, "ThreadActivity/DeleteMember"))
{
ptrA xinitiator, xtarget, initiator, target;
//content = 14291862291648:initiator8:user
HXML xml = xi.parseString(ptrT(mir_a2t(content)), 0, _T("deletemember"));
if (xml != NULL) {
HXML xmlNode = xi.getChildByPath(xml, _T("initiator"), 0);
xinitiator = node != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xmlNode = xi.getChildByPath(xml, _T("target"), 0);
xtarget = xmlNode != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xi.destroyNode(xml);
}
if(xtarget == NULL)
return;
target = ParseUrl(xtarget, "8:");
bool isKick = false;
initiator = ParseUrl(xinitiator, "8:");
isKick = true;
if (isKick)
{
RemoveChatContact(_A2T(chatname), target, target, true, initiator);
}
else
{
RemoveChatContact(_A2T(chatname), target, target);
}
}
else if (!mir_strcmpi(messageType, "ThreadActivity/TopicUpdate"))
{
//content=14295327021308:usertest topic
ptrA xinitiator, value, initiator;
HXML xml = xi.parseString(ptrT(mir_a2t(content)), 0, _T("topicupdate"));
if (xml != NULL) {
HXML xmlNode = xi.getChildByPath(xml, _T("initiator"), 0);
xinitiator = xmlNode != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xmlNode = xi.getChildByPath(xml, _T("value"), 0);
value = xmlNode != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xi.destroyNode(xml);
}
initiator = ParseUrl(xinitiator, "8:");
RenameChat(chatname, value);
ChangeChatTopic(chatname, value, initiator);
}
else if (!mir_strcmpi(messageType, "ThreadActivity/RoleUpdate"))
{
//content=14295512583638:user8:user1admin
ptrA xinitiator, xId, initiator, id, xRole;
HXML xml = xi.parseString(ptrT(mir_a2t(content)), 0, _T("roleupdate"));
if (xml != NULL) {
HXML xmlNode = xi.getChildByPath(xml, _T("initiator"), 0);
xinitiator = xmlNode != NULL ? mir_t2a(xi.getText(xmlNode)) : NULL;
xmlNode = xi.getChildByPath(xml, _T("target"), 0);
if (xmlNode != NULL)
{
HXML xmlId = xi.getChildByPath(xmlNode, _T("id"), 0);
HXML xmlRole = xi.getChildByPath(xmlNode, _T("role"), 0);
xId = xmlNode != NULL ? mir_t2a(xi.getText(xmlId)) : NULL;
xRole = xmlNode != NULL ? mir_t2a(xi.getText(xmlRole)) : NULL;
}
xi.destroyNode(xml);
initiator = ParseUrl(xinitiator, "8:");
id = ParseUrl(xId, "8:");
GCDEST gcd = { m_szModuleName, _A2T(chatname), !mir_strcmpi(xRole, "Admin") ? GC_EVENT_ADDSTATUS : GC_EVENT_REMOVESTATUS};
GCEVENT gce = { sizeof(gce), &gcd };
ptrT tszId(mir_a2t(id));
ptrT tszRole(mir_a2t(xRole));
ptrT tszInitiator(mir_a2t(initiator));
gce.pDest = &gcd;
gce.dwFlags = GCEF_ADDTOLOG;
gce.ptszNick = tszId;
gce.ptszUID = tszId;
gce.ptszText = tszInitiator;
gce.time = time(NULL);
gce.bIsMe = IsMe(id);
gce.ptszStatus = TranslateT("Admin");
CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
}
}
}
void CSkypeProto::OnGetChatInfo(const NETLIBHTTPREQUEST *response)
{
if (response == NULL || response->pData == NULL)
return;
JSONROOT root(response->pData);
JSONNODE *members = json_get(root, "members");
JSONNODE *properties = json_get(root, "properties");
ptrA topic(mir_t2a(json_as_string(json_get(properties, "topic"))));
ptrA chatId(ChatUrlToName(ptrA(mir_t2a(ptrT(json_as_string(json_get(root, "messages")))))));
//RenameChat(chatId, topic);
for (size_t i = 0; i < json_size(members); i++)
{
JSONNODE *member = json_at(members, i);
ptrA username(ContactUrlToName(ptrA(mir_t2a(ptrT(json_as_string(json_get(member, "userLink")))))));
ptrT role(json_as_string(json_get(member, "role")));
if (!IsChatContact(_A2T(chatId), username))
AddChatContact(_A2T(chatId), username, username, role, true);
}
}
void CSkypeProto::RenameChat(const char *chat_id, const char *name)
{
ptrT tchat_id(mir_a2t(chat_id));
ptrT tname(mir_a2t_cp(name, CP_UTF8));
GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_CHANGESESSIONAME };
GCEVENT gce = { sizeof(gce), &gcd };
gce.ptszText = tname;
CallService(MS_GC_EVENT, 0, reinterpret_cast(&gce));
}
void CSkypeProto::ChangeChatTopic(const char *chat_id, const char *topic, const char *initiator)
{
ptrT tchat_id(mir_a2t(chat_id));
ptrT tname(mir_a2t(initiator));
ptrT ttopic(mir_a2t(topic));
GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_TOPIC };
GCEVENT gce = { sizeof(gce), &gcd };
gce.ptszUID = tname;
gce.ptszNick = tname;
gce.ptszText = ttopic;
CallService(MS_GC_EVENT, 0, reinterpret_cast(&gce));
}
bool CSkypeProto::IsChatContact(const TCHAR *chat_id, const char *id)
{
ptrA users(GetChatUsers(chat_id));
return (users != NULL && strstr(users, id) != NULL);
}
char *CSkypeProto::GetChatUsers(const TCHAR *chat_id)
{
GC_INFO gci = { 0 };
gci.Flags = GCF_USERS;
gci.pszModule = m_szModuleName;
gci.pszID = chat_id;
CallService(MS_GC_GETINFO, 0, (LPARAM)&gci);
// mir_free(gci.pszUsers);
return gci.pszUsers;
}
void CSkypeProto::AddChatContact(const TCHAR *tchat_id, const char *id, const char *name, const TCHAR *role, bool isChange)
{
if (IsChatContact(tchat_id, id))
return;
ptrT tnick(mir_a2t_cp(name, CP_UTF8));
ptrT tid(mir_a2t(id));
GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_JOIN };
GCEVENT gce = { sizeof(gce), &gcd };
gce.pDest = &gcd;
gce.dwFlags = GCEF_ADDTOLOG;
gce.ptszNick = tnick;
gce.ptszUID = tid;
gce.time = !isChange ? time(NULL): NULL;
gce.bIsMe = IsMe(id);
gce.ptszStatus = TranslateTS(role);
CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast(&gce));
}
void CSkypeProto::RemoveChatContact(const TCHAR *tchat_id, const char *id, const char *name, bool isKick, const char *initiator)
{
if(IsMe(id))
return;
ptrT tnick(mir_a2t_cp(name, CP_UTF8));
ptrT tid(mir_a2t(id));
ptrT tinitiator(mir_a2t(initiator));
GCDEST gcd = { m_szModuleName, tchat_id, isKick ? GC_EVENT_KICK : GC_EVENT_PART };
GCEVENT gce = { sizeof(gce), &gcd };
if (isKick)
{
gce.ptszUID = tid;
gce.ptszNick = tnick;
gce.ptszStatus = tinitiator;
gce.time = time(NULL);
}
else
{
gce.dwFlags = GCEF_ADDTOLOG;
gce.ptszNick = tnick;
gce.ptszUID = tid;
gce.time = time(NULL);
gce.bIsMe = IsMe(id);
}
CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast(&gce));
}
INT_PTR CSkypeProto::InviteDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
TranslateDialogDefault(hwndDlg);
{
CSkypeProto *ppro = (CSkypeProto*)lParam;
HWND hwndCombo = GetDlgItem(hwndDlg, IDC_CONTACT);
for (MCONTACT hContact = db_find_first(ppro->m_szModuleName); hContact; hContact = db_find_next(hContact, ppro->m_szModuleName)) {
if (ppro->isChatRoom(hContact)) continue;
TCHAR *ptszNick = pcli->pfnGetContactDisplayName(hContact, 0);
int idx = SendMessage(hwndCombo, CB_ADDSTRING, 0, LPARAM(ptszNick));
SendMessage(hwndCombo, CB_SETITEMDATA, idx, hContact);
}
SendMessage(hwndCombo, CB_SETCURSEL, 0, 0);
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCANCEL:
EndDialog(hwndDlg, 0);
return TRUE;
case IDOK:
int idx = SendDlgItemMessage(hwndDlg, IDC_CONTACT, CB_GETCURSEL, 0, 0);
if (idx != -1)
EndDialog(hwndDlg, SendDlgItemMessage(hwndDlg, IDC_CONTACT, CB_GETITEMDATA, idx, 0));
else
EndDialog(hwndDlg, 0);
return TRUE;
}
}
return 0;
}