From f7782fbf614361339617da21e6220044ffcf6822 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Robert=20P=C3=B6sel?= <robyer@seznam.cz>
Date: Mon, 1 Dec 2014 13:32:25 +0000
Subject: Facebook: Fix sending private message to user in chat, which is not
 in contactlist yet

git-svn-id: http://svn.miranda-ng.org/main/trunk@11193 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 protocols/FacebookRM/src/chat.cpp     | 909 +++++++++++++++++-----------------
 protocols/FacebookRM/src/contacts.cpp |   7 +-
 protocols/FacebookRM/src/proto.cpp    |  17 +-
 protocols/FacebookRM/src/proto.h      |   2 +-
 4 files changed, 473 insertions(+), 462 deletions(-)

(limited to 'protocols')

diff --git a/protocols/FacebookRM/src/chat.cpp b/protocols/FacebookRM/src/chat.cpp
index fc02edf353..b9f416a632 100644
--- a/protocols/FacebookRM/src/chat.cpp
+++ b/protocols/FacebookRM/src/chat.cpp
@@ -1,451 +1,460 @@
-/*
-
-Facebook plugin for Miranda Instant Messenger
-_____________________________________________
-
-Copyright � 2011-13 Robert P�sel
-
-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, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "common.h"
-#include <m_history.h>
-#include <m_userinfo.h>
-
-void FacebookProto::UpdateChat(const TCHAR *tchat_id, const char *id, const char *name, const char *message, DWORD timestamp, bool is_old)
-{
-	// replace % to %% to not interfere with chat color codes
-	std::string smessage = message;
-	utils::text::replace_all(&smessage, "%", "%%");
-
-	ptrT tid( mir_a2t(id));
-	ptrT tnick( mir_a2t_cp(name,CP_UTF8));
-	ptrT ttext( mir_a2t_cp(smessage.c_str(),CP_UTF8));
-
-	GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_MESSAGE };
-	GCEVENT gce = { sizeof(gce), &gcd };
-	gce.ptszText = ttext;
-	gce.time = timestamp ? timestamp : ::time(NULL);
-	if (id != NULL)
-		gce.bIsMe = !strcmp(id,facy.self_.user_id.c_str());
-	gce.dwFlags |= GCEF_ADDTOLOG;
-	if (is_old) {
-		gce.dwFlags |= GCEF_NOTNOTIFY;
-		gce.dwFlags &= ~GCEF_ADDTOLOG;
-	}
-	gce.ptszNick = tnick;
-	gce.ptszUID  = tid;
-	CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
-	
-	// TODO: keep it here or move it somewhere else?
-	std::map<std::tstring, facebook_chatroom*>::iterator chatroom = facy.chat_rooms.find(tchat_id);
-	if (chatroom != facy.chat_rooms.end()) {
-		chatroom->second->message_readers.clear();
-	}
-}
-
-void FacebookProto::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<LPARAM>(&gce));
-}
-
-int FacebookProto::OnGCEvent(WPARAM wParam,LPARAM lParam)
-{
-	GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
-
-	if (strcmp(hook->pDest->pszModule, m_szModuleName))
-		return 0;
-
-	// Ignore for special chatrooms
-	if (!_tcscmp(hook->pDest->ptszID, _T(FACEBOOK_NOTIFICATIONS_CHATROOM)))
-		return 0;
-
-	switch(hook->pDest->iType)
-	{
-	case GC_USER_MESSAGE:
-	{
-		std::string msg = _T2A(hook->ptszText, CP_UTF8);
-		std::string chat_id = _T2A(hook->pDest->ptszID, CP_UTF8);
-
-		if (isOnline()) {
-			debugLogA("**Chat - Outgoing message: %s", msg.c_str());
-			ForkThread(&FacebookProto::SendChatMsgWorker, new send_chat(chat_id, msg));
-		}
-	
-		break;
-	}
-
-	case GC_USER_PRIVMESS:
-	{
-		std::string id = _T2A(hook->ptszUID, CP_UTF8);
-
-		MCONTACT hContact = ContactIDToHContact(id);
-		if (!hContact)
-			break; // TODO: create new temporary contact
-
-		CallService(MS_MSG_SENDMESSAGET, hContact, 0);
-		break;
-	}
-
-	/*
-	case GC_USER_LOGMENU:
-	{
-		switch(hook->dwData) 
-		{
-		case 10:
-			DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHATROOM_INVITE), NULL, invite_to_chat_dialog, 
-				LPARAM(new invite_chat_param(item->id, this)));
-			break;
-
-		case 20:
-			//chat_leave(id);
-			break;
-		}
-		break;
-	}
-	*/
-
-	case GC_USER_NICKLISTMENU: 
-	{
-		std::string id = _T2A(hook->ptszUID, CP_UTF8);
-
-		MCONTACT hContact = ContactIDToHContact(id);
-		if (!hContact)
-			break; // TODO: create new temporary contact
-
-		switch (hook->dwData) 
-		{
-		case 10:
-			CallService(MS_USERINFO_SHOWDIALOG, hContact, 0);
-			break;
-
-		case 20:
-			CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0);
-			break;
-
-		case 110:
-			//chat_leave(id);
-			break;
-		}
-
-		break;
-	}
-
-	case GC_USER_LEAVE:
-	case GC_SESSION_TERMINATE:
-		break;
-	}
-
-	return 0;
-}
-
-void FacebookProto::AddChatContact(const TCHAR *tchat_id, const char *id, const char *name)
-{
-	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 = ::time(NULL);
-	gce.bIsMe = !strcmp(id, facy.self_.user_id.c_str());
-
-	if (gce.bIsMe) {
-		gce.ptszStatus = TranslateT("Myself");
-	} else {
-		MCONTACT hContact = ContactIDToHContact(id);
-		if (hContact == NULL || getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_NONE) != CONTACT_FRIEND)
-			gce.ptszStatus = TranslateT("User");
-		else {
-			gce.ptszStatus = TranslateT("Friend");
-		}
-	}
-
-	CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast<LPARAM>(&gce));
-}
-
-void FacebookProto::RemoveChatContact(const TCHAR *tchat_id, const char *id, const char *name)
-{
-	// We dont want to remove our self-contact from chat. Ever.
-	if (!strcmp(id, facy.self_.user_id.c_str()))
-		return;
-
-	ptrT tnick(mir_a2t_cp(name, CP_UTF8));
-	ptrT tid( mir_a2t(id));
-	
-	GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_PART };
-	GCEVENT gce = { sizeof(gce), &gcd };
-	gce.dwFlags = GCEF_ADDTOLOG;
-	gce.ptszNick = tnick;
-	gce.ptszUID = tid;
-	gce.time = ::time(NULL);
-	gce.bIsMe = false;
-
-	CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
-}
-
-/** Caller must free result */
-char *FacebookProto::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);
-
-	debugLogA("**Chat - Users in chat %s: %s", _T2A(chat_id), gci.pszUsers);
-
-	// mir_free(gci.pszUsers);
-	return gci.pszUsers;
-}
-
-bool FacebookProto::IsChatContact(const TCHAR *chat_id, const char *id)
-{
-	ptrA users( GetChatUsers(chat_id));
-	return (users != NULL && strstr(users, id) != NULL);
-}
-
-void FacebookProto::AddChat(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("Myself");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-	gce.ptszStatus = TranslateT("Friend");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-	gce.ptszStatus = TranslateT("User");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-	
-	// Finish initialization
-	gcd.iType = GC_EVENT_CONTROL;
-	gce.time = ::time(NULL);
-	gce.pDest = &gcd;
-	
-	bool hideChats = getBool(FACEBOOK_KEY_HIDE_CHATS, DEFAULT_HIDE_CHATS);
-
-	// 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<LPARAM>(&gce));
-	CallServiceSync(MS_GC_EVENT, SESSION_ONLINE,  reinterpret_cast<LPARAM>(&gce));
-}
-
-INT_PTR FacebookProto::OnJoinChat(WPARAM hContact, LPARAM suppress)
-{	
-	// TODO: load info from server + old history,...
-
-	ptrT idT( getTStringA(hContact, "ChatRoomID"));
-	ptrT nameT( getTStringA(hContact, "Nick"));
-
-	if (idT && nameT)
-		AddChat(idT, nameT);
-
-/*	GCSESSION gcw = {sizeof(gcw)};
-
-	// Create the group chat session
-	gcw.dwFlags   = GC_TCHAR;
-	gcw.iType     = GCW_PRIVMESS;
-	gcw.pszModule = m_szModuleName;
-	gcw.ptszName  = m_tszUserName;
-	gcw.ptszID    = m_tszUserName;
-	CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
-
-	if(m_iStatus != ID_STATUS_ONLINE)
-		return 0;
-
-	// Create a group
-	GCDEST gcd = { m_szModuleName, m_tszUserName, GC_EVENT_ADDGROUP };
-	GCEVENT gce = { sizeof(gce), &gcd };
-	gce.ptszStatus = TranslateT("Myself");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-	gce.ptszStatus = TranslateT("Friend");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-	gce.ptszStatus = TranslateT("User");
-	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
-
-	SetTopic("Omegle is a great way of meeting new friends!");
-
-	// Note: Initialization will finish up in SetChatStatus, called separately
-	if (!suppress)
-		SetChatStatus(m_iStatus);
-*/
-	return 0;
-}
-
-INT_PTR FacebookProto::OnLeaveChat(WPARAM wParam,LPARAM)
-{
-	ptrT idT(wParam ? getTStringA(wParam, "ChatRoomID") : NULL);
-
-	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<LPARAM>(&gce));
-	CallServiceSync(MS_GC_EVENT,SESSION_TERMINATE,reinterpret_cast<LPARAM>(&gce));
-	return 0;
-}
-
-/*
-void FacebookProto::SetChatStatus(int status)
-{
-	GCDEST gcd = { m_szModuleName, m_tszUserName, GC_EVENT_CONTROL };
-	GCEVENT gce = { sizeof(gce), &gcd };
-	gce.time = ::time(NULL);
-
-	if(status == ID_STATUS_ONLINE)
-	{
-		// Add self contact
-		AddChatContact(facy.nick_.c_str());
-
-		CallServiceSync(MS_GC_EVENT,SESSION_INITDONE,reinterpret_cast<LPARAM>(&gce));
-		CallServiceSync(MS_GC_EVENT,SESSION_ONLINE,  reinterpret_cast<LPARAM>(&gce));
-	}
-	else
-	{
-		CallServiceSync(MS_GC_EVENT,SESSION_OFFLINE,reinterpret_cast<LPARAM>(&gce));
-	}
-}
-*/
-
-int FacebookProto::OnGCMenuHook(WPARAM, LPARAM lParam)
-{
-	GCMENUITEMS *gcmi= (GCMENUITEMS*) lParam;
-
-	if (gcmi == NULL || _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 }
-		};
-		gcmi->nItems = SIZEOF(Items);
-		gcmi->Item = (gc_item*)Items;
-	}
-	else if (gcmi->Type == MENU_ON_NICKLIST)
-	{
-		if (!_stricmp(facy.self_.user_id.c_str(), _T2A(gcmi->pszUID)))
-		{
-			/*static const struct gc_item Items[] =
-			{
-				{ LPGENT("User &details"), 10, MENU_ITEM, FALSE },
-				{ LPGENT("User &history"), 20, MENU_ITEM, FALSE },
-				{ _T(""), 100, MENU_SEPARATOR, FALSE },
-				{ LPGENT("&Leave chat session"), 110, MENU_ITEM, FALSE }
-			};
-			gcmi->nItems = SIZEOF(Items);
-			gcmi->Item = (gc_item*)Items;*/
-			gcmi->nItems = 0;
-			gcmi->Item = NULL;
-		}
-		else
-		{
-			static const struct gc_item Items[] =
-			{
-				{ LPGENT("User &details"), 10, MENU_ITEM, FALSE },
-				{ LPGENT("User &history"), 20, MENU_ITEM, FALSE }
-			};
-			gcmi->nItems = SIZEOF(Items);
-			gcmi->Item = (gc_item*)Items;
-		}
-	}
-
-	return 0;
-}
-
-bool FacebookProto::IsSpecialChatRoom(MCONTACT hContact) {
-	if (!isChatRoom(hContact))
-		return false;
-
-	ptrT idT(getTStringA(hContact, "ChatRoomID"));
-	return idT && !_tcscmp(idT, _T(FACEBOOK_NOTIFICATIONS_CHATROOM));
-}
-
-void FacebookProto::PrepareNotificationsChatRoom() {
-	if (!getBool(FACEBOOK_KEY_NOTIFICATIONS_CHATROOM, DEFAULT_NOTIFICATIONS_CHATROOM))
-		return;
-
-	// Prepare notifications chatroom if not exists
-	TCHAR *gidT = _T(FACEBOOK_NOTIFICATIONS_CHATROOM);
-
-	MCONTACT hNotificationsChatRoom = ChatIDToHContact(gidT);
-	if (hNotificationsChatRoom == NULL || getDword(hNotificationsChatRoom, "Status", ID_STATUS_OFFLINE) != ID_STATUS_ONLINE) {
-		TCHAR nameT[200];
-		mir_sntprintf(nameT, SIZEOF(nameT), _T("%s: %s"), m_tszUserName, TranslateT("Notifications"));
-
-		// Create the group chat session
-		GCSESSION gcw = { sizeof(gcw) };
-		gcw.iType = GCW_PRIVMESS;
-		gcw.ptszID = gidT;
-		gcw.pszModule = m_szModuleName;
-		gcw.ptszName = nameT;
-		CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
-
-		// Send setting events
-		GCDEST gcd = { m_szModuleName, gidT, GC_EVENT_CONTROL };
-		GCEVENT gce = { sizeof(gce), &gcd };
-		gce.time = ::time(NULL);
-
-		CallServiceSync(MS_GC_EVENT, WINDOW_HIDDEN, reinterpret_cast<LPARAM>(&gce));
-		CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, reinterpret_cast<LPARAM>(&gce));
-	}
-}
-
-void FacebookProto::UpdateNotificationsChatRoom(facebook_notification *notification) {
-	if (!getBool(FACEBOOK_KEY_NOTIFICATIONS_CHATROOM, DEFAULT_NOTIFICATIONS_CHATROOM))
-		return;
-
-	std::stringstream text;
-	text << notification->text << "\n\n" << PrepareUrl(notification->link);
-
-	std::string message = text.str();
-	utils::text::replace_all(&message, "%", "%%");
-
-	ptrT idT(mir_tstrdup(_T(FACEBOOK_NOTIFICATIONS_CHATROOM)));
-	ptrT messageT(mir_a2t_cp(message.c_str(), CP_UTF8));
-
-	GCDEST gcd = { m_szModuleName, _T(FACEBOOK_NOTIFICATIONS_CHATROOM), GC_EVENT_MESSAGE };
-	GCEVENT gce = { sizeof(gce), &gcd };
-	gce.ptszText = messageT;
-	gce.time = notification->time ? notification->time : ::time(NULL);
-	gce.bIsMe = false;
-	gce.dwFlags |= GCEF_ADDTOLOG;
-	gce.ptszNick = TranslateT("Notifications");
-	gce.ptszUID = idT;
-
-	CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast<LPARAM>(&gce));
+/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright � 2011-13 Robert P�sel
+
+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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+#include <m_history.h>
+#include <m_userinfo.h>
+
+void FacebookProto::UpdateChat(const TCHAR *tchat_id, const char *id, const char *name, const char *message, DWORD timestamp, bool is_old)
+{
+	// replace % to %% to not interfere with chat color codes
+	std::string smessage = message;
+	utils::text::replace_all(&smessage, "%", "%%");
+
+	ptrT tid( mir_a2t(id));
+	ptrT tnick( mir_a2t_cp(name,CP_UTF8));
+	ptrT ttext( mir_a2t_cp(smessage.c_str(),CP_UTF8));
+
+	GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_MESSAGE };
+	GCEVENT gce = { sizeof(gce), &gcd };
+	gce.ptszText = ttext;
+	gce.time = timestamp ? timestamp : ::time(NULL);
+	if (id != NULL)
+		gce.bIsMe = !strcmp(id,facy.self_.user_id.c_str());
+	gce.dwFlags |= GCEF_ADDTOLOG;
+	if (is_old) {
+		gce.dwFlags |= GCEF_NOTNOTIFY;
+		gce.dwFlags &= ~GCEF_ADDTOLOG;
+	}
+	gce.ptszNick = tnick;
+	gce.ptszUID  = tid;
+	CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+	
+	// TODO: keep it here or move it somewhere else?
+	std::map<std::tstring, facebook_chatroom*>::iterator chatroom = facy.chat_rooms.find(tchat_id);
+	if (chatroom != facy.chat_rooms.end()) {
+		chatroom->second->message_readers.clear();
+	}
+}
+
+void FacebookProto::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<LPARAM>(&gce));
+}
+
+int FacebookProto::OnGCEvent(WPARAM wParam,LPARAM lParam)
+{
+	GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
+
+	if (strcmp(hook->pDest->pszModule, m_szModuleName))
+		return 0;
+
+	// Ignore for special chatrooms
+	if (!_tcscmp(hook->pDest->ptszID, _T(FACEBOOK_NOTIFICATIONS_CHATROOM)))
+		return 0;
+
+	switch(hook->pDest->iType)
+	{
+	case GC_USER_MESSAGE:
+	{
+		std::string msg = _T2A(hook->ptszText, CP_UTF8);
+		std::string chat_id = _T2A(hook->pDest->ptszID, CP_UTF8);
+
+		if (isOnline()) {
+			debugLogA("**Chat - Outgoing message: %s", msg.c_str());
+			ForkThread(&FacebookProto::SendChatMsgWorker, new send_chat(chat_id, msg));
+		}
+	
+		break;
+	}
+
+	case GC_USER_PRIVMESS:
+	{
+		facebook_user fbu;
+		fbu.user_id = _T2A(hook->ptszUID, CP_UTF8);
+
+		// Find this contact in list or add new temporary contact
+		MCONTACT hContact = AddToContactList(&fbu, CONTACT_NONE, false, true);
+
+		if (!hContact)
+			break;
+
+		CallService(MS_MSG_SENDMESSAGET, hContact, 0);
+		break;
+	}
+
+	/*
+	case GC_USER_LOGMENU:
+	{
+		switch(hook->dwData) 
+		{
+		case 10:
+			DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHATROOM_INVITE), NULL, invite_to_chat_dialog, 
+				LPARAM(new invite_chat_param(item->id, this)));
+			break;
+
+		case 20:
+			//chat_leave(id);
+			break;
+		}
+		break;
+	}
+	*/
+
+	case GC_USER_NICKLISTMENU: 
+	{
+		MCONTACT hContact = NULL;
+		if (hook->dwData == 10 || hook->dwData == 20) {
+			facebook_user fbu;
+			fbu.user_id = _T2A(hook->ptszUID, CP_UTF8);
+
+			// Find this contact in list or add new temporary contact
+			hContact = AddToContactList(&fbu, CONTACT_NONE, false, true);
+
+			if (!hContact)
+				break;
+		}
+
+		switch (hook->dwData) 
+		{
+		case 10:
+			CallService(MS_USERINFO_SHOWDIALOG, hContact, 0);
+			break;
+
+		case 20:
+			CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0);
+			break;
+
+		case 110:
+			//chat_leave(id);
+			break;
+		}
+
+		break;
+	}
+
+	case GC_USER_LEAVE:
+	case GC_SESSION_TERMINATE:
+		break;
+	}
+
+	return 0;
+}
+
+void FacebookProto::AddChatContact(const TCHAR *tchat_id, const char *id, const char *name)
+{
+	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 = ::time(NULL);
+	gce.bIsMe = !strcmp(id, facy.self_.user_id.c_str());
+
+	if (gce.bIsMe) {
+		gce.ptszStatus = TranslateT("Myself");
+	} else {
+		MCONTACT hContact = ContactIDToHContact(id);
+		if (hContact == NULL || getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_NONE) != CONTACT_FRIEND)
+			gce.ptszStatus = TranslateT("User");
+		else {
+			gce.ptszStatus = TranslateT("Friend");
+		}
+	}
+
+	CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast<LPARAM>(&gce));
+}
+
+void FacebookProto::RemoveChatContact(const TCHAR *tchat_id, const char *id, const char *name)
+{
+	// We dont want to remove our self-contact from chat. Ever.
+	if (!strcmp(id, facy.self_.user_id.c_str()))
+		return;
+
+	ptrT tnick(mir_a2t_cp(name, CP_UTF8));
+	ptrT tid( mir_a2t(id));
+	
+	GCDEST gcd = { m_szModuleName, tchat_id, GC_EVENT_PART };
+	GCEVENT gce = { sizeof(gce), &gcd };
+	gce.dwFlags = GCEF_ADDTOLOG;
+	gce.ptszNick = tnick;
+	gce.ptszUID = tid;
+	gce.time = ::time(NULL);
+	gce.bIsMe = false;
+
+	CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+}
+
+/** Caller must free result */
+char *FacebookProto::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);
+
+	debugLogA("**Chat - Users in chat %s: %s", _T2A(chat_id), gci.pszUsers);
+
+	// mir_free(gci.pszUsers);
+	return gci.pszUsers;
+}
+
+bool FacebookProto::IsChatContact(const TCHAR *chat_id, const char *id)
+{
+	ptrA users( GetChatUsers(chat_id));
+	return (users != NULL && strstr(users, id) != NULL);
+}
+
+void FacebookProto::AddChat(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("Myself");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+	gce.ptszStatus = TranslateT("Friend");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+	gce.ptszStatus = TranslateT("User");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+	
+	// Finish initialization
+	gcd.iType = GC_EVENT_CONTROL;
+	gce.time = ::time(NULL);
+	gce.pDest = &gcd;
+	
+	bool hideChats = getBool(FACEBOOK_KEY_HIDE_CHATS, DEFAULT_HIDE_CHATS);
+
+	// 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<LPARAM>(&gce));
+	CallServiceSync(MS_GC_EVENT, SESSION_ONLINE,  reinterpret_cast<LPARAM>(&gce));
+}
+
+INT_PTR FacebookProto::OnJoinChat(WPARAM hContact, LPARAM suppress)
+{	
+	// TODO: load info from server + old history,...
+
+	ptrT idT( getTStringA(hContact, "ChatRoomID"));
+	ptrT nameT( getTStringA(hContact, "Nick"));
+
+	if (idT && nameT)
+		AddChat(idT, nameT);
+
+/*	GCSESSION gcw = {sizeof(gcw)};
+
+	// Create the group chat session
+	gcw.dwFlags   = GC_TCHAR;
+	gcw.iType     = GCW_PRIVMESS;
+	gcw.pszModule = m_szModuleName;
+	gcw.ptszName  = m_tszUserName;
+	gcw.ptszID    = m_tszUserName;
+	CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
+
+	if(m_iStatus != ID_STATUS_ONLINE)
+		return 0;
+
+	// Create a group
+	GCDEST gcd = { m_szModuleName, m_tszUserName, GC_EVENT_ADDGROUP };
+	GCEVENT gce = { sizeof(gce), &gcd };
+	gce.ptszStatus = TranslateT("Myself");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+	gce.ptszStatus = TranslateT("Friend");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+	gce.ptszStatus = TranslateT("User");
+	CallServiceSync(MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+
+	SetTopic("Omegle is a great way of meeting new friends!");
+
+	// Note: Initialization will finish up in SetChatStatus, called separately
+	if (!suppress)
+		SetChatStatus(m_iStatus);
+*/
+	return 0;
+}
+
+INT_PTR FacebookProto::OnLeaveChat(WPARAM wParam,LPARAM)
+{
+	ptrT idT(wParam ? getTStringA(wParam, "ChatRoomID") : NULL);
+
+	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<LPARAM>(&gce));
+	CallServiceSync(MS_GC_EVENT,SESSION_TERMINATE,reinterpret_cast<LPARAM>(&gce));
+	return 0;
+}
+
+/*
+void FacebookProto::SetChatStatus(int status)
+{
+	GCDEST gcd = { m_szModuleName, m_tszUserName, GC_EVENT_CONTROL };
+	GCEVENT gce = { sizeof(gce), &gcd };
+	gce.time = ::time(NULL);
+
+	if(status == ID_STATUS_ONLINE)
+	{
+		// Add self contact
+		AddChatContact(facy.nick_.c_str());
+
+		CallServiceSync(MS_GC_EVENT,SESSION_INITDONE,reinterpret_cast<LPARAM>(&gce));
+		CallServiceSync(MS_GC_EVENT,SESSION_ONLINE,  reinterpret_cast<LPARAM>(&gce));
+	}
+	else
+	{
+		CallServiceSync(MS_GC_EVENT,SESSION_OFFLINE,reinterpret_cast<LPARAM>(&gce));
+	}
+}
+*/
+
+int FacebookProto::OnGCMenuHook(WPARAM, LPARAM lParam)
+{
+	GCMENUITEMS *gcmi= (GCMENUITEMS*) lParam;
+
+	if (gcmi == NULL || _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 }
+		};
+		gcmi->nItems = SIZEOF(Items);
+		gcmi->Item = (gc_item*)Items;
+	}
+	else if (gcmi->Type == MENU_ON_NICKLIST)
+	{
+		if (!_stricmp(facy.self_.user_id.c_str(), _T2A(gcmi->pszUID)))
+		{
+			/*static const struct gc_item Items[] =
+			{
+				{ LPGENT("User &details"), 10, MENU_ITEM, FALSE },
+				{ LPGENT("User &history"), 20, MENU_ITEM, FALSE },
+				{ _T(""), 100, MENU_SEPARATOR, FALSE },
+				{ LPGENT("&Leave chat session"), 110, MENU_ITEM, FALSE }
+			};
+			gcmi->nItems = SIZEOF(Items);
+			gcmi->Item = (gc_item*)Items;*/
+			gcmi->nItems = 0;
+			gcmi->Item = NULL;
+		}
+		else
+		{
+			static const struct gc_item Items[] =
+			{
+				{ LPGENT("User &details"), 10, MENU_ITEM, FALSE },
+				{ LPGENT("User &history"), 20, MENU_ITEM, FALSE }
+			};
+			gcmi->nItems = SIZEOF(Items);
+			gcmi->Item = (gc_item*)Items;
+		}
+	}
+
+	return 0;
+}
+
+bool FacebookProto::IsSpecialChatRoom(MCONTACT hContact) {
+	if (!isChatRoom(hContact))
+		return false;
+
+	ptrT idT(getTStringA(hContact, "ChatRoomID"));
+	return idT && !_tcscmp(idT, _T(FACEBOOK_NOTIFICATIONS_CHATROOM));
+}
+
+void FacebookProto::PrepareNotificationsChatRoom() {
+	if (!getBool(FACEBOOK_KEY_NOTIFICATIONS_CHATROOM, DEFAULT_NOTIFICATIONS_CHATROOM))
+		return;
+
+	// Prepare notifications chatroom if not exists
+	TCHAR *gidT = _T(FACEBOOK_NOTIFICATIONS_CHATROOM);
+
+	MCONTACT hNotificationsChatRoom = ChatIDToHContact(gidT);
+	if (hNotificationsChatRoom == NULL || getDword(hNotificationsChatRoom, "Status", ID_STATUS_OFFLINE) != ID_STATUS_ONLINE) {
+		TCHAR nameT[200];
+		mir_sntprintf(nameT, SIZEOF(nameT), _T("%s: %s"), m_tszUserName, TranslateT("Notifications"));
+
+		// Create the group chat session
+		GCSESSION gcw = { sizeof(gcw) };
+		gcw.iType = GCW_PRIVMESS;
+		gcw.ptszID = gidT;
+		gcw.pszModule = m_szModuleName;
+		gcw.ptszName = nameT;
+		CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
+
+		// Send setting events
+		GCDEST gcd = { m_szModuleName, gidT, GC_EVENT_CONTROL };
+		GCEVENT gce = { sizeof(gce), &gcd };
+		gce.time = ::time(NULL);
+
+		CallServiceSync(MS_GC_EVENT, WINDOW_HIDDEN, reinterpret_cast<LPARAM>(&gce));
+		CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, reinterpret_cast<LPARAM>(&gce));
+	}
+}
+
+void FacebookProto::UpdateNotificationsChatRoom(facebook_notification *notification) {
+	if (!getBool(FACEBOOK_KEY_NOTIFICATIONS_CHATROOM, DEFAULT_NOTIFICATIONS_CHATROOM))
+		return;
+
+	std::stringstream text;
+	text << notification->text << "\n\n" << PrepareUrl(notification->link);
+
+	std::string message = text.str();
+	utils::text::replace_all(&message, "%", "%%");
+
+	ptrT idT(mir_tstrdup(_T(FACEBOOK_NOTIFICATIONS_CHATROOM)));
+	ptrT messageT(mir_a2t_cp(message.c_str(), CP_UTF8));
+
+	GCDEST gcd = { m_szModuleName, _T(FACEBOOK_NOTIFICATIONS_CHATROOM), GC_EVENT_MESSAGE };
+	GCEVENT gce = { sizeof(gce), &gcd };
+	gce.ptszText = messageT;
+	gce.time = notification->time ? notification->time : ::time(NULL);
+	gce.bIsMe = false;
+	gce.dwFlags |= GCEF_ADDTOLOG;
+	gce.ptszNick = TranslateT("Notifications");
+	gce.ptszUID = idT;
+
+	CallServiceSync(MS_GC_EVENT, 0, reinterpret_cast<LPARAM>(&gce));
 }
\ No newline at end of file
diff --git a/protocols/FacebookRM/src/contacts.cpp b/protocols/FacebookRM/src/contacts.cpp
index 5da990e616..61acec9b56 100644
--- a/protocols/FacebookRM/src/contacts.cpp
+++ b/protocols/FacebookRM/src/contacts.cpp
@@ -326,7 +326,7 @@ void FacebookProto::LoadChatInfo(facebook_chatroom *fbc)
 
 }
 
-MCONTACT FacebookProto::AddToContactList(facebook_user* fbu, ContactType type, bool force_add)
+MCONTACT FacebookProto::AddToContactList(facebook_user* fbu, ContactType type, bool force_add, bool add_temporarily)
 {
 	// First, check if this contact exists (and if does, just return it)
 	if (!force_add) {
@@ -347,6 +347,11 @@ MCONTACT FacebookProto::AddToContactList(facebook_user* fbu, ContactType type, b
 	// If we have some contact, we'll save its data
 	if (hContact) {
 		// Save these values only when adding new contact, not when updating existing
+		if (add_temporarily) {
+			db_set_b(hContact, "Clist", "Hidden", 1);
+			db_set_b(hContact, "Clist", "NotOnList", 1);
+		}
+
 		setString(hContact, FACEBOOK_KEY_ID, fbu->user_id.c_str());
 
 		std::string homepage = FACEBOOK_URL_PROFILE + fbu->user_id;
diff --git a/protocols/FacebookRM/src/proto.cpp b/protocols/FacebookRM/src/proto.cpp
index 463b7c1e89..ec8ee11424 100644
--- a/protocols/FacebookRM/src/proto.cpp
+++ b/protocols/FacebookRM/src/proto.cpp
@@ -288,16 +288,13 @@ MCONTACT FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
 		return NULL;
 	}
 
-	MCONTACT hContact = AddToContactList(&fbu, CONTACT_NONE);
-	if (hContact) {
-		if (flags & PALF_TEMPORARY) {
-			db_set_b(hContact, "Clist", "Hidden", 1);
-			db_set_b(hContact, "Clist", "NotOnList", 1);
-		}
-		else if (db_get_b(hContact, "CList", "NotOnList", 0)) {
-			db_unset(hContact, "CList", "Hidden");
-			db_unset(hContact, "CList", "NotOnList");
-		}
+	bool add_temporarily = (flags & PALF_TEMPORARY);
+	MCONTACT hContact = AddToContactList(&fbu, CONTACT_NONE, false, add_temporarily);
+
+	// Reset NotOnList flag if present and we're adding this contact not temporarily
+	if (hContact && !add_temporarily && db_get_b(hContact, "CList", "NotOnList", 0)) {
+		db_unset(hContact, "CList", "Hidden");
+		db_unset(hContact, "CList", "NotOnList");
 	}
 
 	return hContact;
diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h
index 8f85582ca6..e0c95cf8f9 100644
--- a/protocols/FacebookRM/src/proto.h
+++ b/protocols/FacebookRM/src/proto.h
@@ -204,7 +204,7 @@ public:
 	MCONTACT	ChatIDToHContact(const std::tstring&);
 	std::string	ThreadIDToContactID(const std::string&);
 	void		LoadContactInfo(facebook_user* fbu);
-	MCONTACT	AddToContactList(facebook_user*, ContactType type, bool force_add = false);
+	MCONTACT	AddToContactList(facebook_user*, ContactType type, bool force_add = false, bool add_temporarily = false);
 	void		SetAllContactStatuses(int status);
 	MCONTACT	HContactFromAuthEvent(HANDLE hEvent);
 	void		StartTyping(MCONTACT hContact);
-- 
cgit v1.2.3