From c7cde3a161cdc4714e11bb14187d0aa45c0ad8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20P=C3=B6sel?= Date: Thu, 29 May 2014 12:37:25 +0000 Subject: Facebook: Loading last 30 messages after open contact window git-svn-id: http://svn.miranda-ng.org/main/trunk@9339 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/FacebookRM/src/client.h | 2 + protocols/FacebookRM/src/process.cpp | 112 ++- protocols/FacebookRM/src/proto.cpp | 1807 +++++++++++++++++----------------- protocols/FacebookRM/src/proto.h | 486 ++++----- 4 files changed, 1272 insertions(+), 1135 deletions(-) (limited to 'protocols/FacebookRM') 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 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 messages; + std::map 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::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::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::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 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 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(messages[i]->message_text.c_str()); recv.timestamp = timestamp; ProtoChainRecvMsg(hContact, &recv); @@ -544,6 +651,9 @@ void FacebookProto::ReceiveMessages(std::vector 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 . - -*/ - -#include "common.h" - -FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username) : - PROTO(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::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(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(&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::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(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(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(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(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 . + +*/ + +#include "common.h" + +FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username) : + PROTO(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::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(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(&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::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(data)); + ShowWindow(hDlg, SW_SHOW); + + return 0; +} + +int FacebookProto::OnDbEventRead(WPARAM contactID, LPARAM dbei) +{ + std::map::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(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(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(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 . - -*/ - -#include "constants.h" - -#pragma once - -class FacebookProto : public PROTO -{ -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 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 popupClasses; - - std::string last_status_msg_; - HANDLE hSmileysFolder_; - std::vector 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 . + +*/ + +#include "constants.h" + +#pragma once + +class FacebookProto : public PROTO +{ +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 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 popupClasses; + + std::string last_status_msg_; + HANDLE hSmileysFolder_; + std::vector 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(); +}; -- cgit v1.2.3