/* 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::ProcessBuddyList(void* data) { if (data == NULL) return; ScopedLock s(facy.buddies_lock_); std::string* resp = (std::string*)data; if (isOffline()) goto exit; LOG("***** Starting processing buddy list"); CODE_BLOCK_TRY facebook_json_parser* p = new facebook_json_parser(this); p->parse_buddy_list(data, &facy.buddies); delete p; bool use_mobile_status = getByte(FACEBOOK_KEY_LOAD_MOBILE, DEFAULT_LOAD_MOBILE) != 0; for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL;) { facebook_user* fbu = i->data; bool on_mobile = false; char *status; switch (fbu->status_id) { case ID_STATUS_OFFLINE: status = "Offline"; break; case ID_STATUS_ONLINE: status = "Online"; break; case ID_STATUS_ONTHEPHONE: on_mobile = true; status = "Phone"; if (!use_mobile_status) fbu->status_id = ID_STATUS_OFFLINE; break; } LOG(" Now %s: %s", status, fbu->user_id.c_str()); if (!fbu->deleted) { HANDLE hContact = fbu->handle; if (!hContact) hContact = AddToContactList(fbu, CONTACT_FRIEND); DBVARIANT dbv; TCHAR* client = on_mobile ? _T(FACEBOOK_MOBILE) : _T(FACEBOOK_NAME); if (!getTString(hContact, "MirVer", &dbv)) { if (_tcscmp(dbv.ptszVal, client)) setTString(hContact, "MirVer", client); db_free(&dbv); } else setTString(hContact, "MirVer", client); } if (fbu->status_id == ID_STATUS_OFFLINE || fbu->deleted) { if (fbu->handle) setWord(fbu->handle, "Status", ID_STATUS_OFFLINE); std::string to_delete(i->key); i = i->next; facy.buddies.erase(to_delete); } else { i = i->next; if (!fbu->handle) // just been added fbu->handle = AddToContactList(fbu, CONTACT_FRIEND); if (getWord(fbu->handle, "Status", 0) != fbu->status_id) setWord(fbu->handle, "Status", fbu->status_id); if (getDword(fbu->handle, "LastActiveTS", 0) != fbu->last_active) { if (fbu->last_active > 0) setDword(fbu->handle, "LastActiveTS", fbu->last_active); else delSetting(fbu->handle, "LastActiveTS"); } if (getByte(fbu->handle, FACEBOOK_KEY_CONTACT_TYPE, 0) != CONTACT_FRIEND) { setByte(fbu->handle, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_FRIEND); // TODO: remove that popup and use "Contact added you" event? } // Wasn't contact removed from "server-list" someday? if (getDword(fbu->handle, FACEBOOK_KEY_DELETED, 0)) { delSetting(fbu->handle, FACEBOOK_KEY_DELETED); std::string url = FACEBOOK_URL_PROFILE + fbu->user_id; TCHAR* szTitle = mir_utf8decodeT(fbu->real_name.c_str()); NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), fbu->handle, FACEBOOK_EVENT_OTHER, &url); mir_free(szTitle); } // Check avatar change CheckAvatarChange(fbu->handle, fbu->image_url); } } LOG("***** Buddy list processed"); CODE_BLOCK_CATCH LOG("***** Error processing buddy list: %s", e.what()); CODE_BLOCK_END exit: delete resp; } void FacebookProto::ProcessFriendList(void* data) { if (data == NULL) return; std::string* resp = (std::string*)data; LOG("***** Starting processing friend list"); CODE_BLOCK_TRY std::map friends; facebook_json_parser* p = new facebook_json_parser(this); p->parse_friends(data, &friends); delete p; // Check and update old contacts for (HANDLE hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { if ( isChatRoom(hContact)) continue; DBVARIANT dbv; facebook_user *fbu; if (!getString(hContact, FACEBOOK_KEY_ID, &dbv)) { std::string id = dbv.pszVal; db_free(&dbv); std::map< std::string, facebook_user* >::iterator iter; if ((iter = friends.find(id)) != friends.end()) { // Found contact, update it and remove from map fbu = iter->second; DBVARIANT dbv; bool update_required = true; // TODO RM: remove, because contacts cant change it, so its only for "first run" // - but what with contacts, that was added after logon? // Update gender if (getByte(hContact, "Gender", 0) != fbu->gender) setByte(hContact, "Gender", fbu->gender); // Update real name if (!db_get_utf(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv)) { update_required = strcmp(dbv.pszVal, fbu->real_name.c_str()) != 0; db_free(&dbv); } if (update_required) { db_set_utf(hContact, m_szModuleName, FACEBOOK_KEY_NAME, fbu->real_name.c_str()); db_set_utf(hContact, m_szModuleName, FACEBOOK_KEY_NICK, fbu->real_name.c_str()); } if (getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, 0) != CONTACT_FRIEND) { setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_FRIEND); // TODO: remove that popup and use "Contact added you" event? } // Wasn't contact removed from "server-list" someday? if (getDword(hContact, FACEBOOK_KEY_DELETED, 0)) { delSetting(hContact, FACEBOOK_KEY_DELETED); std::string url = FACEBOOK_URL_PROFILE + fbu->user_id; TCHAR* szTitle = mir_utf8decodeT(fbu->real_name.c_str()); NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url); mir_free(szTitle); } // Check avatar change CheckAvatarChange(hContact, fbu->image_url); delete fbu; friends.erase(iter); } else { // Contact was removed from "server-list", notify it // Wasnt we already been notified about this contact? And was this real friend? if (!getDword(hContact, FACEBOOK_KEY_DELETED, 0) && getByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, 0) == CONTACT_FRIEND) { setDword(hContact, FACEBOOK_KEY_DELETED, ::time(NULL)); setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_NONE); std::string contactname = id; if (!db_get_utf(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv)) { contactname = dbv.pszVal; db_free(&dbv); } std::string url = FACEBOOK_URL_PROFILE + id; TCHAR* szTitle = mir_utf8decodeT(contactname.c_str()); NotifyEvent(szTitle, TranslateT("Contact is no longer on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url); mir_free(szTitle); } } } } // Check remain contacts in map and add it to contact list for (std::map< std::string, facebook_user* >::iterator iter = friends.begin(); iter != friends.end(); ++iter) { facebook_user *fbu = iter->second; HANDLE hContact = AddToContactList(fbu, CONTACT_FRIEND, true); // This contact is surely new ...are you sure? } LOG("***** Friend list processed"); CODE_BLOCK_CATCH LOG("***** Error processing friend list: %s", e.what()); CODE_BLOCK_END delete resp; } void FacebookProto::ProcessUnreadMessages(void*) { facy.handle_entry("messages"); int count = 0; std::string page; while (count < 30) // allow max 30 unread threads to be parsed { std::string get_data = "&page=" + page; http::response resp = facy.flap(REQUEST_UNREAD_THREADS, NULL, &get_data); // Process result data facy.validate_response(&resp); if (resp.code == HTTP_CODE_OK) { std::string items = utils::text::source_get_value(&resp.data, 2, "
"); std::string::size_type pos = 0; std::string::size_type pos2 = 0; while ((pos = items.find("id=\"threadlist_row_", pos)) != std::string::npos) { std::string item = items.substr(pos, items.find("
", pos) - pos); pos++; count++; std::string tid = utils::text::source_get_value2(&item, "?tid=", "&\""); if (tid.empty()) continue; ForkThread(&FacebookProto::ProcessUnreadMessage, new std::string(tid)); } page = utils::text::source_get_value(&items, 3, "id=\"see_older_threads\"", "page=", "&"); if (page.empty()) break; // No more results } } } void FacebookProto::ProcessUnreadMessage(void *tid_data) { if (tid_data == NULL) return; // TODO: get them from /ajax/mercury/thread_info.php std::string get_data = "tid=" + *(std::string*)tid_data; delete (std::string*)tid_data; http::response resp = facy.flap(REQUEST_UNREAD_MESSAGES, NULL, &get_data); facy.validate_response(&resp); if (resp.code != HTTP_CODE_OK) { LOG(" !! !! Error when getting messages list"); return; } if (resp.data.find("