summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/FacebookRM/src/client.h2
-rw-r--r--protocols/FacebookRM/src/process.cpp112
-rw-r--r--protocols/FacebookRM/src/proto.cpp1807
-rw-r--r--protocols/FacebookRM/src/proto.h486
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();
+};