/* 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" void FacebookProto::ChangeStatus(void*) { ScopedLock s(signon_lock_); ScopedLock b(facy.buddies_lock_); int new_status = m_iDesiredStatus; int old_status = m_iStatus; if (new_status == ID_STATUS_OFFLINE) { // Logout LOG("##### Beginning SignOff process"); m_iStatus = facy.self_.status_id = ID_STATUS_OFFLINE; SetEvent(update_loop_lock_); Netlib_Shutdown(facy.hMsgCon); if (getByte(FACEBOOK_KEY_DISCONNECT_CHAT, DEFAULT_DISCONNECT_CHAT)) facy.chat_state(false); facy.logout(); deleteSetting("LogonTS"); facy.clear_cookies(); facy.buddies.clear(); ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); OnLeaveChat(NULL, NULL); SetAllContactStatuses(ID_STATUS_OFFLINE, true); ToggleStatusMenuItems(false); if (facy.hMsgCon) Netlib_CloseHandle(facy.hMsgCon); facy.hMsgCon = NULL; LOG("##### SignOff complete"); return; } else if (old_status == ID_STATUS_OFFLINE) { // Login SYSTEMTIME t; GetLocalTime(&t); Log("[%d.%d.%d] Using Facebook Protocol RM %s", t.wDay, t.wMonth, t.wYear, __VERSION_STRING); LOG("***** Beginning SignOn process"); m_iStatus = facy.self_.status_id = ID_STATUS_CONNECTING; ProtoBroadcastAck(m_szModuleName,0,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); ResetEvent(update_loop_lock_); if (NegotiateConnection() && facy.home()) { facy.reconnect(); facy.load_friends(); // Process Friends requests ForkThread(&FacebookProto::ProcessFriendRequests, this, NULL); // Get unread messages ForkThread(&FacebookProto::ProcessUnreadMessages, this); // Get notifications ForkThread(&FacebookProto::ProcessNotifications, this); setDword("LogonTS", (DWORD)time(NULL)); ForkThread(&FacebookProto::UpdateLoop, this); ForkThread(&FacebookProto::MessageLoop, this); if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS)) { ForkThread(&FacebookProto::SetAwayMsgWorker, this, NULL); } } else { ProtoBroadcastAck(m_szModuleName,0,ACKTYPE_STATUS,ACKRESULT_FAILED, (HANDLE)old_status,m_iStatus); // Set to offline m_iStatus = m_iDesiredStatus = facy.self_.status_id = ID_STATUS_OFFLINE; ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); LOG("***** SignOn failed"); return; } ToggleStatusMenuItems(true); LOG("***** SignOn complete"); } else if (new_status == ID_STATUS_INVISIBLE) { facy.buddies.clear(); this->SetAllContactStatuses(ID_STATUS_OFFLINE, true); } facy.chat_state(m_iDesiredStatus != ID_STATUS_INVISIBLE); facy.buddy_list(); m_iStatus = facy.self_.status_id = m_iDesiredStatus; ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); LOG("***** ChangeStatus complete"); } /** Return true on success, false on error. */ bool FacebookProto::NegotiateConnection() { LOG("***** Negotiating connection with Facebook"); bool error; std::string user, pass; DBVARIANT dbv = {0}; error = true; if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_LOGIN,&dbv)) { user = dbv.pszVal; db_free(&dbv); error = user.empty(); } if (error) { NotifyEvent(m_tszUserName,TranslateT("Please enter a username."),NULL,FACEBOOK_EVENT_CLIENT); return false; } error = true; if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_PASS,&dbv)) { CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1, reinterpret_cast(dbv.pszVal)); pass = dbv.pszVal; db_free(&dbv); error = pass.empty(); } if (error) { NotifyEvent(m_tszUserName,TranslateT("Please enter a password."),NULL,FACEBOOK_EVENT_CLIENT); return false; } // Load machine name if (!db_get_s(NULL,m_szModuleName,FACEBOOK_KEY_DEVICE_ID,&dbv)) { facy.cookies["datr"] = dbv.pszVal; db_free(&dbv); } // Refresh last time of feeds update facy.last_feeds_update_ = ::time(NULL); // Get info about secured connection facy.https_ = db_get_b(NULL, m_szModuleName, FACEBOOK_KEY_FORCE_HTTPS, DEFAULT_FORCE_HTTPS) != 0; // Create default group for new contacts if (!db_get_ts(NULL, m_szModuleName, FACEBOOK_KEY_DEF_GROUP, &dbv) && lstrlen(dbv.ptszVal) > 0) { CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)dbv.ptszVal); } return facy.login(user, pass); } void FacebookProto::UpdateLoop(void *) { time_t tim = ::time(NULL); LOG(">>>>> Entering Facebook::UpdateLoop[%d]", tim); for (int i = -1; !isOffline(); i = ++i % 50) { if (i != -1) { if (!facy.invisible_) if (!facy.buddy_list()) break; } if (i == 2 && getByte(FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE)) if (!facy.feeds()) break; if (i == 49) ForkThread(&FacebookProto::ProcessFriendRequests, this, NULL); LOG("***** FacebookProto::UpdateLoop[%d] going to sleep...", tim); if (WaitForSingleObjectEx(update_loop_lock_, GetPollRate() * 1000, true) != WAIT_TIMEOUT) break; LOG("***** FacebookProto::UpdateLoop[%d] waking up...", tim); } ResetEvent(update_loop_lock_); LOG("<<<<< Exiting FacebookProto::UpdateLoop[%d]", tim); } void FacebookProto::MessageLoop(void *) { time_t tim = ::time(NULL); LOG(">>>>> Entering Facebook::MessageLoop[%d]", tim); while (facy.channel()) { if (isOffline()) break; LOG("***** FacebookProto::MessageLoop[%d] refreshing...", tim); } LOG("<<<<< Exiting FacebookProto::MessageLoop[%d]", tim); } BYTE FacebookProto::GetPollRate() { BYTE poll_rate = getByte(FACEBOOK_KEY_POLL_RATE, FACEBOOK_DEFAULT_POLL_RATE); return ( (poll_rate >= FACEBOOK_MINIMAL_POLL_RATE && poll_rate <= FACEBOOK_MAXIMAL_POLL_RATE) ? poll_rate : FACEBOOK_DEFAULT_POLL_RATE); }