diff options
Diffstat (limited to 'protocols/WhatsApp/src/chat.cpp')
| -rw-r--r-- | protocols/WhatsApp/src/chat.cpp | 505 | 
1 files changed, 463 insertions, 42 deletions
diff --git a/protocols/WhatsApp/src/chat.cpp b/protocols/WhatsApp/src/chat.cpp index 361da25850..1f855f9b55 100644 --- a/protocols/WhatsApp/src/chat.cpp +++ b/protocols/WhatsApp/src/chat.cpp @@ -1,64 +1,485 @@  #include "common.h"
 -// #TODO Remove, as we are not using the chat-module for groups anymore
 +static const TCHAR *sttStatuses[] = { LPGENT("Members"), LPGENT("Owners") };
 -INT_PTR WhatsAppProto::OnJoinChat(WPARAM, LPARAM)
 +enum
  {
 -	return 0;
 -}
 +	IDM_CANCEL,
 +
 +	IDM_INVITE, IDM_LEAVE, IDM_TOPIC,
 -INT_PTR WhatsAppProto::OnLeaveChat(WPARAM, LPARAM)
 +	IDM_MESSAGE, IDM_KICK,
 +	IDM_CPY_NICK, IDM_CPY_TOPIC,
 +	IDM_ADD_RJID, IDM_CPY_RJID
 +};
 +
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// protocol menu handler - create a new group
 +
 +INT_PTR __cdecl WhatsAppProto::OnCreateGroup(WPARAM wParam, LPARAM lParam)
  {
 -	return 0;
 +	ENTER_STRING es = { 0 };
 +	es.cbSize = sizeof(es);
 +	es.type = ESF_MULTILINE;
 +	es.caption = _T("Enter a subject for new group");
 +	es.szModuleName = m_szModuleName;
 +	if (EnterString(&es)) {
 +		if (isOnline()) {
 +			std::string groupName(ptrA(mir_utf8encodeT(es.ptszResult)));
 +			m_pConnection->sendCreateGroupChat(groupName);
 +		}
 +		mir_free(es.ptszResult);
 +	}
 +
 +	return FALSE;
  }
 -int WhatsAppProto::OnChatOutgoing(WPARAM wParam, LPARAM lParam)
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// handler to pass events from SRMM to WAConnection
 +
 +int WhatsAppProto::onGroupChatEvent(WPARAM wParam, LPARAM lParam)
  {
 -	GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
 -	char *text;
 +	GCHOOK *gch = (GCHOOK*)lParam;
 +	if (mir_strcmp(gch->pDest->pszModule, m_szModuleName))
 +		return 0;
 -	if (strcmp(hook->pDest->pszModule, m_szModuleName))
 +	std::string chat_id(ptrA(mir_utf8encodeT(gch->pDest->ptszID)));
 +	WAChatInfo *pInfo = SafeGetChat(chat_id);
 +	if (pInfo == NULL)
  		return 0;
 -	switch (hook->pDest->iType) {
 +	switch (gch->pDest->iType) {
 +	case GC_USER_LEAVE:
 +	case GC_SESSION_TERMINATE:
 +		break;
 +
 +	case GC_USER_LOGMENU:
 +		ChatLogMenuHook(pInfo, gch);
 +		break;
 +
 +	case GC_USER_NICKLISTMENU:
 +		NickListMenuHook(pInfo, gch);
 +		break;
 +
  	case GC_USER_MESSAGE:
 -		text = mir_t2a_cp(hook->ptszText, CP_UTF8);
 -		{
 -			std::string msg = text;
 -
 -			char *id = mir_t2a_cp(hook->pDest->ptszID, CP_UTF8);
 -			std::string chat_id = id;
 -
 -			mir_free(text);
 -			mir_free(id);
 -
 -			if (isOnline()) {
 -				MCONTACT hContact = this->ContactIDToHContact(chat_id);
 -				if (hContact) {
 -					debugLogA("**Chat - Outgoing message: %s", text);
 -					this->SendMsg(hContact, IS_CHAT, msg.c_str());
 -
 -					GCDEST gcd = { m_szModuleName, hook->pDest->ptszID, GC_EVENT_MESSAGE };
 -					GCEVENT gce = { sizeof(gce), &gcd };
 -					gce.dwFlags = GCEF_ADDTOLOG;
 -					gce.ptszNick = mir_a2t(m_szNick.c_str());
 -					gce.ptszUID = mir_a2t(m_szJid.c_str());
 -					gce.time = time(NULL);
 -					gce.ptszText = hook->ptszText;
 -					gce.bIsMe = TRUE;
 -					CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
 -
 -					mir_free((void*)gce.ptszUID);
 -					mir_free((void*)gce.ptszNick);
 -				}
 +		if (isOnline()) {
 +			std::string msg(ptrA(mir_utf8encodeT(gch->ptszText)));
 +			
 +			try {
 +				int msgId = GetSerial();
 +				time_t now = time(NULL);
 +				std::string id = Utilities::intToStr(now) + "-" + Utilities::intToStr(msgId);
 +
 +				FMessage fmsg(chat_id, true, id);
 +				fmsg.timestamp = now;
 +				fmsg.data = msg;
 +				m_pConnection->sendMessage(&fmsg);
 +
 +				pInfo->m_unsentMsgs[id] = gch->ptszText;
  			}
 +			CODE_BLOCK_CATCH_ALL
  		}
  		break;
 +	}
 -	case GC_USER_LEAVE:
 -	case GC_SESSION_TERMINATE:
 +	return 0;
 +}
 +
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// chat log menu event handler
 +
 +static gc_item sttLogListItems[] =
 +{
 +	{ LPGENT("&Invite a user"),      IDM_INVITE,    MENU_ITEM },
 +	{ NULL, 0, MENU_SEPARATOR },
 +	{ LPGENT("&Room options"),       0,             MENU_NEWPOPUP },
 +	{ LPGENT("View/change &topic"),  IDM_TOPIC,     MENU_POPUPITEM },
 +	{ LPGENT("&Leave chat session"), IDM_LEAVE,     MENU_POPUPITEM },
 +	{ NULL, 0, MENU_SEPARATOR },
 +	{ LPGENT("Copy room &JID"),      IDM_CPY_RJID,  MENU_ITEM },
 +	{ LPGENT("Copy room topic"),     IDM_CPY_TOPIC, MENU_ITEM },
 +};
 +
 +void WhatsAppProto::ChatLogMenuHook(WAChatInfo *pInfo, struct GCHOOK *gch)
 +{
 +	switch (gch->dwData) {
 +	case IDM_INVITE:
 +		InviteChatUser(pInfo);
  		break;
 +
 +	case IDM_TOPIC:
 +		EditChatSubject(pInfo);
 +		break;
 +
 +	case IDM_CPY_RJID:
 +		utils::copyText(pcli->hwndContactList, pInfo->tszJid);
 +		break;
 +
 +	case IDM_CPY_TOPIC:
 +		utils::copyText(pcli->hwndContactList, pInfo->tszNick);
 +		break;
 +
 +	case IDM_LEAVE:
 +		if (isOnline())
 +			m_pConnection->sendJoinLeaveGroup(_T2A(pInfo->tszJid), false);
 +		break;
 +	}
 +}
 +
 +void WhatsAppProto::EditChatSubject(WAChatInfo *pInfo)
 +{
 +	CMString title(FORMAT, TranslateT("Set new subject for %s"), pInfo->tszNick);
 +	ptrT tszOldValue(getTStringA(pInfo->hContact, "Nick"));
 +
 +	ENTER_STRING es = { 0 };
 +	es.cbSize = sizeof(es);
 +	es.type = ESF_RICHEDIT;
 +	es.szModuleName = m_szModuleName;
 +	es.ptszInitVal = tszOldValue;
 +	es.caption = title;
 +	es.szDataPrefix = "setSubject_";
 +	if (EnterString(&es)) {
 +		ptrA gjid(mir_utf8encodeT(pInfo->tszJid));
 +		ptrA gsubject(mir_utf8encodeT(es.ptszResult));
 +		m_pConnection->sendSetNewSubject(std::string(gjid), std::string(gsubject));
 +		mir_free(es.ptszResult);
 +	}
 +}
 +
 +void WhatsAppProto::InviteChatUser(WAChatInfo *pInfo)
 +{
 +	if (TRUE != DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_GROUPCHAT_INVITE), NULL, InviteDialogProc, (LPARAM)this))
 +		return;
 +
 +	if (isOnline()) {
 +		m_pConnection->sendAddParticipants((char*)_T2A(pInfo->tszJid), m_szInviteJids);
 +		m_szInviteJids.clear();
 +	}
 +}
 +
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// nicklist menu event handler
 +
 +static gc_item sttListItems[] =
 +{
 +	{ LPGENT("&Add to roster"), IDM_ADD_RJID, MENU_POPUPITEM },
 +	{ NULL, 0, MENU_SEPARATOR },
 +	{ LPGENT("&Kick"), IDM_KICK, MENU_ITEM },
 +	{ NULL, 0, MENU_SEPARATOR },
 +	{ LPGENT("Copy &nickname"), IDM_CPY_NICK, MENU_ITEM },
 +	{ LPGENT("Copy real &JID"), IDM_CPY_RJID, MENU_ITEM },
 +};
 +
 +void WhatsAppProto::NickListMenuHook(WAChatInfo *pInfo, struct GCHOOK *gch)
 +{
 +	switch (gch->dwData) {
 +	case IDM_ADD_RJID:
 +		AddChatUser(pInfo, gch->ptszUID);
 +		break;
 +
 +	case IDM_KICK:
 +		KickChatUser(pInfo, gch->ptszUID);
 +		break;
 +
 +	case IDM_CPY_NICK:
 +		utils::copyText(pcli->hwndContactList, GetChatUserNick(std::string((char*)_T2A(gch->ptszUID))));
 +		break;
 +
 +	case IDM_CPY_RJID:
 +		utils::copyText(pcli->hwndContactList, gch->ptszUID);
 +		break;
 +	}
 +}
 +
 +void WhatsAppProto::AddChatUser(WAChatInfo *pInfo, const TCHAR *ptszJid)
 +{
 +	std::string jid((char*)_T2A(ptszJid));
 +	MCONTACT hContact = ContactIDToHContact(jid);
 +	if (hContact && !db_get_b(hContact, "CList", "NotInList", 0))
 +		return;
 +
 +	PROTOSEARCHRESULT sr = { 0 };
 +	sr.cbSize = sizeof(sr);
 +	sr.flags = PSR_TCHAR;
 +	sr.id = (TCHAR*)ptszJid;
 +	sr.nick = GetChatUserNick(jid);
 +
 +	ADDCONTACTSTRUCT acs = { 0 };
 +	acs.handleType = HANDLE_SEARCHRESULT;
 +	acs.szProto = m_szModuleName;
 +	acs.psr = (PROTOSEARCHRESULT*)&sr;
 +	CallService(MS_ADDCONTACT_SHOW, (WPARAM)CallService(MS_CLUI_GETHWND, 0, 0), (LPARAM)&acs);
 +}
 +
 +void WhatsAppProto::KickChatUser(WAChatInfo *pInfo, const TCHAR *ptszJid)
 +{
 +	if (!isOnline())
 +		return;
 +
 +	std::string gjid((char*)_T2A(pInfo->tszJid));
 +	std::vector<std::string> jids(1);
 +	jids[0] = (char*)_T2A(ptszJid);
 +	m_pConnection->sendRemoveParticipants(gjid, jids);
 +}
 +
 +/////////////////////////////////////////////////////////////////////////////////////////
 +// handler to customize chat menus
 +
 +int WhatsAppProto::OnChatMenu(WPARAM wParam, LPARAM lParam)
 +{
 +	GCMENUITEMS *gcmi = (GCMENUITEMS*)lParam;
 +	if (gcmi == NULL)
 +		return 0;
 +
 +	if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
 +		return 0;
 +
 +	if (gcmi->Type == MENU_ON_LOG) {
 +		gcmi->nItems = SIZEOF(sttLogListItems);
 +		gcmi->Item = sttLogListItems;
 +	}
 +	else if (gcmi->Type == MENU_ON_NICKLIST) {
 +		gcmi->nItems = SIZEOF(sttListItems);
 +		gcmi->Item = sttListItems;
  	}
  	return 0;
  }
 +
 +///////////////////////////////////////////////////////////////////////////////
 +// chat helpers
 +
 +WAChatInfo* WhatsAppProto::InitChat(const std::string &jid, const std::string &nick)
 +{
 +	TCHAR *ptszJid = str2t(jid), *ptszNick = str2t(nick);
 +
 +	WAChatInfo *pInfo = new WAChatInfo(ptszJid, ptszNick);
 +	m_chats[jid] = pInfo;
 +
 +	GCSESSION gcw = { sizeof(GCSESSION) };
 +	gcw.iType = GCW_CHATROOM;
 +	gcw.pszModule = m_szModuleName;
 +	gcw.ptszName = ptszNick;
 +	gcw.ptszID = ptszJid;
 +	CallServiceSync(MS_GC_NEWSESSION, NULL, (LPARAM)&gcw);
 +
 +	pInfo->hContact = ContactIDToHContact(jid);
 +
 +	GCDEST gcd = { m_szModuleName, ptszJid, GC_EVENT_ADDGROUP };
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	for (int i = SIZEOF(sttStatuses) - 1; i >= 0; i--) {
 +		gce.ptszStatus = TranslateTS(sttStatuses[i]);
 +		CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +	}
 +
 +	gcd.iType = GC_EVENT_CONTROL;
 +	CallServiceSync(MS_GC_EVENT, (m_pConnection) ? WINDOW_HIDDEN : SESSION_INITDONE, (LPARAM)&gce);
 +	CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce);
 +
 +	if (m_pConnection)
 +		m_pConnection->sendGetParticipants(jid);
 +
 +	return pInfo;
 +}
 +
 +TCHAR* WhatsAppProto::GetChatUserNick(const std::string &jid)
 +{
 +	if (m_szJid == jid)
 +		return str2t(m_szNick);
 +
 +	MCONTACT hContact = ContactIDToHContact(jid);
 +	return (hContact == 0) ? utils::removeA(str2t(jid)) : mir_tstrdup(pcli->pfnGetContactDisplayName(hContact, 0));
 +}
 +
 +WAChatInfo* WhatsAppProto::SafeGetChat(const std::string &jid)
 +{
 +	mir_cslock lck(m_csChats);
 +	return m_chats[jid];
 +}
 +
 +///////////////////////////////////////////////////////////////////////////////
 +// WAGroupListener members
 +
 +void WhatsAppProto::onGroupInfo(const std::string &jid, const std::string &owner, const std::string &subject, const std::string &subject_owner, int time_subject, int time_created)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(jid);
 +	if (pInfo == NULL) {
 +		pInfo = InitChat(jid, subject);
 +		pInfo->bActive = true;
 +	}
 +
 +	if (!subject.empty()) {
 +		pInfo->tszOwner = str2t(owner);
 +
 +		onGroupNewSubject(jid, subject_owner, subject, time_subject);
 +	}
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_CONTROL };
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce);
 +}
 +
 +void WhatsAppProto::onGroupMessage(const FMessage &msg)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(msg.key.remote_jid);
 +	if (pInfo == NULL) {
 +		pInfo = InitChat(msg.key.remote_jid, "");
 +		pInfo->bActive = true;
 +	}
 +
 +	ptrT tszText(str2t(msg.data));
 +	ptrT tszUID(str2t(msg.remote_resource));
 +	ptrT tszNick(GetChatUserNick(msg.remote_resource));
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_MESSAGE };
 +
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.dwFlags = GCEF_ADDTOLOG;
 +	gce.ptszUID = tszUID;
 +	gce.ptszNick = tszNick;
 +	gce.time = msg.timestamp;
 +	gce.ptszText = tszText;
 +	gce.bIsMe = m_szJid == msg.remote_resource;
 +	CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +
 +	if (isOnline())
 +		m_pConnection->sendMessageReceived(msg);
 +}
 +
 +void WhatsAppProto::onGroupNewSubject(const std::string &gjid, const std::string &author, const std::string &newSubject, int ts)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(gjid);
 +	if (pInfo == NULL)
 +		return;
 +
 +	ptrT tszUID(str2t(author));
 +	ptrT tszNick(GetChatUserNick(author));
 +	ptrT tszText(str2t(newSubject));
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_TOPIC };
 +
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.dwFlags = GCEF_ADDTOLOG;
 +	gce.ptszUID = tszUID;
 +	gce.ptszNick = tszNick;
 +	gce.time = ts;
 +	gce.ptszText = tszText;
 +	CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +
 +	setTString(pInfo->hContact, "Nick", tszText);
 +}
 +
 +void WhatsAppProto::onGroupAddUser(const std::string &gjid, const std::string &ujid, int ts)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(gjid);
 +	if (pInfo == NULL)
 +		return;
 +
 +	ptrT tszUID(str2t(ujid));
 +	ptrT tszNick(GetChatUserNick(ujid));
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_JOIN };
 +
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.dwFlags = GCEF_ADDTOLOG;
 +	gce.ptszUID = tszUID;
 +	gce.ptszNick = tszNick;
 +	gce.time = ts;
 +	CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +}
 +
 +void WhatsAppProto::onGroupRemoveUser(const std::string &gjid, const std::string &ujid, int ts)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(gjid);
 +	if (pInfo == NULL)
 +		return;
 +
 +	ptrT tszUID(str2t(ujid));
 +	ptrT tszNick(GetChatUserNick(ujid));
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_PART };
 +
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.dwFlags = GCEF_ADDTOLOG;
 +	gce.ptszUID = tszUID;
 +	gce.ptszNick = tszNick;
 +	gce.time = ts;
 +	CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +}
 +
 +void WhatsAppProto::onLeaveGroup(const std::string &gjid)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(gjid);
 +	if (pInfo == NULL)
 +		return;
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_CONTROL };
 +	
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.ptszUID = pInfo->tszJid;
 +	CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce);
 +
 +	CallService(MS_DB_CONTACT_DELETE, pInfo->hContact, 0);
 +	m_chats.erase((char*)_T2A(pInfo->tszJid));
 +}
 +
 +void WhatsAppProto::onGetParticipants(const std::string &gjid, const std::vector<string> &participants)
 +{
 +	mir_cslock lck(m_csChats);
 +
 +	WAChatInfo *pInfo = m_chats[gjid];
 +	if (pInfo == NULL)
 +		return;
 +
 +	for (size_t i = 0; i < participants.size(); i++) {
 +		std::string curr = participants[i];
 +
 +		ptrT ujid(str2t(curr)), nick(GetChatUserNick(curr));
 +		bool bIsOwner = !mir_tstrcmp(ujid, pInfo->tszOwner);
 +
 +		GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_JOIN };
 +
 +		GCEVENT gce = { sizeof(gce), &gcd };
 +		gce.ptszNick = nick;
 +		gce.ptszUID = utils::removeA(ujid);
 +		gce.ptszStatus = (bIsOwner) ? _T("Owners") : _T("Members");
 +		CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +	}
 +}
 +
 +void WhatsAppProto::onGroupCreated(const std::string &gjid, const std::string &subject)
 +{
 +	WAChatInfo *pInfo = InitChat(gjid, subject);
 +	pInfo->tszOwner = str2t(m_szJid);
 +
 +	// also set new subject if it's present
 +	if (!subject.empty())
 +		onGroupNewSubject(gjid, "Server", subject, time(0));
 +}
 +
 +void WhatsAppProto::onGroupMessageReceived(const FMessage &msg)
 +{
 +	WAChatInfo *pInfo = SafeGetChat(msg.key.remote_jid);
 +	if (pInfo == NULL)
 +		return;
 +	
 +	auto p = pInfo->m_unsentMsgs.find(msg.key.id);
 +	if (p == pInfo->m_unsentMsgs.end())
 +		return;
 +
 +	ptrT tszUID(str2t(m_szJid));
 +	ptrT tszNick(str2t(m_szNick));
 +
 +	GCDEST gcd = { m_szModuleName, pInfo->tszJid, GC_EVENT_MESSAGE };
 +
 +	GCEVENT gce = { sizeof(gce), &gcd };
 +	gce.dwFlags = GCEF_ADDTOLOG;
 +	gce.ptszUID = tszUID;
 +	gce.ptszNick = tszNick;
 +	gce.time = msg.timestamp;
 +	gce.ptszText = p->second.c_str();
 +	gce.bIsMe = m_szJid == msg.remote_resource;
 +	CallServiceSync(MS_GC_EVENT, NULL, (LPARAM)&gce);
 +
 +	pInfo->m_unsentMsgs.erase(p);
 +}
  | 
