diff options
Diffstat (limited to 'protocols')
| -rw-r--r-- | protocols/FacebookRM/src/client.h | 2 | ||||
| -rw-r--r-- | protocols/FacebookRM/src/process.cpp | 112 | ||||
| -rw-r--r-- | protocols/FacebookRM/src/proto.cpp | 1807 | ||||
| -rw-r--r-- | protocols/FacebookRM/src/proto.h | 486 | 
4 files changed, 1272 insertions, 1135 deletions
diff --git a/protocols/FacebookRM/src/client.h b/protocols/FacebookRM/src/client.h index b17cc8b202..5189e1be68 100644 --- a/protocols/FacebookRM/src/client.h +++ b/protocols/FacebookRM/src/client.h @@ -104,6 +104,8 @@ public:  	std::string get_server_type();
  	std::string get_privacy_type();
 +	std::map<MCONTACT, bool> ignore_read;
 +
  	char*   load_cookies();
  	void    store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headers_count);
  	void    clear_cookies();
 diff --git a/protocols/FacebookRM/src/process.cpp b/protocols/FacebookRM/src/process.cpp index 3fc432943e..6f08cbed47 100644 --- a/protocols/FacebookRM/src/process.cpp +++ b/protocols/FacebookRM/src/process.cpp @@ -416,6 +416,110 @@ void FacebookProto::ProcessUnreadMessage(void *p)  	}  } +void FacebookProto::LoadLastMessages(void *p) +{ +	if (p == NULL) +		return; + +	facy.handle_entry("LoadLastMessages"); + +	MCONTACT hContact = *(MCONTACT*)p; +	delete (MCONTACT*)p; + +	std::string data = "client=mercury"; +	data += "&__user=" + facy.self_.user_id; +	data += "&fb_dtsg=" + (facy.dtsg_.length() ? facy.dtsg_ : "0"); +	data += "&__a=1&__dyn=&__req=&ttstamp=0"; + +	bool isChat = isChatRoom(hContact); + +	ptrA item_id(getStringA(hContact, isChat ? FACEBOOK_KEY_TID : FACEBOOK_KEY_ID)); +	if (item_id == NULL) { +		debugLogA("!!!!! LoadLastMessages: Contact has no TID/ID"); +		return; +	} + +	std::string id = utils::url::encode(std::string(item_id)); +	std::string type = isChat ? "thread_ids" : "user_ids"; + +	// request messages from thread +	data += "&messages[" + type + "][" + id; +	data += "][offset]=0"; +	data += "&messages[" + type + "][" + id; +	data += "][limit]=30"; + +	// request info about thread +	data += "&threads[" + type + "][0]=" + id; + +	http::response resp = facy.flap(REQUEST_THREAD_INFO, &data); + +	if (resp.code != HTTP_CODE_OK || resp.data.empty()) { +		facy.handle_error("LoadLastMessages"); +		return; +	} + +	// Temporarily disable marking messages as read for this contact +	facy.ignore_read.insert(std::make_pair(hContact, true)); + +CODE_BLOCK_TRY + +	std::vector<facebook_message*> messages; +	std::map<std::string, facebook_chatroom*> chatrooms; + +	facebook_json_parser* p = new facebook_json_parser(this); +	p->parse_thread_messages(&resp.data, &messages, &chatrooms, false, false, 20); +	delete p; + +	for (std::map<std::string, facebook_chatroom*>::iterator it = chatrooms.begin(); it != chatrooms.end();) { + +		facebook_chatroom *room = it->second; +		MCONTACT hChatContact = NULL; +		if (GetChatUsers(room->thread_id.c_str()) == NULL) { +			AddChat(room->thread_id.c_str(), room->chat_name.c_str()); +			hChatContact = ChatIDToHContact(room->thread_id); +			// Set thread id (TID) for later +			setTString(hChatContact, FACEBOOK_KEY_TID, room->thread_id.c_str()); + +			for (std::map<std::string, std::string>::iterator jt = room->participants.begin(); jt != room->participants.end();) { +				AddChatContact(room->thread_id.c_str(), jt->first.c_str(), jt->second.c_str()); +				++jt; +			} +		} + +		if (!hChatContact) +			hChatContact = ChatIDToHContact(room->thread_id); + +		ForkThread(&FacebookProto::ReadMessageWorker, (void*)hChatContact); + +		delete it->second; +		it = chatrooms.erase(it); +	} +	chatrooms.clear(); + +	bool local_timestamp = getBool(FACEBOOK_KEY_LOCAL_TIMESTAMP_UNREAD, 0); +	ReceiveMessages(messages, local_timestamp, true); + +	debugLogA("***** Thread messages processed"); + +CODE_BLOCK_CATCH + +	debugLogA("***** Error processing thread messages: %s", e.what()); + +CODE_BLOCK_END + +	facy.handle_success("LoadLastMessages"); + +	// Enable marking messages as read for this contact +	std::map<MCONTACT, bool>::iterator it = facy.ignore_read.find(hContact); +	if (it != facy.ignore_read.end()) { +		it->second = false; +	} +	 +	// And force mark read +	OnDbEventRead(hContact, NULL); +} + +  void FacebookProto::ReceiveMessages(std::vector<facebook_message*> messages, bool local_timestamp, bool check_duplicates)  {  	bool naseemsSpamMode = getBool(FACEBOOK_KEY_NASEEMS_SPAM_MODE, false); @@ -535,7 +639,10 @@ void FacebookProto::ReceiveMessages(std::vector<facebook_message*> messages, boo  			if (messages[i]->isIncoming) {  				PROTORECVEVENT recv = { 0 }; -				recv.flags = PREF_UTF; +				recv.flags = PREF_UTF | PREF_CREATEREAD; +				if (!messages[i]->isUnread) +					recv.flags |= PREF_CREATEREAD; +  				recv.szMessage = const_cast<char*>(messages[i]->message_text.c_str());  				recv.timestamp = timestamp;  				ProtoChainRecvMsg(hContact, &recv); @@ -544,6 +651,9 @@ void FacebookProto::ReceiveMessages(std::vector<facebook_message*> messages, boo  				dbei.cbSize = sizeof(dbei);  				dbei.eventType = EVENTTYPE_MESSAGE;  				dbei.flags = DBEF_SENT | DBEF_UTF; +				//if (!messages[i]->isUnread) // sent messages are always read +					dbei.flags |= DBEF_READ; +  				dbei.szModule = m_szModuleName;  				dbei.timestamp = timestamp;  				dbei.cbBlob = (DWORD)messages[i]->message_text.length() + 1; diff --git a/protocols/FacebookRM/src/proto.cpp b/protocols/FacebookRM/src/proto.cpp index c4d9f6e79e..4880f4c7c2 100644 --- a/protocols/FacebookRM/src/proto.cpp +++ b/protocols/FacebookRM/src/proto.cpp @@ -1,892 +1,915 @@ -/*
 -
 -Facebook plugin for Miranda Instant Messenger
 -_____________________________________________
 -
 -Copyright © 2009-11 Michal Zelinka, 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"
 -
 -FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username) :
 -	PROTO<FacebookProto>(proto_name, username)
 -{
 -	facy.parent = this;
 -
 -	signon_lock_ = CreateMutex(NULL, FALSE, NULL);
 -	avatar_lock_ = CreateMutex(NULL, FALSE, NULL);
 -	log_lock_ = CreateMutex(NULL, FALSE, NULL);
 -	update_loop_lock_ = CreateEvent(NULL, FALSE, FALSE, NULL);
 -	facy.buddies_lock_ = CreateMutex(NULL, FALSE, NULL);
 -	facy.send_message_lock_ = CreateMutex(NULL, FALSE, NULL);
 -	facy.fcb_conn_lock_ = CreateMutex(NULL, FALSE, NULL);
 -
 -	CreateProtoService(PS_CREATEACCMGRUI, &FacebookProto::SvcCreateAccMgrUI);
 -	CreateProtoService(PS_GETMYAWAYMSG,   &FacebookProto::GetMyAwayMsg);
 -	CreateProtoService(PS_GETMYAVATART,   &FacebookProto::GetMyAvatar);
 -	CreateProtoService(PS_GETAVATARINFOT, &FacebookProto::GetAvatarInfo);
 -	CreateProtoService(PS_GETAVATARCAPS,  &FacebookProto::GetAvatarCaps);
 -	CreateProtoService(PS_GETUNREADEMAILCOUNT, &FacebookProto::GetNotificationsCount);
 -
 -	CreateProtoService(PS_JOINCHAT,  &FacebookProto::OnJoinChat);
 -	CreateProtoService(PS_LEAVECHAT, &FacebookProto::OnLeaveChat);
 -
 -	CreateProtoService("/Mind", &FacebookProto::OnMind);
 -
 -	HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &FacebookProto::OnBuildStatusMenu);
 -	HookProtoEvent(ME_OPT_INITIALISE,           &FacebookProto::OnOptionsInit);
 -	HookProtoEvent(ME_IDLE_CHANGED,             &FacebookProto::OnIdleChanged);
 -	HookProtoEvent(ME_TTB_MODULELOADED,         &FacebookProto::OnToolbarInit);
 -	HookProtoEvent(ME_GC_EVENT,					&FacebookProto::OnGCEvent);
 -	HookProtoEvent(ME_GC_BUILDMENU,				&FacebookProto::OnGCMenuHook);
 -	HookProtoEvent(ME_DB_EVENT_MARKED_READ,		&FacebookProto::OnDbEventRead);
 -
 -	db_set_resident(m_szModuleName, "Status");
 -	db_set_resident(m_szModuleName, "IdleTS");
 -
 -	InitHotkeys();
 -	InitPopups();
 -	InitSounds();
 -
 -	// Create standard network connection
 -	TCHAR descr[512];
 -	NETLIBUSER nlu = {sizeof(nlu)};
 -	nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_TCHAR;
 -	nlu.szSettingsModule = m_szModuleName;
 -	mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), m_tszUserName);
 -	nlu.ptszDescriptiveName = descr;
 -	m_hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
 -	if (m_hNetlibUser == NULL)
 -		MessageBox(NULL, TranslateT("Unable to get Netlib connection for Facebook"), m_tszUserName, MB_OK);
 -
 -	facy.set_handle(m_hNetlibUser);	
 -
 -	// Set all contacts offline -- in case we crashed
 -	SetAllContactStatuses(ID_STATUS_OFFLINE);
 -}
 -
 -FacebookProto::~FacebookProto()
 -{
 -	// Uninit popup classes
 -	for (std::vector<HANDLE>::size_type i = 0; i < popupClasses.size(); i++)
 -		Popup_UnregisterClass(popupClasses[i]);
 -	popupClasses.clear();	
 -	
 -	Netlib_CloseHandle(m_hNetlibUser);
 -
 -	WaitForSingleObject(signon_lock_, IGNORE);
 -	WaitForSingleObject(avatar_lock_, IGNORE);
 -	WaitForSingleObject(log_lock_, IGNORE);
 -	WaitForSingleObject(facy.buddies_lock_, IGNORE);
 -	WaitForSingleObject(facy.send_message_lock_, IGNORE);
 -
 -	CloseHandle(signon_lock_);
 -	CloseHandle(avatar_lock_);
 -	CloseHandle(log_lock_);
 -	CloseHandle(update_loop_lock_);
 -	CloseHandle(facy.buddies_lock_);
 -	CloseHandle(facy.send_message_lock_);
 -	CloseHandle(facy.fcb_conn_lock_);
 -}
 -
 -//////////////////////////////////////////////////////////////////////////////
 -
 -DWORD_PTR FacebookProto::GetCaps(int type, MCONTACT hContact)
 -{
 -	switch(type)
 -	{
 -	case PFLAGNUM_1:
 -	{
 -		DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | /*PF1_ADDED |*/ PF1_BASICSEARCH | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_ADDSEARCHRES; // | PF1_VISLIST | PF1_INVISLIST;
 -
 -		if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, 0))
 -			return flags |= PF1_MODEMSG;
 -		else
 -			return flags |= PF1_MODEMSGRECV;
 -	}
 -	case PFLAGNUM_2:
 -		return PF2_ONLINE | PF2_INVISIBLE | PF2_ONTHEPHONE | PF2_IDLE; // | PF2_SHORTAWAY;
 -	case PFLAGNUM_3:
 -		if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, 0))
 -			return PF2_ONLINE; // | PF2_SHORTAWAY;
 -		else
 -			return 0;
 -	case PFLAGNUM_4:
 -		return PF4_NOCUSTOMAUTH | PF4_FORCEADDED | PF4_IMSENDUTF | PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE;
 -	case PFLAGNUM_5:
 -		return PF2_ONTHEPHONE;
 -	case PFLAG_MAXLENOFMESSAGE:
 -		return FACEBOOK_MESSAGE_LIMIT;
 -	case PFLAG_UNIQUEIDTEXT:
 -		return (DWORD_PTR) "Facebook ID";
 -	case PFLAG_UNIQUEIDSETTING:
 -		return (DWORD_PTR) FACEBOOK_KEY_ID;
 -	}
 -	return 0;
 -}
 -
 -//////////////////////////////////////////////////////////////////////////////
 -
 -int FacebookProto::SetStatus(int new_status)
 -{
 -	debugLogA("===== Beginning SetStatus process");
 -
 -	// Routing statuses not supported by Facebook
 -	switch (new_status)
 -	{
 -	case ID_STATUS_INVISIBLE:
 -	case ID_STATUS_OFFLINE:
 -		m_iDesiredStatus = new_status;
 -		break;
 -
 -	case ID_STATUS_IDLE:
 -	default:
 -		m_iDesiredStatus = ID_STATUS_INVISIBLE;
 -		if (getByte(FACEBOOK_KEY_MAP_STATUSES, DEFAULT_MAP_STATUSES))
 -			break;
 -	case ID_STATUS_ONLINE:
 -	case ID_STATUS_FREECHAT:
 -		m_iDesiredStatus = ID_STATUS_ONLINE;
 -		break;
 -	}
 -
 -	if (new_status != ID_STATUS_OFFLINE && m_iStatus == ID_STATUS_CONNECTING) {
 -		debugLogA("===== Status is already connecting, no change");
 -		return 0;
 -	}
 -
 -	if (m_iStatus == m_iDesiredStatus) {
 -		debugLogA("===== Statuses are same, no change");
 -		return 0;
 -	}
 -
 -	ForkThread(&FacebookProto::ChangeStatus, this);
 -	return 0;
 -}
 -
 -int FacebookProto::SetAwayMsg(int status, const PROTOCHAR *msg)
 -{
 -	if (!msg) {
 -		last_status_msg_.clear();
 -		return 0;
 -	}
 -
 -	char *narrow = mir_utf8encodeT(msg);
 -	if (last_status_msg_ != narrow)
 -		last_status_msg_ = narrow;
 -	mir_free(narrow);
 -
 -	if (isOnline() && getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS))
 -		ForkThread(&FacebookProto::SetAwayMsgWorker, NULL);
 -
 -	return 0;
 -}
 -
 -void FacebookProto::SetAwayMsgWorker(void *p)
 -{
 -	if (p != NULL) {
 -		status_data *data = static_cast<status_data*>(p);
 -		facy.post_status(data);
 -		delete data;
 -	} else if (!last_status_msg_.empty()) {
 -		status_data data;
 -		data.text = last_status_msg_;
 -		data.privacy = facy.get_privacy_type();
 -		facy.post_status(&data);
 -	}
 -}
 -
 -HANDLE FacebookProto::SearchBasic(const PROTOCHAR* id)
 -{
 -	if (isOffline())
 -		return 0;
 -
 -	TCHAR *tid = mir_tstrdup(id);
 -	ForkThread(&FacebookProto::SearchIdAckThread, tid);
 -	return tid;
 -}
 -
 -HANDLE FacebookProto::SearchByEmail(const PROTOCHAR* email)
 -{
 -	if (isOffline())
 -		return 0;
 -
 -	TCHAR *temail = mir_tstrdup(email);
 -	ForkThread(&FacebookProto::SearchAckThread, temail);
 -	return temail;
 -}
 -
 -HANDLE FacebookProto::SearchByName(const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName)
 -{
 -	TCHAR arg[200];
 -	mir_sntprintf (arg, SIZEOF(arg), _T("%s %s %s"), nick, firstName, lastName);
 -	return SearchByEmail(arg); // Facebook is using one search method for everything (except IDs)
 -}
 -
 -MCONTACT FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
 -{
 -	ptrA id( mir_t2a_cp(psr->id, CP_UTF8));
 -	ptrA name( mir_t2a_cp(psr->firstName, CP_UTF8));
 -	ptrA surname( mir_t2a_cp(psr->lastName, CP_UTF8));
 -
 -	if (id == NULL)
 -		return NULL;
 -
 -	facebook_user fbu;
 -	fbu.user_id = id;
 -	if (name != NULL)
 -		fbu.real_name = name;
 -	if (surname != NULL) {
 -		fbu.real_name += " ";
 -		fbu.real_name += surname;
 -	}
 -
 -	if (fbu.user_id.find_first_not_of("0123456789") != std::string::npos) {
 -		MessageBox(0, TranslateT("Facebook ID must be numeric value."), m_tszUserName, MB_ICONERROR | MB_OK);
 -		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");
 -		}
 -	}
 -
 -	return hContact;
 -}
 -
 -int FacebookProto::AuthRequest(MCONTACT hContact,const PROTOCHAR *message)
 -{
 -	return RequestFriendship(hContact, NULL);
 -}
 -
 -int FacebookProto::Authorize(HANDLE hDbEvent)
 -{
 -	if (!hDbEvent || isOffline())
 -		return 1;
 -
 -	MCONTACT hContact = HContactFromAuthEvent(hDbEvent);
 -	if (hContact == INVALID_CONTACT_ID)
 -		return 1;
 -
 -	return ApproveFriendship(hContact, NULL);
 -}
 -
 -int FacebookProto::AuthDeny(HANDLE hDbEvent, const PROTOCHAR *reason)
 -{
 -	if (!hDbEvent || isOffline())
 -		return 1;
 -
 -	MCONTACT hContact = HContactFromAuthEvent(hDbEvent);
 -	if (hContact == INVALID_CONTACT_ID)
 -		return 1;
 -
 -	// TODO: hide from facebook requests list
 -
 -	if (db_get_b(hContact, "CList", "NotOnList", 0))
 -		CallService(MS_DB_CONTACT_DELETE, hContact, 0);
 -
 -	return 0;
 -}
 -
 -int FacebookProto::GetInfo(MCONTACT hContact, int infoType)
 -{
 -	ptrA user_id(getStringA(hContact, FACEBOOK_KEY_ID));
 -
 -	if (user_id == NULL)
 -		return 1;
 -	
 -	facebook_user fbu;
 -	fbu.user_id = user_id;
 -
 -	LoadContactInfo(&fbu);
 -
 -	// TODO: don't duplicate code this way, refactor all this userInfo loading
 -	// TODO: load more info about user (authorization state,...)
 -
 -	std::string homepage = FACEBOOK_URL_PROFILE + fbu.user_id;
 -	setString(hContact, "Homepage", homepage.c_str());
 -
 -	if (!fbu.real_name.empty()) {
 -		SaveName(hContact, &fbu);
 -	}
 -
 -	if (fbu.gender)
 -		setByte(hContact, "Gender", fbu.gender);
 -
 -	CheckAvatarChange(hContact, fbu.image_url);
 -
 -	return 1;
 -}
 -
 -//////////////////////////////////////////////////////////////////////////////
 -// SERVICES
 -
 -INT_PTR FacebookProto::GetMyAwayMsg(WPARAM wParam, LPARAM lParam)
 -{
 -	DBVARIANT dbv = { DBVT_TCHAR };
 -	if (!getTString("StatusMsg", &dbv) && lstrlen(dbv.ptszVal) != 0)
 -	{
 -		int res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_t2u(dbv.ptszVal) : (INT_PTR)mir_t2a(dbv.ptszVal);
 -		db_free(&dbv);
 -		return res;
 -	} else {
 -		return 0;
 -	}
 -}
 -
 -int FacebookProto::OnIdleChanged(WPARAM wParam, LPARAM lParam)
 -{
 -	if (m_iStatus == ID_STATUS_INVISIBLE || m_iStatus <= ID_STATUS_OFFLINE)
 -		return 0;
 -
 -	bool bIdle = (lParam & IDF_ISIDLE) != 0;
 -	bool bPrivacy = (lParam & IDF_PRIVACY) != 0;
 -
 -	if (facy.is_idle_ && !bIdle)
 -	{
 -		facy.is_idle_ = false;
 -		SetStatus(m_iDesiredStatus);
 -	}
 -	else if (!facy.is_idle_ && bIdle && !bPrivacy && m_iDesiredStatus != ID_STATUS_INVISIBLE)
 -	{
 -		facy.is_idle_ = true;
 -		SetStatus(ID_STATUS_IDLE);
 -	}
 -
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::GetNotificationsCount(WPARAM wParam, LPARAM lParam)
 -{
 -	if (isOffline())
 -		return 0;
 -
 -	return facy.notifications.size();
 -}
 -
 -//////////////////////////////////////////////////////////////////////////////
 -
 -int FacebookProto::OnEvent(PROTOEVENTTYPE event, WPARAM wParam, LPARAM lParam)
 -{
 -	switch(event)
 -	{
 -	case EV_PROTO_ONLOAD:
 -		return OnModulesLoaded(wParam,lParam);
 -
 -	case EV_PROTO_ONEXIT:
 -		return OnPreShutdown(wParam,lParam);
 -
 -	case EV_PROTO_ONOPTIONS:
 -		return OnOptionsInit(wParam,lParam);
 -
 -	case EV_PROTO_ONCONTACTDELETED:
 - 		return OnContactDeleted(wParam,lParam);
 -	}
 -
 -	return 1;
 -}
 -
 -//////////////////////////////////////////////////////////////////////////////
 -// EVENTS
 -
 -INT_PTR FacebookProto::SvcCreateAccMgrUI(WPARAM wParam, LPARAM lParam)
 -{
 -	return (int)CreateDialogParam(g_hInstance,MAKEINTRESOURCE(IDD_FACEBOOKACCOUNT),
 -		 (HWND)lParam, FBAccountProc, (LPARAM)this);
 -}
 -
 -int FacebookProto::OnModulesLoaded(WPARAM wParam, LPARAM lParam)
 -{
 -	// Register group chat
 -	GCREGISTER gcr = {sizeof(gcr)};
 -	gcr.dwFlags = 0; //GC_ACKMSG;
 -	gcr.pszModule = m_szModuleName;
 -	gcr.ptszDispName = m_tszUserName;
 -	gcr.iMaxText = FACEBOOK_MESSAGE_LIMIT;
 -	gcr.nColors = 0;
 -	gcr.pColors = NULL;
 -	CallService(MS_GC_REGISTER,0,reinterpret_cast<LPARAM>(&gcr));
 -
 -	return 0;
 -}
 -
 -int FacebookProto::OnPreShutdown(WPARAM wParam, LPARAM lParam)
 -{
 -	SetStatus(ID_STATUS_OFFLINE);
 -	return 0;
 -}
 -
 -int FacebookProto::OnOptionsInit(WPARAM wParam, LPARAM lParam)
 -{
 -	OPTIONSDIALOGPAGE odp = {sizeof(odp)};
 -	odp.hInstance   = g_hInstance;
 -	odp.ptszTitle   = m_tszUserName;
 -	odp.dwInitParam = LPARAM(this);
 -	odp.flags       = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_DONTTRANSLATE;
 -
 -	odp.position    = 271828;
 -	odp.ptszGroup   = LPGENT("Network");
 -	odp.ptszTab     = LPGENT("Account");
 -	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
 -	odp.pfnDlgProc  = FBOptionsProc;
 -	Options_AddPage(wParam, &odp);
 -
 -	odp.position    = 271829;
 -	odp.ptszTab     = LPGENT("Events");
 -	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_EVENTS);
 -	odp.pfnDlgProc  = FBEventsProc;
 -	Options_AddPage(wParam, &odp);
 -
 -	odp.position    = 271830;
 -	odp.ptszTab     = LPGENT("Advanced");
 -	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_ADVANCED);
 -	odp.pfnDlgProc  = FBOptionsAdvancedProc;
 -	Options_AddPage(wParam, &odp);
 -	return 0;
 -}
 -
 -int FacebookProto::OnToolbarInit(WPARAM, LPARAM)
 -{
 -	TTBButton ttb = { sizeof(ttb) };
 -	ttb.dwFlags = TTBBF_SHOWTOOLTIP | TTBBF_VISIBLE;
 -
 -	char service[100];
 -	mir_snprintf(service, sizeof(service), "%s%s", m_szModuleName, "/Mind");
 -
 -	ttb.pszService = service;
 -	ttb.pszTooltipUp = ttb.name = LPGEN("Share status...");
 -	ttb.hIconHandleUp = Skin_GetIconByHandle(GetIconHandle("mind"));
 -	TopToolbar_AddButton(&ttb);
 -
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::OnMind(WPARAM wParam, LPARAM lParam)
 -{
 -	if (!isOnline())
 -		return 1;
 -
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	wall_data *wall = new wall_data();
 -	wall->user_id = ptrA(getStringA(hContact, FACEBOOK_KEY_ID));
 -	wall->isPage = false;
 -	if (wall->user_id == facy.self_.user_id) {
 -		wall->title = _tcsdup(TranslateT("Own wall"));
 -	} else
 -		wall->title = getTStringA(hContact, FACEBOOK_KEY_NICK);
 -
 -	post_status_data *data = new post_status_data(this, wall);
 -
 -	if (wall->user_id == facy.self_.user_id) {
 -		for (std::map<std::string, std::string>::iterator iter = facy.pages.begin(); iter != facy.pages.end(); ++iter) {
 -			data->walls.push_back(new wall_data(iter->first, mir_utf8decodeT(iter->second.c_str()), true));
 -		}
 -	}
 -		
 -	HWND hDlg = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_MIND), (HWND)0, FBMindProc, reinterpret_cast<LPARAM>(data));
 -	ShowWindow(hDlg, SW_SHOW);
 -
 -	return 0;
 -}
 -
 -int FacebookProto::OnDbEventRead(WPARAM contactID, LPARAM dbei)
 -{
 -	if ((IsMyContact(contactID, true)) && !isOffline()) {
 -		ForkThread(&FacebookProto::ReadMessageWorker, (void*)contactID);
 -	}
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::CheckNewsfeeds(WPARAM, LPARAM)
 -{
 -	if (!isOffline()) {
 -		facy.client_notify(TranslateT("Loading newsfeeds..."));
 -		facy.last_feeds_update_ = 0;
 -		ForkThread(&FacebookProto::ProcessFeeds, NULL);
 -	}
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::CheckFriendRequests(WPARAM, LPARAM)
 -{
 -	if (!isOffline()) {
 -		facy.client_notify(TranslateT("Checking friend requests..."));
 -		ProcessFriendRequests(NULL);
 -	}
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::RefreshBuddyList(WPARAM, LPARAM)
 -{
 -	if (!isOffline()) {
 -		facy.client_notify(TranslateT("Refreshing buddy list..."));
 -		ForkThread(&FacebookProto::ProcessBuddyList, NULL);
 -	}
 -	return 0;
 -}
 -
 -
 -INT_PTR FacebookProto::VisitProfile(WPARAM wParam,LPARAM lParam)
 -{
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	std::string url = FACEBOOK_URL_PROFILE;
 -
 -	ptrA val(getStringA(hContact, "Homepage"));
 -	if (val != NULL) {
 -		// Homepage link already present, get it
 -		url = val;
 -	} else {
 -		// No homepage link, create and save it
 -		val = getStringA(hContact, FACEBOOK_KEY_ID);
 -		if (val != NULL) {
 -			url += val;
 -			setString(hContact, "Homepage", url.c_str());
 -		}
 -	}
 -
 -	OpenUrl(url);
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::VisitFriendship(WPARAM wParam,LPARAM lParam)
 -{
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	if (wParam == 0 || !IsMyContact(hContact))
 -		return 1;
 -
 -	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID));
 -
 -	std::string url = FACEBOOK_URL_PROFILE;
 -	url += facy.self_.user_id;
 -	url += "&and=" + std::string(id);
 -
 -	OpenUrl(url);
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::VisitConversation(WPARAM wParam, LPARAM lParam)
 -{
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	if (wParam == 0 || !IsMyContact(hContact, true))
 -		return 1;
 -
 -	std::string url = FACEBOOK_URL_CONVERSATION;
 -
 -	if (isChatRoom(hContact)) {
 -		url += "conversation-";
 -		url += ptrA(getStringA(hContact, FACEBOOK_KEY_TID));
 -	} else {
 -		url += ptrA(getStringA(hContact, FACEBOOK_KEY_ID));
 -	}
 -
 -	OpenUrl(url);
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::VisitNotifications(WPARAM wParam, LPARAM lParam)
 -{
 -	OpenUrl(FACEBOOK_URL_NOTIFICATIONS);
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::Poke(WPARAM wParam,LPARAM lParam)
 -{
 -	if (wParam == NULL || isOffline())
 -		return 1;
 -
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID));
 -	if (id == NULL)
 -		return 1;
 -
 -	ForkThread(&FacebookProto::SendPokeWorker, new std::string(id));
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::CancelFriendship(WPARAM wParam,LPARAM lParam)
 -{
 -	if (wParam == NULL || isOffline())
 -		return 1;
 -
 -	bool deleting = (lParam == 1);
 -
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	// Ignore groupchats and, if deleting, also not-friends
 -	if (isChatRoom(hContact) || (deleting && getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, 0) != CONTACT_FRIEND))
 -		return 0;
 -
 -	ptrT tname(getTStringA(hContact, FACEBOOK_KEY_NICK));
 -	if (tname == NULL)
 -		tname = getTStringA(hContact, FACEBOOK_KEY_ID);
 -
 -	TCHAR tstr[256];
 -	mir_sntprintf(tstr,SIZEOF(tstr),TranslateT("Do you want to cancel your friendship with '%s'?"), tname);
 -	if (MessageBox(0, tstr, m_tszUserName, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2) == IDYES) {
 -
 -		ptrA id(getStringA(hContact, FACEBOOK_KEY_ID));
 -		if (id == NULL)
 -			return 1;
 -
 -		std::string *val = new std::string(id);
 -
 -		if (deleting) {
 -			facebook_user *fbu = facy.buddies.find(*val);
 -			if (fbu != NULL)
 -				fbu->handle = NULL;
 -		}
 -
 -		ForkThread(&FacebookProto::DeleteContactFromServer, val);
 -	}
 -
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::RequestFriendship(WPARAM wParam,LPARAM lParam)
 -{
 -	if (wParam == NULL || isOffline())
 -		return 1;
 -
 -	MCONTACT hContact = MCONTACT(wParam);
 -
 -	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID));
 -	if (id == NULL)
 -		return 1;
 -
 -	ForkThread(&FacebookProto::AddContactToServer, new std::string(id));
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::ApproveFriendship(WPARAM wParam,LPARAM lParam)
 -{
 -	if (wParam == NULL || isOffline())
 -		return 1;
 -
 -	HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam));
 -
 -	ForkThread(&FacebookProto::ApproveContactToServer, hContact);
 -	return 0;
 -}
 -
 -INT_PTR FacebookProto::OnCancelFriendshipRequest(WPARAM wParam,LPARAM lParam)
 -{
 -	if (wParam == NULL || isOffline())
 -		return 1;
 -
 -	HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam));
 -
 -	ForkThread(&FacebookProto::CancelFriendsRequest, hContact);
 -	return 0;
 -}
 -
 -MCONTACT FacebookProto::HContactFromAuthEvent(HANDLE hEvent)
 -{
 -	DWORD body[2];
 -	DBEVENTINFO dbei = { sizeof(dbei) };
 -	dbei.cbBlob = sizeof(DWORD)*2;
 -	dbei.pBlob = (PBYTE)&body;
 -
 -	if (db_event_get(hEvent, &dbei))
 -		return INVALID_CONTACT_ID;
 -
 -	if (dbei.eventType != EVENTTYPE_AUTHREQUEST)
 -		return INVALID_CONTACT_ID;
 -
 -	if (strcmp(dbei.szModule, m_szModuleName))
 -		return INVALID_CONTACT_ID;
 -
 -	return DbGetAuthEventContact(&dbei);
 -}
 -
 -void FacebookProto::OpenUrl(std::string url)
 -{
 -	std::string::size_type pos = url.find(FACEBOOK_SERVER_DOMAIN);
 -	bool isFacebookUrl = (pos != std::string::npos);
 -	bool isRelativeUrl = (url.substr(0, 4) != "http");
 -
 -	if (isFacebookUrl || isRelativeUrl) {
 -
 -		// Make realtive url
 -		if (!isRelativeUrl) {
 -			url = url.substr(pos + strlen(FACEBOOK_SERVER_DOMAIN));
 -
 -			// Strip eventual port
 -			pos = url.find("/");
 -			if (pos != std::string::npos && pos != 0)
 -				url = url.substr(pos);
 -		}
 -
 -		// Make absolute url
 -		bool useHttps = getByte(FACEBOOK_KEY_FORCE_HTTPS, 1) > 0;
 -		url = (useHttps ? HTTP_PROTO_SECURE : HTTP_PROTO_REGULAR) + facy.get_server_type() + url;
 -	}
 -
 -	ptrT data( mir_utf8decodeT(url.c_str()));
 -	CallService(MS_UTILS_OPENURL, (WPARAM)OUF_TCHAR, (LPARAM)data);
 -}
 -
 -void FacebookProto::ReadNotificationWorker(void *p)
 -{
 -	if (p == NULL)
 -		return;
 -
 -	std::string *id = static_cast<std::string*>(p);
 -
 -	std::string data = "seen=0&asyncSignal=&__dyn=&__req=a&alert_ids%5B0%5D=" + utils::url::encode(*id);
 -	data += "&fb_dtsg=" + (facy.dtsg_.length() ? facy.dtsg_ : "0");
 -	data += "&__user=" + facy.self_.user_id;
 -
 -	facy.flap(REQUEST_NOTIFICATIONS_READ, NULL, &data);
 -
 -	delete p;
 -}
 -
 -/**
 - * Popup processing callback
 - */
 -LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 -{
 -	switch(message)
 -	{
 -	case WM_COMMAND:
 -	case WM_CONTEXTMENU:
 -	{
 -		// Get the plugin data (we need the Popup service to do it)
 -		popup_data *data = (popup_data *)PUGetPluginData(hwnd);
 -		if (data != NULL) {
 -			if (!data->notification_id.empty())
 -				data->proto->ForkThread(&FacebookProto::ReadNotificationWorker, new std::string(data->notification_id));
 -
 -			if (message == WM_COMMAND && !data->url.empty())
 -				data->proto->OpenUrl(data->url);
 -		}
 -
 -		// After a click, destroy popup
 -		PUDeletePopup(hwnd);
 -	} break;
 -
 -	case UM_FREEPLUGINDATA:
 -	{
 -		// After close, free
 -		popup_data *data = (popup_data *)PUGetPluginData(hwnd);
 -		delete data;
 -	} return FALSE;
 -
 -	default:
 -		break;
 -	}
 -
 -	return DefWindowProc(hwnd, message, wParam, lParam);
 -};
 -
 -/**
 - * Popup classes initialization
 - */
 -void FacebookProto::InitPopups()
 -{		
 -	POPUPCLASS ppc = { sizeof(ppc) };
 -	ppc.flags = PCF_TCHAR;		
 -	ppc.PluginWindowProc = PopupDlgProc;
 -	ppc.lParam = APF_RETURN_HWND;
 -
 -	TCHAR desc[256];
 -	char name[256];
 -
 -	// Client
 -	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Client notifications"));
 -	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Client");
 -	ppc.ptszDescription = desc;
 -	ppc.pszName = name;
 -	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("facebook"));
 -	ppc.colorBack = RGB(191, 0, 0); // red
 -	ppc.colorText = RGB(255, 255, 255); // white
 -	ppc.iSeconds = 0;
 -	popupClasses.push_back(Popup_RegisterClass(&ppc));
 -
 -	// Newsfeeds
 -	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("News feeds"));
 -	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Newsfeed");
 -	ppc.ptszDescription = desc;
 -	ppc.pszName = name;
 -	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("newsfeed"));
 -	ppc.colorBack = RGB(255, 255, 255); // white
 -	ppc.colorText = RGB(0, 0, 0); // black
 -	ppc.iSeconds = 0;
 -	popupClasses.push_back(Popup_RegisterClass(&ppc));
 -
 -	// Notifications
 -	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Notifications"));
 -	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Notification");
 -	ppc.ptszDescription = desc;
 -	ppc.pszName = name;
 -	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("notification"));
 -	ppc.colorBack = RGB(59, 89, 152); // Facebook's blue
 -	ppc.colorText = RGB(255, 255, 255); // white
 -	ppc.iSeconds = 0;
 -	popupClasses.push_back(Popup_RegisterClass(&ppc));
 -
 -	// Others
 -	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Other events"));
 -	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Other");
 -	ppc.ptszDescription = desc;
 -	ppc.pszName = name;
 -	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("facebook"));
 -	ppc.colorBack = RGB(255, 255, 255); // white
 -	ppc.colorText = RGB(0, 0, 0); // black
 -	ppc.iSeconds = 0;
 -	popupClasses.push_back(Popup_RegisterClass(&ppc));
 -}
 -
 -/**
 - * Hotkeys initialiation
 - */
 -void FacebookProto::InitHotkeys()
 -{
 -	char module[512];
 -	mir_snprintf(module, sizeof(module), "%s/Mind", m_szModuleName);
 -
 -	HOTKEYDESC hkd = { sizeof(hkd) };
 -	hkd.dwFlags = HKD_TCHAR;
 -	hkd.ptszDescription = LPGENT("Show 'Share status' window");
 -	hkd.pszName = "ShowMindWnd";
 -	hkd.ptszSection = m_tszUserName;
 -	hkd.pszService = module;
 -	hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_EXT, 'F');
 -	Hotkey_Register(&hkd);
 -}
 -
 -/**
 - * Sounds initialization
 - */
 -void FacebookProto::InitSounds()
 -{
 -	SkinAddNewSoundExT("Notification", m_tszUserName, LPGENT("Notification"));
 -	SkinAddNewSoundExT("NewsFeed", m_tszUserName, LPGENT("News Feed"));
 -	SkinAddNewSoundExT("OtherEvent", m_tszUserName, LPGENT("Other Event"));
 -}
 +/* + +Facebook plugin for Miranda Instant Messenger +_____________________________________________ + +Copyright © 2009-11 Michal Zelinka, 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" + +FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username) : +	PROTO<FacebookProto>(proto_name, username) +{ +	facy.parent = this; + +	signon_lock_ = CreateMutex(NULL, FALSE, NULL); +	avatar_lock_ = CreateMutex(NULL, FALSE, NULL); +	log_lock_ = CreateMutex(NULL, FALSE, NULL); +	update_loop_lock_ = CreateEvent(NULL, FALSE, FALSE, NULL); +	facy.buddies_lock_ = CreateMutex(NULL, FALSE, NULL); +	facy.send_message_lock_ = CreateMutex(NULL, FALSE, NULL); +	facy.fcb_conn_lock_ = CreateMutex(NULL, FALSE, NULL); + +	CreateProtoService(PS_CREATEACCMGRUI, &FacebookProto::SvcCreateAccMgrUI); +	CreateProtoService(PS_GETMYAWAYMSG,   &FacebookProto::GetMyAwayMsg); +	CreateProtoService(PS_GETMYAVATART,   &FacebookProto::GetMyAvatar); +	CreateProtoService(PS_GETAVATARINFOT, &FacebookProto::GetAvatarInfo); +	CreateProtoService(PS_GETAVATARCAPS,  &FacebookProto::GetAvatarCaps); +	CreateProtoService(PS_GETUNREADEMAILCOUNT, &FacebookProto::GetNotificationsCount); + +	CreateProtoService(PS_JOINCHAT,  &FacebookProto::OnJoinChat); +	CreateProtoService(PS_LEAVECHAT, &FacebookProto::OnLeaveChat); + +	CreateProtoService("/Mind", &FacebookProto::OnMind); + +	HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &FacebookProto::OnBuildStatusMenu); +	HookProtoEvent(ME_OPT_INITIALISE,           &FacebookProto::OnOptionsInit); +	HookProtoEvent(ME_IDLE_CHANGED,             &FacebookProto::OnIdleChanged); +	HookProtoEvent(ME_TTB_MODULELOADED,         &FacebookProto::OnToolbarInit); +	HookProtoEvent(ME_GC_EVENT,					&FacebookProto::OnGCEvent); +	HookProtoEvent(ME_GC_BUILDMENU,				&FacebookProto::OnGCMenuHook); +	HookProtoEvent(ME_DB_EVENT_MARKED_READ,		&FacebookProto::OnDbEventRead); +	HookProtoEvent(ME_MSG_WINDOWEVENT,			&FacebookProto::OnProcessSrmmEvent); + +	db_set_resident(m_szModuleName, "Status"); +	db_set_resident(m_szModuleName, "IdleTS"); + +	InitHotkeys(); +	InitPopups(); +	InitSounds(); + +	// Create standard network connection +	TCHAR descr[512]; +	NETLIBUSER nlu = {sizeof(nlu)}; +	nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_TCHAR; +	nlu.szSettingsModule = m_szModuleName; +	mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), m_tszUserName); +	nlu.ptszDescriptiveName = descr; +	m_hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); +	if (m_hNetlibUser == NULL) +		MessageBox(NULL, TranslateT("Unable to get Netlib connection for Facebook"), m_tszUserName, MB_OK); + +	facy.set_handle(m_hNetlibUser);	 + +	// Set all contacts offline -- in case we crashed +	SetAllContactStatuses(ID_STATUS_OFFLINE); +} + +FacebookProto::~FacebookProto() +{ +	// Uninit popup classes +	for (std::vector<HANDLE>::size_type i = 0; i < popupClasses.size(); i++) +		Popup_UnregisterClass(popupClasses[i]); +	popupClasses.clear();	 +	 +	Netlib_CloseHandle(m_hNetlibUser); + +	WaitForSingleObject(signon_lock_, IGNORE); +	WaitForSingleObject(avatar_lock_, IGNORE); +	WaitForSingleObject(log_lock_, IGNORE); +	WaitForSingleObject(facy.buddies_lock_, IGNORE); +	WaitForSingleObject(facy.send_message_lock_, IGNORE); + +	CloseHandle(signon_lock_); +	CloseHandle(avatar_lock_); +	CloseHandle(log_lock_); +	CloseHandle(update_loop_lock_); +	CloseHandle(facy.buddies_lock_); +	CloseHandle(facy.send_message_lock_); +	CloseHandle(facy.fcb_conn_lock_); +} + +////////////////////////////////////////////////////////////////////////////// + +DWORD_PTR FacebookProto::GetCaps(int type, MCONTACT hContact) +{ +	switch(type) +	{ +	case PFLAGNUM_1: +	{ +		DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | /*PF1_ADDED |*/ PF1_BASICSEARCH | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_ADDSEARCHRES; // | PF1_VISLIST | PF1_INVISLIST; + +		if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, 0)) +			return flags |= PF1_MODEMSG; +		else +			return flags |= PF1_MODEMSGRECV; +	} +	case PFLAGNUM_2: +		return PF2_ONLINE | PF2_INVISIBLE | PF2_ONTHEPHONE | PF2_IDLE; // | PF2_SHORTAWAY; +	case PFLAGNUM_3: +		if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, 0)) +			return PF2_ONLINE; // | PF2_SHORTAWAY; +		else +			return 0; +	case PFLAGNUM_4: +		return PF4_NOCUSTOMAUTH | PF4_FORCEADDED | PF4_IMSENDUTF | PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE; +	case PFLAGNUM_5: +		return PF2_ONTHEPHONE; +	case PFLAG_MAXLENOFMESSAGE: +		return FACEBOOK_MESSAGE_LIMIT; +	case PFLAG_UNIQUEIDTEXT: +		return (DWORD_PTR) "Facebook ID"; +	case PFLAG_UNIQUEIDSETTING: +		return (DWORD_PTR) FACEBOOK_KEY_ID; +	} +	return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +int FacebookProto::SetStatus(int new_status) +{ +	debugLogA("===== Beginning SetStatus process"); + +	// Routing statuses not supported by Facebook +	switch (new_status) +	{ +	case ID_STATUS_INVISIBLE: +	case ID_STATUS_OFFLINE: +		m_iDesiredStatus = new_status; +		break; + +	case ID_STATUS_IDLE: +	default: +		m_iDesiredStatus = ID_STATUS_INVISIBLE; +		if (getByte(FACEBOOK_KEY_MAP_STATUSES, DEFAULT_MAP_STATUSES)) +			break; +	case ID_STATUS_ONLINE: +	case ID_STATUS_FREECHAT: +		m_iDesiredStatus = ID_STATUS_ONLINE; +		break; +	} + +	if (new_status != ID_STATUS_OFFLINE && m_iStatus == ID_STATUS_CONNECTING) { +		debugLogA("===== Status is already connecting, no change"); +		return 0; +	} + +	if (m_iStatus == m_iDesiredStatus) { +		debugLogA("===== Statuses are same, no change"); +		return 0; +	} + +	ForkThread(&FacebookProto::ChangeStatus, this); +	return 0; +} + +int FacebookProto::SetAwayMsg(int status, const PROTOCHAR *msg) +{ +	if (!msg) { +		last_status_msg_.clear(); +		return 0; +	} + +	char *narrow = mir_utf8encodeT(msg); +	if (last_status_msg_ != narrow) +		last_status_msg_ = narrow; +	mir_free(narrow); + +	if (isOnline() && getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS)) +		ForkThread(&FacebookProto::SetAwayMsgWorker, NULL); + +	return 0; +} + +void FacebookProto::SetAwayMsgWorker(void *p) +{ +	if (p != NULL) { +		status_data *data = static_cast<status_data*>(p); +		facy.post_status(data); +		delete data; +	} else if (!last_status_msg_.empty()) { +		status_data data; +		data.text = last_status_msg_; +		data.privacy = facy.get_privacy_type(); +		facy.post_status(&data); +	} +} + +HANDLE FacebookProto::SearchBasic(const PROTOCHAR* id) +{ +	if (isOffline()) +		return 0; + +	TCHAR *tid = mir_tstrdup(id); +	ForkThread(&FacebookProto::SearchIdAckThread, tid); +	return tid; +} + +HANDLE FacebookProto::SearchByEmail(const PROTOCHAR* email) +{ +	if (isOffline()) +		return 0; + +	TCHAR *temail = mir_tstrdup(email); +	ForkThread(&FacebookProto::SearchAckThread, temail); +	return temail; +} + +HANDLE FacebookProto::SearchByName(const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName) +{ +	TCHAR arg[200]; +	mir_sntprintf (arg, SIZEOF(arg), _T("%s %s %s"), nick, firstName, lastName); +	return SearchByEmail(arg); // Facebook is using one search method for everything (except IDs) +} + +MCONTACT FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr) +{ +	ptrA id( mir_t2a_cp(psr->id, CP_UTF8)); +	ptrA name( mir_t2a_cp(psr->firstName, CP_UTF8)); +	ptrA surname( mir_t2a_cp(psr->lastName, CP_UTF8)); + +	if (id == NULL) +		return NULL; + +	facebook_user fbu; +	fbu.user_id = id; +	if (name != NULL) +		fbu.real_name = name; +	if (surname != NULL) { +		fbu.real_name += " "; +		fbu.real_name += surname; +	} + +	if (fbu.user_id.find_first_not_of("0123456789") != std::string::npos) { +		MessageBox(0, TranslateT("Facebook ID must be numeric value."), m_tszUserName, MB_ICONERROR | MB_OK); +		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"); +		} +	} + +	return hContact; +} + +int FacebookProto::AuthRequest(MCONTACT hContact,const PROTOCHAR *message) +{ +	return RequestFriendship(hContact, NULL); +} + +int FacebookProto::Authorize(HANDLE hDbEvent) +{ +	if (!hDbEvent || isOffline()) +		return 1; + +	MCONTACT hContact = HContactFromAuthEvent(hDbEvent); +	if (hContact == INVALID_CONTACT_ID) +		return 1; + +	return ApproveFriendship(hContact, NULL); +} + +int FacebookProto::AuthDeny(HANDLE hDbEvent, const PROTOCHAR *reason) +{ +	if (!hDbEvent || isOffline()) +		return 1; + +	MCONTACT hContact = HContactFromAuthEvent(hDbEvent); +	if (hContact == INVALID_CONTACT_ID) +		return 1; + +	// TODO: hide from facebook requests list + +	if (db_get_b(hContact, "CList", "NotOnList", 0)) +		CallService(MS_DB_CONTACT_DELETE, hContact, 0); + +	return 0; +} + +int FacebookProto::GetInfo(MCONTACT hContact, int infoType) +{ +	ptrA user_id(getStringA(hContact, FACEBOOK_KEY_ID)); + +	if (user_id == NULL) +		return 1; +	 +	facebook_user fbu; +	fbu.user_id = user_id; + +	LoadContactInfo(&fbu); + +	// TODO: don't duplicate code this way, refactor all this userInfo loading +	// TODO: load more info about user (authorization state,...) + +	std::string homepage = FACEBOOK_URL_PROFILE + fbu.user_id; +	setString(hContact, "Homepage", homepage.c_str()); + +	if (!fbu.real_name.empty()) { +		SaveName(hContact, &fbu); +	} + +	if (fbu.gender) +		setByte(hContact, "Gender", fbu.gender); + +	CheckAvatarChange(hContact, fbu.image_url); + +	return 1; +} + +////////////////////////////////////////////////////////////////////////////// +// SERVICES + +INT_PTR FacebookProto::GetMyAwayMsg(WPARAM wParam, LPARAM lParam) +{ +	DBVARIANT dbv = { DBVT_TCHAR }; +	if (!getTString("StatusMsg", &dbv) && lstrlen(dbv.ptszVal) != 0) +	{ +		int res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_t2u(dbv.ptszVal) : (INT_PTR)mir_t2a(dbv.ptszVal); +		db_free(&dbv); +		return res; +	} else { +		return 0; +	} +} + +int FacebookProto::OnIdleChanged(WPARAM wParam, LPARAM lParam) +{ +	if (m_iStatus == ID_STATUS_INVISIBLE || m_iStatus <= ID_STATUS_OFFLINE) +		return 0; + +	bool bIdle = (lParam & IDF_ISIDLE) != 0; +	bool bPrivacy = (lParam & IDF_PRIVACY) != 0; + +	if (facy.is_idle_ && !bIdle) +	{ +		facy.is_idle_ = false; +		SetStatus(m_iDesiredStatus); +	} +	else if (!facy.is_idle_ && bIdle && !bPrivacy && m_iDesiredStatus != ID_STATUS_INVISIBLE) +	{ +		facy.is_idle_ = true; +		SetStatus(ID_STATUS_IDLE); +	} + +	return 0; +} + +INT_PTR FacebookProto::GetNotificationsCount(WPARAM wParam, LPARAM lParam) +{ +	if (isOffline()) +		return 0; + +	return facy.notifications.size(); +} + +////////////////////////////////////////////////////////////////////////////// + +int FacebookProto::OnEvent(PROTOEVENTTYPE event, WPARAM wParam, LPARAM lParam) +{ +	switch(event) +	{ +	case EV_PROTO_ONLOAD: +		return OnModulesLoaded(wParam,lParam); + +	case EV_PROTO_ONEXIT: +		return OnPreShutdown(wParam,lParam); + +	case EV_PROTO_ONOPTIONS: +		return OnOptionsInit(wParam,lParam); + +	case EV_PROTO_ONCONTACTDELETED: + 		return OnContactDeleted(wParam,lParam); +	} + +	return 1; +} + +////////////////////////////////////////////////////////////////////////////// +// EVENTS + +INT_PTR FacebookProto::SvcCreateAccMgrUI(WPARAM wParam, LPARAM lParam) +{ +	return (int)CreateDialogParam(g_hInstance,MAKEINTRESOURCE(IDD_FACEBOOKACCOUNT), +		 (HWND)lParam, FBAccountProc, (LPARAM)this); +} + +int FacebookProto::OnModulesLoaded(WPARAM wParam, LPARAM lParam) +{ +	// Register group chat +	GCREGISTER gcr = {sizeof(gcr)}; +	gcr.dwFlags = 0; //GC_ACKMSG; +	gcr.pszModule = m_szModuleName; +	gcr.ptszDispName = m_tszUserName; +	gcr.iMaxText = FACEBOOK_MESSAGE_LIMIT; +	gcr.nColors = 0; +	gcr.pColors = NULL; +	CallService(MS_GC_REGISTER,0,reinterpret_cast<LPARAM>(&gcr)); + +	return 0; +} + +int FacebookProto::OnPreShutdown(WPARAM wParam, LPARAM lParam) +{ +	SetStatus(ID_STATUS_OFFLINE); +	return 0; +} + +int FacebookProto::OnOptionsInit(WPARAM wParam, LPARAM lParam) +{ +	OPTIONSDIALOGPAGE odp = {sizeof(odp)}; +	odp.hInstance   = g_hInstance; +	odp.ptszTitle   = m_tszUserName; +	odp.dwInitParam = LPARAM(this); +	odp.flags       = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_DONTTRANSLATE; + +	odp.position    = 271828; +	odp.ptszGroup   = LPGENT("Network"); +	odp.ptszTab     = LPGENT("Account"); +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS); +	odp.pfnDlgProc  = FBOptionsProc; +	Options_AddPage(wParam, &odp); + +	odp.position    = 271829; +	odp.ptszTab     = LPGENT("Events"); +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_EVENTS); +	odp.pfnDlgProc  = FBEventsProc; +	Options_AddPage(wParam, &odp); + +	odp.position    = 271830; +	odp.ptszTab     = LPGENT("Advanced"); +	odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_ADVANCED); +	odp.pfnDlgProc  = FBOptionsAdvancedProc; +	Options_AddPage(wParam, &odp); +	return 0; +} + +int FacebookProto::OnToolbarInit(WPARAM, LPARAM) +{ +	TTBButton ttb = { sizeof(ttb) }; +	ttb.dwFlags = TTBBF_SHOWTOOLTIP | TTBBF_VISIBLE; + +	char service[100]; +	mir_snprintf(service, sizeof(service), "%s%s", m_szModuleName, "/Mind"); + +	ttb.pszService = service; +	ttb.pszTooltipUp = ttb.name = LPGEN("Share status..."); +	ttb.hIconHandleUp = Skin_GetIconByHandle(GetIconHandle("mind")); +	TopToolbar_AddButton(&ttb); + +	return 0; +} + +INT_PTR FacebookProto::OnMind(WPARAM wParam, LPARAM lParam) +{ +	if (!isOnline()) +		return 1; + +	MCONTACT hContact = MCONTACT(wParam); + +	wall_data *wall = new wall_data(); +	wall->user_id = ptrA(getStringA(hContact, FACEBOOK_KEY_ID)); +	wall->isPage = false; +	if (wall->user_id == facy.self_.user_id) { +		wall->title = _tcsdup(TranslateT("Own wall")); +	} else +		wall->title = getTStringA(hContact, FACEBOOK_KEY_NICK); + +	post_status_data *data = new post_status_data(this, wall); + +	if (wall->user_id == facy.self_.user_id) { +		for (std::map<std::string, std::string>::iterator iter = facy.pages.begin(); iter != facy.pages.end(); ++iter) { +			data->walls.push_back(new wall_data(iter->first, mir_utf8decodeT(iter->second.c_str()), true)); +		} +	} +		 +	HWND hDlg = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_MIND), (HWND)0, FBMindProc, reinterpret_cast<LPARAM>(data)); +	ShowWindow(hDlg, SW_SHOW); + +	return 0; +} + +int FacebookProto::OnDbEventRead(WPARAM contactID, LPARAM dbei) +{ +	std::map<MCONTACT, bool>::iterator it = facy.ignore_read.find(contactID); +	if (it != facy.ignore_read.end()) { +		if (it->second) // it's TRUE, so we ignore this +			return 0; +		else // it's FALSE, so we just remove it from list +			facy.ignore_read.erase(it); +	} + +	if ((IsMyContact(contactID, true)) && !isOffline()) { +		ForkThread(&FacebookProto::ReadMessageWorker, (void*)contactID); +	} + +	return 0; +} + + +int FacebookProto::OnProcessSrmmEvent(WPARAM, LPARAM lParam) +{ +	MessageWindowEventData *event = (MessageWindowEventData *)lParam; + +	if (event->uType == MSG_WINDOW_EVT_OPEN) { +		// Load last messages for this contact +		ForkThread(&FacebookProto::LoadLastMessages, new MCONTACT(event->hContact)); +	} + +	return 0; +} + +INT_PTR FacebookProto::CheckNewsfeeds(WPARAM, LPARAM) +{ +	if (!isOffline()) { +		facy.client_notify(TranslateT("Loading newsfeeds...")); +		facy.last_feeds_update_ = 0; +		ForkThread(&FacebookProto::ProcessFeeds, NULL); +	} +	return 0; +} + +INT_PTR FacebookProto::CheckFriendRequests(WPARAM, LPARAM) +{ +	if (!isOffline()) { +		facy.client_notify(TranslateT("Checking friend requests...")); +		ProcessFriendRequests(NULL); +	} +	return 0; +} + +INT_PTR FacebookProto::RefreshBuddyList(WPARAM, LPARAM) +{ +	if (!isOffline()) { +		facy.client_notify(TranslateT("Refreshing buddy list...")); +		ForkThread(&FacebookProto::ProcessBuddyList, NULL); +	} +	return 0; +} + + +INT_PTR FacebookProto::VisitProfile(WPARAM wParam,LPARAM lParam) +{ +	MCONTACT hContact = MCONTACT(wParam); + +	std::string url = FACEBOOK_URL_PROFILE; + +	ptrA val(getStringA(hContact, "Homepage")); +	if (val != NULL) { +		// Homepage link already present, get it +		url = val; +	} else { +		// No homepage link, create and save it +		val = getStringA(hContact, FACEBOOK_KEY_ID); +		if (val != NULL) { +			url += val; +			setString(hContact, "Homepage", url.c_str()); +		} +	} + +	OpenUrl(url); +	return 0; +} + +INT_PTR FacebookProto::VisitFriendship(WPARAM wParam,LPARAM lParam) +{ +	MCONTACT hContact = MCONTACT(wParam); + +	if (wParam == 0 || !IsMyContact(hContact)) +		return 1; + +	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID)); + +	std::string url = FACEBOOK_URL_PROFILE; +	url += facy.self_.user_id; +	url += "&and=" + std::string(id); + +	OpenUrl(url); +	return 0; +} + +INT_PTR FacebookProto::VisitConversation(WPARAM wParam, LPARAM lParam) +{ +	MCONTACT hContact = MCONTACT(wParam); + +	if (wParam == 0 || !IsMyContact(hContact, true)) +		return 1; + +	std::string url = FACEBOOK_URL_CONVERSATION; + +	if (isChatRoom(hContact)) { +		url += "conversation-"; +		url += ptrA(getStringA(hContact, FACEBOOK_KEY_TID)); +	} else { +		url += ptrA(getStringA(hContact, FACEBOOK_KEY_ID)); +	} + +	OpenUrl(url); +	return 0; +} + +INT_PTR FacebookProto::VisitNotifications(WPARAM wParam, LPARAM lParam) +{ +	OpenUrl(FACEBOOK_URL_NOTIFICATIONS); +	return 0; +} + +INT_PTR FacebookProto::Poke(WPARAM wParam,LPARAM lParam) +{ +	if (wParam == NULL || isOffline()) +		return 1; + +	MCONTACT hContact = MCONTACT(wParam); + +	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID)); +	if (id == NULL) +		return 1; + +	ForkThread(&FacebookProto::SendPokeWorker, new std::string(id)); +	return 0; +} + +INT_PTR FacebookProto::CancelFriendship(WPARAM wParam,LPARAM lParam) +{ +	if (wParam == NULL || isOffline()) +		return 1; + +	bool deleting = (lParam == 1); + +	MCONTACT hContact = MCONTACT(wParam); + +	// Ignore groupchats and, if deleting, also not-friends +	if (isChatRoom(hContact) || (deleting && getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, 0) != CONTACT_FRIEND)) +		return 0; + +	ptrT tname(getTStringA(hContact, FACEBOOK_KEY_NICK)); +	if (tname == NULL) +		tname = getTStringA(hContact, FACEBOOK_KEY_ID); + +	TCHAR tstr[256]; +	mir_sntprintf(tstr,SIZEOF(tstr),TranslateT("Do you want to cancel your friendship with '%s'?"), tname); +	if (MessageBox(0, tstr, m_tszUserName, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2) == IDYES) { + +		ptrA id(getStringA(hContact, FACEBOOK_KEY_ID)); +		if (id == NULL) +			return 1; + +		std::string *val = new std::string(id); + +		if (deleting) { +			facebook_user *fbu = facy.buddies.find(*val); +			if (fbu != NULL) +				fbu->handle = NULL; +		} + +		ForkThread(&FacebookProto::DeleteContactFromServer, val); +	} + +	return 0; +} + +INT_PTR FacebookProto::RequestFriendship(WPARAM wParam,LPARAM lParam) +{ +	if (wParam == NULL || isOffline()) +		return 1; + +	MCONTACT hContact = MCONTACT(wParam); + +	ptrA id(getStringA(hContact, FACEBOOK_KEY_ID)); +	if (id == NULL) +		return 1; + +	ForkThread(&FacebookProto::AddContactToServer, new std::string(id)); +	return 0; +} + +INT_PTR FacebookProto::ApproveFriendship(WPARAM wParam,LPARAM lParam) +{ +	if (wParam == NULL || isOffline()) +		return 1; + +	HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam)); + +	ForkThread(&FacebookProto::ApproveContactToServer, hContact); +	return 0; +} + +INT_PTR FacebookProto::OnCancelFriendshipRequest(WPARAM wParam,LPARAM lParam) +{ +	if (wParam == NULL || isOffline()) +		return 1; + +	HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam)); + +	ForkThread(&FacebookProto::CancelFriendsRequest, hContact); +	return 0; +} + +MCONTACT FacebookProto::HContactFromAuthEvent(HANDLE hEvent) +{ +	DWORD body[2]; +	DBEVENTINFO dbei = { sizeof(dbei) }; +	dbei.cbBlob = sizeof(DWORD)*2; +	dbei.pBlob = (PBYTE)&body; + +	if (db_event_get(hEvent, &dbei)) +		return INVALID_CONTACT_ID; + +	if (dbei.eventType != EVENTTYPE_AUTHREQUEST) +		return INVALID_CONTACT_ID; + +	if (strcmp(dbei.szModule, m_szModuleName)) +		return INVALID_CONTACT_ID; + +	return DbGetAuthEventContact(&dbei); +} + +void FacebookProto::OpenUrl(std::string url) +{ +	std::string::size_type pos = url.find(FACEBOOK_SERVER_DOMAIN); +	bool isFacebookUrl = (pos != std::string::npos); +	bool isRelativeUrl = (url.substr(0, 4) != "http"); + +	if (isFacebookUrl || isRelativeUrl) { + +		// Make realtive url +		if (!isRelativeUrl) { +			url = url.substr(pos + strlen(FACEBOOK_SERVER_DOMAIN)); + +			// Strip eventual port +			pos = url.find("/"); +			if (pos != std::string::npos && pos != 0) +				url = url.substr(pos); +		} + +		// Make absolute url +		bool useHttps = getByte(FACEBOOK_KEY_FORCE_HTTPS, 1) > 0; +		url = (useHttps ? HTTP_PROTO_SECURE : HTTP_PROTO_REGULAR) + facy.get_server_type() + url; +	} + +	ptrT data( mir_utf8decodeT(url.c_str())); +	CallService(MS_UTILS_OPENURL, (WPARAM)OUF_TCHAR, (LPARAM)data); +} + +void FacebookProto::ReadNotificationWorker(void *p) +{ +	if (p == NULL) +		return; + +	std::string *id = static_cast<std::string*>(p); + +	std::string data = "seen=0&asyncSignal=&__dyn=&__req=a&alert_ids%5B0%5D=" + utils::url::encode(*id); +	data += "&fb_dtsg=" + (facy.dtsg_.length() ? facy.dtsg_ : "0"); +	data += "&__user=" + facy.self_.user_id; + +	facy.flap(REQUEST_NOTIFICATIONS_READ, NULL, &data); + +	delete p; +} + +/** + * Popup processing callback + */ +LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ +	switch(message) +	{ +	case WM_COMMAND: +	case WM_CONTEXTMENU: +	{ +		// Get the plugin data (we need the Popup service to do it) +		popup_data *data = (popup_data *)PUGetPluginData(hwnd); +		if (data != NULL) { +			if (!data->notification_id.empty()) +				data->proto->ForkThread(&FacebookProto::ReadNotificationWorker, new std::string(data->notification_id)); + +			if (message == WM_COMMAND && !data->url.empty()) +				data->proto->OpenUrl(data->url); +		} + +		// After a click, destroy popup +		PUDeletePopup(hwnd); +	} break; + +	case UM_FREEPLUGINDATA: +	{ +		// After close, free +		popup_data *data = (popup_data *)PUGetPluginData(hwnd); +		delete data; +	} return FALSE; + +	default: +		break; +	} + +	return DefWindowProc(hwnd, message, wParam, lParam); +}; + +/** + * Popup classes initialization + */ +void FacebookProto::InitPopups() +{		 +	POPUPCLASS ppc = { sizeof(ppc) }; +	ppc.flags = PCF_TCHAR;		 +	ppc.PluginWindowProc = PopupDlgProc; +	ppc.lParam = APF_RETURN_HWND; + +	TCHAR desc[256]; +	char name[256]; + +	// Client +	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Client notifications")); +	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Client"); +	ppc.ptszDescription = desc; +	ppc.pszName = name; +	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("facebook")); +	ppc.colorBack = RGB(191, 0, 0); // red +	ppc.colorText = RGB(255, 255, 255); // white +	ppc.iSeconds = 0; +	popupClasses.push_back(Popup_RegisterClass(&ppc)); + +	// Newsfeeds +	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("News feeds")); +	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Newsfeed"); +	ppc.ptszDescription = desc; +	ppc.pszName = name; +	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("newsfeed")); +	ppc.colorBack = RGB(255, 255, 255); // white +	ppc.colorText = RGB(0, 0, 0); // black +	ppc.iSeconds = 0; +	popupClasses.push_back(Popup_RegisterClass(&ppc)); + +	// Notifications +	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Notifications")); +	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Notification"); +	ppc.ptszDescription = desc; +	ppc.pszName = name; +	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("notification")); +	ppc.colorBack = RGB(59, 89, 152); // Facebook's blue +	ppc.colorText = RGB(255, 255, 255); // white +	ppc.iSeconds = 0; +	popupClasses.push_back(Popup_RegisterClass(&ppc)); + +	// Others +	mir_sntprintf(desc, SIZEOF(desc), _T("%s/%s"), m_tszUserName, TranslateT("Other events")); +	mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Other"); +	ppc.ptszDescription = desc; +	ppc.pszName = name; +	ppc.hIcon = Skin_GetIconByHandle(GetIconHandle("facebook")); +	ppc.colorBack = RGB(255, 255, 255); // white +	ppc.colorText = RGB(0, 0, 0); // black +	ppc.iSeconds = 0; +	popupClasses.push_back(Popup_RegisterClass(&ppc)); +} + +/** + * Hotkeys initialiation + */ +void FacebookProto::InitHotkeys() +{ +	char module[512]; +	mir_snprintf(module, sizeof(module), "%s/Mind", m_szModuleName); + +	HOTKEYDESC hkd = { sizeof(hkd) }; +	hkd.dwFlags = HKD_TCHAR; +	hkd.ptszDescription = LPGENT("Show 'Share status' window"); +	hkd.pszName = "ShowMindWnd"; +	hkd.ptszSection = m_tszUserName; +	hkd.pszService = module; +	hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_EXT, 'F'); +	Hotkey_Register(&hkd); +} + +/** + * Sounds initialization + */ +void FacebookProto::InitSounds() +{ +	SkinAddNewSoundExT("Notification", m_tszUserName, LPGENT("Notification")); +	SkinAddNewSoundExT("NewsFeed", m_tszUserName, LPGENT("News Feed")); +	SkinAddNewSoundExT("OtherEvent", m_tszUserName, LPGENT("Other Event")); +} diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h index 716cdd3d62..ea6c9d37eb 100644 --- a/protocols/FacebookRM/src/proto.h +++ b/protocols/FacebookRM/src/proto.h @@ -1,242 +1,244 @@ -/*
 -
 -Facebook plugin for Miranda Instant Messenger
 -_____________________________________________
 -
 -Copyright © 2009-11 Michal Zelinka, 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 "constants.h"
 -
 -#pragma once
 -
 -class FacebookProto : public PROTO<FacebookProto>
 -{
 -public:
 -	FacebookProto(const char *proto_name, const TCHAR *username);
 -	~FacebookProto();
 -
 -	inline const char* ModuleName() const
 -	{
 -		return m_szModuleName;
 -	}
 -
 -	inline bool isOnline()
 -	{
 -		return (m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING);
 -	}
 -
 -	inline bool isOffline()
 -	{
 -		return (m_iStatus == ID_STATUS_OFFLINE);
 -	}
 -
 -	inline bool isInvisible()
 -	{
 -		return (m_iStatus == ID_STATUS_INVISIBLE);
 -	}
 -
 -	// DB utils missing in proto_interface
 -
 -	__forceinline INT_PTR getStringUtf(const char *name, DBVARIANT *result) {
 -		return db_get_utf(NULL, m_szModuleName, name, result); }
 -	__forceinline INT_PTR getStringUtf(MCONTACT hContact, const char *name, DBVARIANT *result) {
 -		return db_get_utf(hContact, m_szModuleName, name, result); }
 -
 -	__forceinline void setStringUtf(const char *name, const char* value) { db_set_utf(NULL, m_szModuleName, name, value); }
 -	__forceinline void setStringUtf(MCONTACT hContact, const char *name, const char* value) { db_set_utf(hContact, m_szModuleName, name, value); }
 -
 -	//PROTO_INTERFACE
 -
 -	virtual	MCONTACT __cdecl AddToList(int flags, PROTOSEARCHRESULT* psr);
 -	virtual	MCONTACT __cdecl AddToListByEvent(int flags, int iContact, HANDLE hDbEvent);
 -
 -	virtual	int      __cdecl Authorize(HANDLE hDbEvent);
 -	virtual	int      __cdecl AuthDeny(HANDLE hDbEvent, const PROTOCHAR* szReason);
 -	virtual	int      __cdecl AuthRecv(MCONTACT hContact, PROTORECVEVENT*);
 -	virtual	int      __cdecl AuthRequest(MCONTACT hContact, const PROTOCHAR* szMessage);
 -
 -	virtual	HANDLE   __cdecl FileAllow(MCONTACT hContact, HANDLE hTransfer, const PROTOCHAR* szPath);
 -	virtual	int      __cdecl FileCancel(MCONTACT hContact, HANDLE hTransfer);
 -	virtual	int      __cdecl FileDeny(MCONTACT hContact, HANDLE hTransfer, const PROTOCHAR* szReason);
 -	virtual	int      __cdecl FileResume(HANDLE hTransfer, int* action, const PROTOCHAR** szFilename);
 -
 -	virtual	DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL);
 -	virtual	int       __cdecl GetInfo(MCONTACT hContact, int infoType);
 -
 -	virtual	HANDLE    __cdecl SearchBasic(const PROTOCHAR* id);
 -	virtual	HANDLE    __cdecl SearchByEmail(const PROTOCHAR* email);
 -	virtual	HANDLE    __cdecl SearchByName(const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName);
 -	virtual	HWND      __cdecl SearchAdvanced(HWND owner);
 -	virtual	HWND      __cdecl CreateExtendedSearchUI(HWND owner);
 -
 -	virtual	int       __cdecl RecvContacts(MCONTACT hContact, PROTORECVEVENT*);
 -	virtual	int       __cdecl RecvFile(MCONTACT hContact, PROTOFILEEVENT*);
 -	virtual	int       __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT*);
 -	virtual	int       __cdecl RecvUrl(MCONTACT hContact, PROTORECVEVENT*);
 -
 -	virtual	int       __cdecl SendContacts(MCONTACT hContact, int flags, int nContacts, MCONTACT *hContactsList);
 -	virtual	HANDLE    __cdecl SendFile(MCONTACT hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles);
 -	virtual	int       __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg);
 -	virtual	int       __cdecl SendUrl(MCONTACT hContact, int flags, const char* url);
 -
 -	virtual	int       __cdecl SetApparentMode(MCONTACT hContact, int mode);
 -	virtual	int       __cdecl SetStatus(int iNewStatus);
 -
 -	virtual	HANDLE    __cdecl GetAwayMsg(MCONTACT hContact);
 -	virtual	int       __cdecl RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT* evt);
 -	virtual	int       __cdecl SetAwayMsg(int iStatus, const PROTOCHAR* msg);
 -
 -	virtual	int       __cdecl UserIsTyping(MCONTACT hContact, int type);
 -
 -	virtual	int       __cdecl OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam);
 -
 -	////////////////////////
 -
 -	// Services
 -	INT_PTR __cdecl GetMyAwayMsg(WPARAM, LPARAM);
 -	INT_PTR __cdecl SetMyAwayMsg(WPARAM, LPARAM);
 -	INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
 -	INT_PTR __cdecl GetMyAvatar(WPARAM, LPARAM);
 -	INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM);
 -	INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM);
 -	INT_PTR __cdecl VisitProfile(WPARAM, LPARAM);
 -	INT_PTR __cdecl VisitFriendship(WPARAM, LPARAM);
 -	INT_PTR __cdecl VisitConversation(WPARAM, LPARAM);
 -	INT_PTR __cdecl VisitNotifications(WPARAM, LPARAM);
 -	INT_PTR __cdecl Poke(WPARAM, LPARAM);
 -	INT_PTR __cdecl CancelFriendship(WPARAM, LPARAM);
 -	INT_PTR __cdecl RequestFriendship(WPARAM, LPARAM);
 -	INT_PTR __cdecl ApproveFriendship(WPARAM, LPARAM);
 -	INT_PTR __cdecl OnCancelFriendshipRequest(WPARAM, LPARAM);
 -	INT_PTR __cdecl CheckNewsfeeds(WPARAM, LPARAM);
 -	INT_PTR __cdecl CheckFriendRequests(WPARAM, LPARAM);
 -	INT_PTR __cdecl RefreshBuddyList(WPARAM, LPARAM);
 -	INT_PTR __cdecl GetNotificationsCount(WPARAM, LPARAM);
 -
 -	INT_PTR __cdecl OnJoinChat(WPARAM,LPARAM);
 -	INT_PTR __cdecl OnLeaveChat(WPARAM,LPARAM);
 -
 -	INT_PTR __cdecl OnMind(WPARAM,LPARAM);
 -
 -	// Initialiation
 -	void InitPopups();
 -	void InitHotkeys();
 -	void InitSounds();
 -
 -	// Events
 -	int  __cdecl OnModulesLoaded(WPARAM, LPARAM);
 -	int  __cdecl OnOptionsInit(WPARAM, LPARAM);
 -	int  __cdecl OnToolbarInit(WPARAM, LPARAM);
 -	int  __cdecl OnBuildStatusMenu(WPARAM,LPARAM);
 -	int  __cdecl OnContactDeleted(WPARAM,LPARAM);
 -	int  __cdecl OnPreShutdown(WPARAM,LPARAM);
 -	int  __cdecl OnPrebuildContactMenu(WPARAM,LPARAM);
 -	int  __cdecl OnIdleChanged(WPARAM,LPARAM);
 -	int  __cdecl OnGCEvent(WPARAM,LPARAM);
 -	int  __cdecl OnGCMenuHook(WPARAM,LPARAM);
 -	int  __cdecl OnDbEventRead(WPARAM, LPARAM);
 -
 -	// Loops
 -	bool    NegotiateConnection();
 -	BYTE    GetPollRate();
 -	void __cdecl MessageLoop(void*);
 -	void __cdecl UpdateLoop(void*);
 -
 -	// Processing threads
 -	void __cdecl ProcessBuddyList(void*);
 -	void __cdecl ProcessFriendList(void*);
 -	void __cdecl ProcessMessages(void*);
 -	void __cdecl ProcessUnreadMessages(void*);
 -	void __cdecl ProcessUnreadMessage(void*);
 -	void __cdecl ProcessFeeds(void*);
 -	void __cdecl ProcessNotifications(void*);
 -	void __cdecl ProcessFriendRequests(void*);
 -	void __cdecl SearchAckThread(void*);
 -	void __cdecl SearchIdAckThread(void*);
 -	void __cdecl ProcessPages(void*);
 -
 -	// Worker threads
 -	void __cdecl SignOn(void*);
 -	void __cdecl ChangeStatus(void*);
 -	void __cdecl SignOff(void*);
 -	void __cdecl SetAwayMsgWorker(void*);
 -	void __cdecl UpdateAvatarWorker(void*);
 -	void __cdecl SendMsgWorker(void*);
 -	void __cdecl SendChatMsgWorker(void*);
 -	void __cdecl SendTypingWorker(void*);
 -	void __cdecl ReadMessageWorker(void*);
 -	void __cdecl ReadNotificationWorker(void*);
 -	void __cdecl DeleteContactFromServer(void*);
 -	void __cdecl AddContactToServer(void*);
 -	void __cdecl ApproveContactToServer(void*);
 -	void __cdecl CancelFriendsRequest(void*);
 -	void __cdecl SendPokeWorker(void*);
 -
 -	// Contacts handling
 -	bool     IsMyContact(MCONTACT, bool include_chat = false);
 -	MCONTACT ContactIDToHContact(std::string);
 -	MCONTACT ChatIDToHContact(std::tstring);
 -	std::string ThreadIDToContactID(std::string thread_id);
 -	void FacebookProto::LoadContactInfo(facebook_user* fbu);
 -	MCONTACT AddToContactList(facebook_user*, ContactType type, bool force_add = false);
 -	void     SetAllContactStatuses(int status);
 -	MCONTACT HContactFromAuthEvent(HANDLE hEvent);
 -
 -	// Chats handling
 - 	void AddChat(const TCHAR *id, const TCHAR *name);
 -	void UpdateChat(const TCHAR *chat_id, const char *id, const char *name, const char *message, DWORD timestamp = 0);
 -	void RenameChat(const char *chat_id, const char *name);
 -	bool IsChatContact(const TCHAR *chat_id, const char *id);
 -	void AddChatContact(const TCHAR *chat_id, const char *id, const char *name);
 -	void RemoveChatContact(const TCHAR *chat_id, const char *id);
 -	void SetChatStatus(const char *chat_id, int status);
 -	char *GetChatUsers(const TCHAR *chat_id);
 -	void ReceiveMessages(std::vector<facebook_message*> messages, bool local_timestamp, bool check_duplicates = false);
 -
 -	// Connection client
 -	facebook_client facy; // TODO: Refactor to "client" and make dynamic
 -
 -	// Helpers
 -	std::tstring GetAvatarFolder();
 -	bool GetDbAvatarInfo(PROTO_AVATAR_INFORMATIONT &ai, std::string *url);
 -	void CheckAvatarChange(MCONTACT hContact, std::string image_url);
 -	void ToggleStatusMenuItems(BOOL bEnable);
 -	void ParseSmileys(std::string message, MCONTACT hContact);
 -	void OpenUrl(std::string url);
 -	void SaveName(MCONTACT hContact, const facebook_user *fbu);
 -
 -	// Handles, Locks
 -	HGENMENU m_hMenuRoot, m_hMenuServicesRoot, m_hStatusMind;
 -
 -	HANDLE  signon_lock_;
 -	HANDLE  avatar_lock_;
 -	HANDLE  log_lock_;
 -	HANDLE  update_loop_lock_;
 -
 -	std::vector<HANDLE> popupClasses;
 -
 -	std::string last_status_msg_;
 -	HANDLE	hSmileysFolder_;
 -	std::vector<MCONTACT> avatar_queue;
 -
 -	static void CALLBACK APC_callback(ULONG_PTR p);
 -
 -	// Information providing
 -	HWND NotifyEvent(TCHAR* title, TCHAR* info, MCONTACT contact, DWORD flags, std::string *url = NULL, std::string *notification_id = NULL);
 -	void ShowNotifications();
 -};
 +/* + +Facebook plugin for Miranda Instant Messenger +_____________________________________________ + +Copyright © 2009-11 Michal Zelinka, 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 "constants.h" + +#pragma once + +class FacebookProto : public PROTO<FacebookProto> +{ +public: +	FacebookProto(const char *proto_name, const TCHAR *username); +	~FacebookProto(); + +	inline const char* ModuleName() const +	{ +		return m_szModuleName; +	} + +	inline bool isOnline() +	{ +		return (m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING); +	} + +	inline bool isOffline() +	{ +		return (m_iStatus == ID_STATUS_OFFLINE); +	} + +	inline bool isInvisible() +	{ +		return (m_iStatus == ID_STATUS_INVISIBLE); +	} + +	// DB utils missing in proto_interface + +	__forceinline INT_PTR getStringUtf(const char *name, DBVARIANT *result) { +		return db_get_utf(NULL, m_szModuleName, name, result); } +	__forceinline INT_PTR getStringUtf(MCONTACT hContact, const char *name, DBVARIANT *result) { +		return db_get_utf(hContact, m_szModuleName, name, result); } + +	__forceinline void setStringUtf(const char *name, const char* value) { db_set_utf(NULL, m_szModuleName, name, value); } +	__forceinline void setStringUtf(MCONTACT hContact, const char *name, const char* value) { db_set_utf(hContact, m_szModuleName, name, value); } + +	//PROTO_INTERFACE + +	virtual	MCONTACT __cdecl AddToList(int flags, PROTOSEARCHRESULT* psr); +	virtual	MCONTACT __cdecl AddToListByEvent(int flags, int iContact, HANDLE hDbEvent); + +	virtual	int      __cdecl Authorize(HANDLE hDbEvent); +	virtual	int      __cdecl AuthDeny(HANDLE hDbEvent, const PROTOCHAR* szReason); +	virtual	int      __cdecl AuthRecv(MCONTACT hContact, PROTORECVEVENT*); +	virtual	int      __cdecl AuthRequest(MCONTACT hContact, const PROTOCHAR* szMessage); + +	virtual	HANDLE   __cdecl FileAllow(MCONTACT hContact, HANDLE hTransfer, const PROTOCHAR* szPath); +	virtual	int      __cdecl FileCancel(MCONTACT hContact, HANDLE hTransfer); +	virtual	int      __cdecl FileDeny(MCONTACT hContact, HANDLE hTransfer, const PROTOCHAR* szReason); +	virtual	int      __cdecl FileResume(HANDLE hTransfer, int* action, const PROTOCHAR** szFilename); + +	virtual	DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL); +	virtual	int       __cdecl GetInfo(MCONTACT hContact, int infoType); + +	virtual	HANDLE    __cdecl SearchBasic(const PROTOCHAR* id); +	virtual	HANDLE    __cdecl SearchByEmail(const PROTOCHAR* email); +	virtual	HANDLE    __cdecl SearchByName(const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName); +	virtual	HWND      __cdecl SearchAdvanced(HWND owner); +	virtual	HWND      __cdecl CreateExtendedSearchUI(HWND owner); + +	virtual	int       __cdecl RecvContacts(MCONTACT hContact, PROTORECVEVENT*); +	virtual	int       __cdecl RecvFile(MCONTACT hContact, PROTOFILEEVENT*); +	virtual	int       __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT*); +	virtual	int       __cdecl RecvUrl(MCONTACT hContact, PROTORECVEVENT*); + +	virtual	int       __cdecl SendContacts(MCONTACT hContact, int flags, int nContacts, MCONTACT *hContactsList); +	virtual	HANDLE    __cdecl SendFile(MCONTACT hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles); +	virtual	int       __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg); +	virtual	int       __cdecl SendUrl(MCONTACT hContact, int flags, const char* url); + +	virtual	int       __cdecl SetApparentMode(MCONTACT hContact, int mode); +	virtual	int       __cdecl SetStatus(int iNewStatus); + +	virtual	HANDLE    __cdecl GetAwayMsg(MCONTACT hContact); +	virtual	int       __cdecl RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT* evt); +	virtual	int       __cdecl SetAwayMsg(int iStatus, const PROTOCHAR* msg); + +	virtual	int       __cdecl UserIsTyping(MCONTACT hContact, int type); + +	virtual	int       __cdecl OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam); + +	//////////////////////// + +	// Services +	INT_PTR __cdecl GetMyAwayMsg(WPARAM, LPARAM); +	INT_PTR __cdecl SetMyAwayMsg(WPARAM, LPARAM); +	INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); +	INT_PTR __cdecl GetMyAvatar(WPARAM, LPARAM); +	INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM); +	INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM); +	INT_PTR __cdecl VisitProfile(WPARAM, LPARAM); +	INT_PTR __cdecl VisitFriendship(WPARAM, LPARAM); +	INT_PTR __cdecl VisitConversation(WPARAM, LPARAM); +	INT_PTR __cdecl VisitNotifications(WPARAM, LPARAM); +	INT_PTR __cdecl Poke(WPARAM, LPARAM); +	INT_PTR __cdecl CancelFriendship(WPARAM, LPARAM); +	INT_PTR __cdecl RequestFriendship(WPARAM, LPARAM); +	INT_PTR __cdecl ApproveFriendship(WPARAM, LPARAM); +	INT_PTR __cdecl OnCancelFriendshipRequest(WPARAM, LPARAM); +	INT_PTR __cdecl CheckNewsfeeds(WPARAM, LPARAM); +	INT_PTR __cdecl CheckFriendRequests(WPARAM, LPARAM); +	INT_PTR __cdecl RefreshBuddyList(WPARAM, LPARAM); +	INT_PTR __cdecl GetNotificationsCount(WPARAM, LPARAM); + +	INT_PTR __cdecl OnJoinChat(WPARAM,LPARAM); +	INT_PTR __cdecl OnLeaveChat(WPARAM,LPARAM); + +	INT_PTR __cdecl OnMind(WPARAM,LPARAM); + +	// Initialiation +	void InitPopups(); +	void InitHotkeys(); +	void InitSounds(); + +	// Events +	int  __cdecl OnModulesLoaded(WPARAM, LPARAM); +	int  __cdecl OnOptionsInit(WPARAM, LPARAM); +	int  __cdecl OnToolbarInit(WPARAM, LPARAM); +	int  __cdecl OnBuildStatusMenu(WPARAM,LPARAM); +	int  __cdecl OnContactDeleted(WPARAM,LPARAM); +	int  __cdecl OnPreShutdown(WPARAM,LPARAM); +	int  __cdecl OnPrebuildContactMenu(WPARAM,LPARAM); +	int  __cdecl OnIdleChanged(WPARAM,LPARAM); +	int  __cdecl OnGCEvent(WPARAM,LPARAM); +	int  __cdecl OnGCMenuHook(WPARAM,LPARAM); +	int  __cdecl OnDbEventRead(WPARAM, LPARAM); +	int  __cdecl OnProcessSrmmEvent(WPARAM, LPARAM); + +	// Loops +	bool    NegotiateConnection(); +	BYTE    GetPollRate(); +	void __cdecl MessageLoop(void*); +	void __cdecl UpdateLoop(void*); + +	// Processing threads +	void __cdecl ProcessBuddyList(void*); +	void __cdecl ProcessFriendList(void*); +	void __cdecl ProcessMessages(void*); +	void __cdecl ProcessUnreadMessages(void*); +	void __cdecl ProcessUnreadMessage(void*); +	void __cdecl ProcessFeeds(void*); +	void __cdecl ProcessNotifications(void*); +	void __cdecl ProcessFriendRequests(void*); +	void __cdecl SearchAckThread(void*); +	void __cdecl SearchIdAckThread(void*); +	void __cdecl ProcessPages(void*); +	void __cdecl LoadLastMessages(void*); + +	// Worker threads +	void __cdecl SignOn(void*); +	void __cdecl ChangeStatus(void*); +	void __cdecl SignOff(void*); +	void __cdecl SetAwayMsgWorker(void*); +	void __cdecl UpdateAvatarWorker(void*); +	void __cdecl SendMsgWorker(void*); +	void __cdecl SendChatMsgWorker(void*); +	void __cdecl SendTypingWorker(void*); +	void __cdecl ReadMessageWorker(void*); +	void __cdecl ReadNotificationWorker(void*); +	void __cdecl DeleteContactFromServer(void*); +	void __cdecl AddContactToServer(void*); +	void __cdecl ApproveContactToServer(void*); +	void __cdecl CancelFriendsRequest(void*); +	void __cdecl SendPokeWorker(void*); + +	// Contacts handling +	bool     IsMyContact(MCONTACT, bool include_chat = false); +	MCONTACT ContactIDToHContact(std::string); +	MCONTACT ChatIDToHContact(std::tstring); +	std::string ThreadIDToContactID(std::string thread_id); +	void FacebookProto::LoadContactInfo(facebook_user* fbu); +	MCONTACT AddToContactList(facebook_user*, ContactType type, bool force_add = false); +	void     SetAllContactStatuses(int status); +	MCONTACT HContactFromAuthEvent(HANDLE hEvent); + +	// Chats handling + 	void AddChat(const TCHAR *id, const TCHAR *name); +	void UpdateChat(const TCHAR *chat_id, const char *id, const char *name, const char *message, DWORD timestamp = 0); +	void RenameChat(const char *chat_id, const char *name); +	bool IsChatContact(const TCHAR *chat_id, const char *id); +	void AddChatContact(const TCHAR *chat_id, const char *id, const char *name); +	void RemoveChatContact(const TCHAR *chat_id, const char *id); +	void SetChatStatus(const char *chat_id, int status); +	char *GetChatUsers(const TCHAR *chat_id); +	void ReceiveMessages(std::vector<facebook_message*> messages, bool local_timestamp, bool check_duplicates = false); + +	// Connection client +	facebook_client facy; // TODO: Refactor to "client" and make dynamic + +	// Helpers +	std::tstring GetAvatarFolder(); +	bool GetDbAvatarInfo(PROTO_AVATAR_INFORMATIONT &ai, std::string *url); +	void CheckAvatarChange(MCONTACT hContact, std::string image_url); +	void ToggleStatusMenuItems(BOOL bEnable); +	void ParseSmileys(std::string message, MCONTACT hContact); +	void OpenUrl(std::string url); +	void SaveName(MCONTACT hContact, const facebook_user *fbu); + +	// Handles, Locks +	HGENMENU m_hMenuRoot, m_hMenuServicesRoot, m_hStatusMind; + +	HANDLE  signon_lock_; +	HANDLE  avatar_lock_; +	HANDLE  log_lock_; +	HANDLE  update_loop_lock_; + +	std::vector<HANDLE> popupClasses; + +	std::string last_status_msg_; +	HANDLE	hSmileysFolder_; +	std::vector<MCONTACT> avatar_queue; + +	static void CALLBACK APC_callback(ULONG_PTR p); + +	// Information providing +	HWND NotifyEvent(TCHAR* title, TCHAR* info, MCONTACT contact, DWORD flags, std::string *url = NULL, std::string *notification_id = NULL); +	void ShowNotifications(); +};  | 
