summaryrefslogtreecommitdiff
path: root/protocols
diff options
context:
space:
mode:
authorRobert Pösel <robyer@seznam.cz>2014-05-28 14:01:22 +0000
committerRobert Pösel <robyer@seznam.cz>2014-05-28 14:01:22 +0000
commit4c818adaabffd85c54df9c9e4b59a8b441119641 (patch)
tree2df1783d13f720a70d90c34bf43f5cba232c8dbc /protocols
parentb64e6827f700f296faf60c1f370e4e319ec755a5 (diff)
Facebook: Fixed crash when notifying long newsfeeds
git-svn-id: http://svn.miranda-ng.org/main/trunk@9333 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols')
-rw-r--r--protocols/FacebookRM/src/events.cpp233
-rw-r--r--protocols/FacebookRM/src/process.cpp2149
2 files changed, 1195 insertions, 1187 deletions
diff --git a/protocols/FacebookRM/src/events.cpp b/protocols/FacebookRM/src/events.cpp
index 8604d58492..7ebe65983d 100644
--- a/protocols/FacebookRM/src/events.cpp
+++ b/protocols/FacebookRM/src/events.cpp
@@ -1,115 +1,118 @@
-/*
-
-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"
-
-HWND FacebookProto::NotifyEvent(TCHAR* title, TCHAR* info, MCONTACT contact, DWORD flags, std::string *url, std::string *notification_id)
-{
- char name[256];
-
- switch (flags)
- {
- case FACEBOOK_EVENT_CLIENT:
- if (!getByte(FACEBOOK_KEY_EVENT_CLIENT_ENABLE, DEFAULT_EVENT_CLIENT_ENABLE))
- return NULL;
- mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Client");
- flags |= NIIF_WARNING;
- break;
-
- case FACEBOOK_EVENT_NEWSFEED:
- if (!getByte(FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE))
- return NULL;
- mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Newsfeed");
- SkinPlaySound("NewsFeed");
- flags |= NIIF_INFO;
- break;
-
- case FACEBOOK_EVENT_NOTIFICATION:
- if (!getByte(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
- return NULL;
- mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Notification");
- SkinPlaySound("Notification");
- flags |= NIIF_INFO;
- break;
-
- case FACEBOOK_EVENT_OTHER:
- if (!getByte(FACEBOOK_KEY_EVENT_OTHER_ENABLE, DEFAULT_EVENT_OTHER_ENABLE))
- return NULL;
- mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Other");
- SkinPlaySound("OtherEvent");
- flags |= NIIF_INFO;
- break;
- }
-
- if (!getByte(FACEBOOK_KEY_SYSTRAY_NOTIFY,DEFAULT_SYSTRAY_NOTIFY))
- {
- if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) {
-
- // TODO: if popup with particular ID is already showed, just update his content
- // ... but f***ed up Popup Classes won't allow it now - they need to return hPopupWindow somehow
- /* if (popup exists) {
- if (PUChangeTextT(hWndPopup, info) > 0) // success
- return;
- }*/
-
- POPUPDATACLASS pd = { sizeof(pd) };
- pd.ptszTitle = title;
- pd.ptszText = info;
- pd.pszClassName = name;
- pd.hContact = contact;
-
- if (url != NULL || notification_id != NULL) {
- popup_data *data = new popup_data(this);
- if (url != NULL)
- data->url = *url;
- if (notification_id != NULL)
- data->notification_id = *notification_id;
- pd.PluginData = data;
- }
-
- return (HWND) CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&pd);
- }
- } else {
- if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY))
- {
- MIRANDASYSTRAYNOTIFY err;
- int niif_flags = flags;
- REMOVE_FLAG(niif_flags, FACEBOOK_EVENT_CLIENT |
- FACEBOOK_EVENT_NEWSFEED |
- FACEBOOK_EVENT_NOTIFICATION |
- FACEBOOK_EVENT_OTHER);
- err.szProto = m_szModuleName;
- err.cbSize = sizeof(err);
- err.dwInfoFlags = NIIF_INTERN_TCHAR | niif_flags;
- err.tszInfoTitle = title;
- err.tszInfo = info;
- err.uTimeout = 10000;
- if (CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM) & err) == 0)
- return NULL;
- }
- }
-
- if (FLAG_CONTAINS(flags, FACEBOOK_EVENT_CLIENT))
- MessageBox(NULL, info, title, MB_OK | MB_ICONINFORMATION);
-
- return NULL;
-}
+/*
+
+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"
+
+HWND FacebookProto::NotifyEvent(TCHAR* title, TCHAR* info, MCONTACT contact, DWORD flags, std::string *url, std::string *notification_id)
+{
+ if (title == NULL || info == NULL)
+ return NULL;
+
+ char name[256];
+
+ switch (flags)
+ {
+ case FACEBOOK_EVENT_CLIENT:
+ if (!getByte(FACEBOOK_KEY_EVENT_CLIENT_ENABLE, DEFAULT_EVENT_CLIENT_ENABLE))
+ return NULL;
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Client");
+ flags |= NIIF_WARNING;
+ break;
+
+ case FACEBOOK_EVENT_NEWSFEED:
+ if (!getByte(FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE))
+ return NULL;
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Newsfeed");
+ SkinPlaySound("NewsFeed");
+ flags |= NIIF_INFO;
+ break;
+
+ case FACEBOOK_EVENT_NOTIFICATION:
+ if (!getByte(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
+ return NULL;
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Notification");
+ SkinPlaySound("Notification");
+ flags |= NIIF_INFO;
+ break;
+
+ case FACEBOOK_EVENT_OTHER:
+ if (!getByte(FACEBOOK_KEY_EVENT_OTHER_ENABLE, DEFAULT_EVENT_OTHER_ENABLE))
+ return NULL;
+ mir_snprintf(name, SIZEOF(name), "%s_%s", m_szModuleName, "Other");
+ SkinPlaySound("OtherEvent");
+ flags |= NIIF_INFO;
+ break;
+ }
+
+ if (!getByte(FACEBOOK_KEY_SYSTRAY_NOTIFY,DEFAULT_SYSTRAY_NOTIFY))
+ {
+ if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) {
+
+ // TODO: if popup with particular ID is already showed, just update his content
+ // ... but f***ed up Popup Classes won't allow it now - they need to return hPopupWindow somehow
+ /* if (popup exists) {
+ if (PUChangeTextT(hWndPopup, info) > 0) // success
+ return;
+ }*/
+
+ POPUPDATACLASS pd = { sizeof(pd) };
+ pd.ptszTitle = title;
+ pd.ptszText = info;
+ pd.pszClassName = name;
+ pd.hContact = contact;
+
+ if (url != NULL || notification_id != NULL) {
+ popup_data *data = new popup_data(this);
+ if (url != NULL)
+ data->url = *url;
+ if (notification_id != NULL)
+ data->notification_id = *notification_id;
+ pd.PluginData = data;
+ }
+
+ return (HWND) CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&pd);
+ }
+ } else {
+ if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY))
+ {
+ MIRANDASYSTRAYNOTIFY err;
+ int niif_flags = flags;
+ REMOVE_FLAG(niif_flags, FACEBOOK_EVENT_CLIENT |
+ FACEBOOK_EVENT_NEWSFEED |
+ FACEBOOK_EVENT_NOTIFICATION |
+ FACEBOOK_EVENT_OTHER);
+ err.szProto = m_szModuleName;
+ err.cbSize = sizeof(err);
+ err.dwInfoFlags = NIIF_INTERN_TCHAR | niif_flags;
+ err.tszInfoTitle = title;
+ err.tszInfo = info;
+ err.uTimeout = 10000;
+ if (CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM) & err) == 0)
+ return NULL;
+ }
+ }
+
+ if (FLAG_CONTAINS(flags, FACEBOOK_EVENT_CLIENT))
+ MessageBox(NULL, info, title, MB_OK | MB_ICONINFORMATION);
+
+ return NULL;
+}
diff --git a/protocols/FacebookRM/src/process.cpp b/protocols/FacebookRM/src/process.cpp
index 26d7829b0f..11991db5b4 100644
--- a/protocols/FacebookRM/src/process.cpp
+++ b/protocols/FacebookRM/src/process.cpp
@@ -1,1072 +1,1077 @@
-/*
-
-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"
-
-void FacebookProto::ProcessBuddyList(void*)
-{
- ScopedLock s(facy.buddies_lock_);
-
- if (isOffline())
- return;
-
- facy.handle_entry("ProcessBuddyList");
-
- // Prepare update data
- std::string post_data = "user=" + facy.self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + facy.dtsg_ + "&__user=" + facy.self_.user_id + "&cached_user_info_ids=";
-
- int counter = 0;
- for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL; i = i->next, counter++)
- {
- post_data += i->data->user_id + "%2C";
- }
-
- // Get buddy list
- http::response resp = facy.flap(REQUEST_BUDDY_LIST, &post_data);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("buddy_list");
- return;
- }
-
- debugLogA("***** Starting processing buddy list");
-
- CODE_BLOCK_TRY
-
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_buddy_list(&resp.data, &facy.buddies);
- delete p;
-
- for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL;)
- {
- facebook_user* fbu = i->data;
-
- std::string status;
- switch (fbu->status_id) {
- case ID_STATUS_OFFLINE:
- status = "Offline"; break;
- case ID_STATUS_ONLINE:
- status = "Online"; break;
- case ID_STATUS_ONTHEPHONE:
- status = "Mobile"; break;
- }
-
- if (fbu->idle)
- status += " (idle)";
-
- debugLogA(" Now %s: %s", status.c_str(), fbu->user_id.c_str());
-
- if (!fbu->deleted)
- {
- if (!fbu->handle) // just been added
- fbu->handle = AddToContactList(fbu, CONTACT_FRIEND);
-
- ptrT client( getTStringA(fbu->handle, "MirVer"));
- if (!client || _tcscmp(client, fbu->getMirVer()))
- setTString(fbu->handle, "MirVer", fbu->getMirVer());
-
- if (getDword(fbu->handle, "IdleTS", 0) != fbu->last_active) {
- if ((fbu->idle || fbu->status_id == ID_STATUS_OFFLINE) && fbu->last_active > 0)
- setDword(fbu->handle, "IdleTS", fbu->last_active);
- else
- delSetting(fbu->handle, "IdleTS");
- }
- }
-
- 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;
-
- ptrT szTitle( mir_utf8decodeT(fbu->real_name.c_str()));
- NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), fbu->handle, FACEBOOK_EVENT_OTHER, &url);
- }
-
- // Check avatar change
- CheckAvatarChange(fbu->handle, fbu->image_url);
- }
- }
-
- debugLogA("***** Buddy list processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing buddy list: %s", e.what());
-
- CODE_BLOCK_END
-}
-
-void FacebookProto::ProcessFriendList(void*)
-{
- ScopedLock s(facy.buddies_lock_);
-
- if (isOffline())
- return;
-
- facy.handle_entry("load_friends");
-
- // Get buddy list
- http::response resp = facy.flap(REQUEST_LOAD_FRIENDS);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("load_friends");
- return;
- }
-
- debugLogA("***** Starting processing friend list");
-
- CODE_BLOCK_TRY
-
- std::map<std::string, facebook_user*> friends;
-
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_friends(&resp.data, &friends);
- delete p;
-
-
- // Check and update old contacts
- for (MCONTACT 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;
-
- // 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);
-
- // TODO: remove this in some future version?
- // Remove old useless "RealName" field
- ptrA realname(getStringA(hContact, "RealName"));
- if (realname != NULL) {
- delSetting(hContact, "RealName");
- }
-
- // Update real name and nick
- if (!fbu->real_name.empty()) {
- SaveName(hContact, fbu);
- }
-
- 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;
-
- ptrT szTitle( mir_utf8decodeT(fbu->real_name.c_str()));
- NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url);
- }
-
- // Check avatar change
- CheckAvatarChange(hContact, fbu->image_url);
-
- // Mark this contact as deleted ("processed") and delete them later (as there may be some duplicit contacts to use)
- fbu->deleted = true;
- } 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 (!getStringUtf(hContact, FACEBOOK_KEY_NICK, &dbv)) {
- contactname = dbv.pszVal;
- db_free(&dbv);
- }
-
- std::string url = FACEBOOK_URL_PROFILE + id;
-
- ptrT szTitle( mir_utf8decodeT(contactname.c_str()));
- NotifyEvent(szTitle, TranslateT("Contact is no longer on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url);
- }
- }
- }
- }
-
- // Check remaining contacts in map and add them to contact list
- for (std::map< std::string, facebook_user* >::iterator it = friends.begin(); it != friends.end(); ) {
- if (!it->second->deleted)
- AddToContactList(it->second, CONTACT_FRIEND, true); // we know this contact doesn't exists, so we force add it
-
- delete it->second;
- it = friends.erase(it);
- }
- friends.clear();
-
- debugLogA("***** Friend list processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing friend list: %s", e.what());
-
- CODE_BLOCK_END
-}
-
-void FacebookProto::ProcessUnreadMessages(void*)
-{
- facy.handle_entry("ProcessUnreadMessages");
-
- // receive messages from all folders by default, use hidden setting to receive only inbox messages
- bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
-
- std::string data = "folders[0]=inbox";
- if (!inboxOnly)
- data += "&folders[1]=other";
- 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";
-
- http::response resp = facy.flap(REQUEST_UNREAD_THREADS, &data);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("ProcessUnreadMessages");
- return;
- }
-
- CODE_BLOCK_TRY
-
- std::vector<std::string> threads;
-
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_unread_threads(&resp.data, &threads, inboxOnly);
- delete p;
-
- ForkThread(&FacebookProto::ProcessUnreadMessage, new std::vector<std::string>(threads));
-
- debugLogA("***** Unread threads list processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing unread threads list: %s", e.what());
-
- CODE_BLOCK_END
-
- facy.handle_success("ProcessUnreadMessages");
-}
-
-void FacebookProto::ProcessUnreadMessage(void *p)
-{
- if (p == NULL)
- return;
-
- facy.handle_entry("ProcessUnreadMessage");
-
- std::vector<std::string> threads = *(std::vector<std::string>*)p;
- delete (std::vector<std::string>*)p;
-
- int offset = 0;
- int limit = 21;
-
- bool local_timestamp = getBool(FACEBOOK_KEY_LOCAL_TIMESTAMP_UNREAD, 0);
-
- // receive messages from all folders by default, use hidden setting to receive only inbox messages
- bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
-
- http::response resp;
-
- while (!threads.empty()) {
- 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";
-
- for (std::vector<std::string>::size_type i = 0; i < threads.size(); i++) {
- std::string thread_id = utils::url::encode(threads[i]);
-
- // request messages from thread
- data += "&messages[thread_ids][" + thread_id;
- data += "][offset]=" + utils::conversion::to_string(&offset, UTILS_CONV_SIGNED_NUMBER);
- data += "&messages[thread_ids][" + thread_id;
- data += "][limit]=" + utils::conversion::to_string(&limit, UTILS_CONV_SIGNED_NUMBER);
-
- // request info about thread
- data += "&threads[thread_ids][" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
- data += "]=" + thread_id;
- }
-
- resp = facy.flap(REQUEST_THREAD_INFO, &data);
-
- if (resp.code == HTTP_CODE_OK) {
-
- 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, true, inboxOnly, limit);
- 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();
-
- ReceiveMessages(messages, local_timestamp, true);
-
- debugLogA("***** Unread messages processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing unread messages: %s", e.what());
-
- CODE_BLOCK_END
-
- facy.handle_success("ProcessUnreadMessage");
- } else {
- facy.handle_error("ProcessUnreadMessage");
- }
-
- offset += limit;
- limit = 20; // TODO: use better limits?
-
- threads.clear(); // TODO: if we have limit messages from one user, there may be more unread messages... continue with it... otherwise remove that threadd from threads list -- or do it in json parser? hm = allow more than "limit" unread messages to be parsed
- }
-}
-
-void FacebookProto::ReceiveMessages(std::vector<facebook_message*> messages, bool local_timestamp, bool check_duplicates)
-{
- bool naseemsSpamMode = getBool(FACEBOOK_KEY_NASEEMS_SPAM_MODE, false);
-
- // TODO: make this checking more lightweight as now it is not effective at all...
- if (check_duplicates) {
- // 1. check if there are some message that we already have (compare FACEBOOK_KEY_MESSAGE_ID = last received message ID)
- for (std::vector<facebook_message*>::size_type i = 0; i < messages.size(); i++) {
-
- MCONTACT hContact = messages[i]->isChat
- ? ChatIDToHContact(std::tstring(_A2T(messages[i]->thread_id.c_str())))
- : ContactIDToHContact(messages[i]->user_id);
-
- if (hContact == NULL)
- continue;
-
- ptrA lastId(getStringA(hContact, FACEBOOK_KEY_MESSAGE_ID));
- if (lastId == NULL)
- continue;
-
- if (!messages[i]->message_id.compare(lastId)) {
- // Equal, ignore all older messages (including this) from same contact
- for (std::vector<facebook_message*>::size_type j = 0; j < messages.size(); j++) {
- bool equalsId = messages[i]->isChat
- ? (messages[j]->thread_id == messages[i]->thread_id)
- : (messages[j]->user_id == messages[i]->user_id);
-
- if (equalsId && messages[j]->time <= messages[i]->time)
- messages[j]->flag_ = 1;
- }
- }
- }
-
- // 2. remove all marked messages from list
- for (std::vector<facebook_message*>::iterator it = messages.begin(); it != messages.end();) {
- if ((*it)->flag_ == 1)
- it = messages.erase(it);
- else
- ++it;
- }
- }
-
- for(std::vector<facebook_message*>::size_type i = 0; i < messages.size(); i++) {
- DWORD timestamp = local_timestamp || !messages[i]->time ? ::time(NULL) : messages[i]->time;
-
- if (messages[i]->isChat) {
- // Multi-user message
- debugLogA(" Got chat message: %s", messages[i]->message_text.c_str());
-
- std::tstring tthread_id = _A2T(messages[i]->thread_id.c_str());
-
- // RM TODO: better use check if chatroom exists/is in db/is online... no?
- // like: if (ChatIDToHContact(tthread_id) == NULL) {
- if (GetChatUsers(tthread_id.c_str()) == NULL) {
- // In Naseem's spam mode we ignore outgoing messages sent from other instances
- if (naseemsSpamMode && !messages[i]->isIncoming) {
- delete messages[i];
- continue;
- }
-
- // TODO: We don't have this chat, lets load info about it (name, list of users, last messages, etc.)
- //LoadChatInfo(&fbu); // In this method use json's parse_chat_info
- std::tstring tthread_name = tthread_id; // TODO: use correct name for chat, not thread_id
-
- AddChat(tthread_id.c_str(), tthread_name.c_str());
- }
-
- MCONTACT hChatContact = ChatIDToHContact(tthread_id);
-
- // Save last (this) message ID
- setString(hChatContact, FACEBOOK_KEY_MESSAGE_ID, messages[i]->message_id.c_str());
-
- // Save TID if not exists already
- ptrA tid(getStringA(hChatContact, FACEBOOK_KEY_TID));
- if (!tid || strcmp(tid, messages[i]->thread_id.c_str()))
- setString(hChatContact, FACEBOOK_KEY_TID, messages[i]->thread_id.c_str());
-
- ParseSmileys(messages[i]->message_text, hChatContact);
-
- // TODO: support also system messages (rename chat, user quit, etc.)! (here? or it is somewhere else?
- // ... we must add some new "type" field into facebook_message structure and use it also for Pokes and similar)
- UpdateChat(tthread_id.c_str(), messages[i]->user_id.c_str(), messages[i]->sender_name.c_str(), messages[i]->message_text.c_str(), timestamp);
-
- // Automatically mark message as read because chatroom doesn't support onRead event (yet)
- ForkThread(&FacebookProto::ReadMessageWorker, (void*)hChatContact);
- } else {
- // Single-user message
- debugLogA(" Got message: %s", messages[i]->message_text.c_str());
-
- facebook_user fbu;
- fbu.user_id = messages[i]->user_id;
- fbu.real_name = messages[i]->sender_name;
-
- MCONTACT hContact = ContactIDToHContact(fbu.user_id);
- if (hContact == NULL) {
- // In Naseem's spam mode we ignore outgoing messages sent from other instances
- if (naseemsSpamMode && !messages[i]->isIncoming) {
- delete messages[i];
- continue;
- }
-
- // We don't have this contact, lets load info about him
- LoadContactInfo(&fbu);
-
- hContact = AddToContactList(&fbu, CONTACT_NONE);
- }
-
- // Save last (this) message ID
- setString(hContact, FACEBOOK_KEY_MESSAGE_ID, messages[i]->message_id.c_str());
-
- // Save TID if not exists already
- ptrA tid(getStringA(hContact, FACEBOOK_KEY_TID));
- if (!tid || strcmp(tid, messages[i]->thread_id.c_str()))
- setString(hContact, FACEBOOK_KEY_TID, messages[i]->thread_id.c_str());
-
- ParseSmileys(messages[i]->message_text, hContact);
-
- if (messages[i]->isIncoming) {
- PROTORECVEVENT recv = { 0 };
- recv.flags = PREF_UTF;
- recv.szMessage = const_cast<char*>(messages[i]->message_text.c_str());
- recv.timestamp = timestamp;
- ProtoChainRecvMsg(hContact, &recv);
- } else {
- DBEVENTINFO dbei = { 0 };
- dbei.cbSize = sizeof(dbei);
- dbei.eventType = EVENTTYPE_MESSAGE;
- dbei.flags = DBEF_SENT | DBEF_UTF;
- dbei.szModule = m_szModuleName;
- dbei.timestamp = timestamp;
- dbei.cbBlob = (DWORD)messages[i]->message_text.length() + 1;
- dbei.pBlob = (PBYTE)messages[i]->message_text.c_str();
- db_event_add(hContact, &dbei);
- }
- }
- delete messages[i];
- }
- messages.clear();
-}
-
-void FacebookProto::ProcessMessages(void* data)
-{
- if (data == NULL)
- return;
-
- std::string* resp = (std::string*)data;
-
- // receive messages from all folders by default, use hidden setting to receive only inbox messages
- bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
-
- if (isOffline())
- goto exit;
-
- debugLogA("***** Starting processing messages");
-
- CODE_BLOCK_TRY
-
- std::vector< facebook_message* > messages;
-
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_messages(data, &messages, &facy.notifications, inboxOnly);
- delete p;
-
- bool local_timestamp = getBool(FACEBOOK_KEY_LOCAL_TIMESTAMP, 0);
-
- ReceiveMessages(messages, local_timestamp);
-
- ShowNotifications();
-
- debugLogA("***** Messages processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing messages: %s", e.what());
-
- CODE_BLOCK_END
-
-exit:
- delete resp;
-}
-
-void FacebookProto::ShowNotifications() {
- if (!getByte(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
- return;
-
- for (std::map<std::string, facebook_notification*>::iterator it = facy.notifications.begin(); it != facy.notifications.end(); ++it) {
- if (it->second != NULL && !it->second->seen) {
- debugLogA(" Got notification: %s", it->second->text.c_str());
- ptrT szText(mir_utf8decodeT(it->second->text.c_str()));
- it->second->hWndPopup = NotifyEvent(m_tszUserName, szText, ContactIDToHContact(it->second->user_id), FACEBOOK_EVENT_NOTIFICATION, &it->second->link, &it->second->id);
- it->second->seen = true;
- }
- }
-}
-
-void FacebookProto::ProcessNotifications(void*)
-{
- if (isOffline())
- return;
-
- facy.handle_entry("notifications");
-
- // Get notifications
- http::response resp = facy.flap(REQUEST_NOTIFICATIONS);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("notifications");
- return;
- }
-
-
- // Process notifications
- debugLogA("***** Starting processing notifications");
-
- CODE_BLOCK_TRY
-
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_notifications(&(resp.data), &facy.notifications);
- delete p;
-
- ShowNotifications();
-
- debugLogA("***** Notifications processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing notifications: %s", e.what());
-
- CODE_BLOCK_END
-}
-
-void FacebookProto::ProcessFriendRequests(void*)
-{
- facy.handle_entry("friendRequests");
-
- // Get notifications
- http::response resp = facy.flap(REQUEST_LOAD_REQUESTS);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("friendRequests");
- return;
- }
-
- // Parse it
- std::string reqs = utils::text::source_get_value(&resp.data, 2, "class=\"mRequestItem", "al aps\">");
-
- std::string::size_type pos = 0;
- std::string::size_type pos2 = 0;
- bool last = false;
-
- while (!last && !reqs.empty()) {
- std::string req;
- if ((pos2 = reqs.find("class=\"mRequestItem", pos)) != std::string::npos) {
- req = reqs.substr(pos, pos2 - pos);
- pos = pos2 + 19;
- } else {
- req = reqs.substr(pos);
- last = true;
- }
-
- std::string get = utils::text::source_get_value(&req, 3, "<form", "action=\"", "\">");
- std::string time = utils::text::source_get_value2(&get, "seenrequesttime=", "&\"");
-
- facebook_user *fbu = new facebook_user();
- fbu->real_name = utils::text::source_get_value(&req, 2, "class=\"actor\">", "</");
- fbu->user_id = utils::text::source_get_value(&get, 2, "id=", "&");
-
- if (fbu->user_id.length() && fbu->real_name.length())
- {
- MCONTACT hContact = AddToContactList(fbu, CONTACT_APPROVE);
- setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_APPROVE);
-
- bool seen = false;
-
- DBVARIANT dbv;
- if (!getString(hContact, "RequestTime", &dbv)) {
- seen = !strcmp(dbv.pszVal, time.c_str());
- db_free(&dbv);
- }
-
- if (!seen) {
- // This is new request
- setString(hContact, "RequestTime", time.c_str());
-
- //blob is: uin(DWORD), hContact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)
- //blob is: 0(DWORD), hContact(HANDLE), nick(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ)
- DBEVENTINFO dbei = {0};
- dbei.cbSize = sizeof(DBEVENTINFO);
- dbei.szModule = m_szModuleName;
- dbei.timestamp = ::time(NULL);
- dbei.flags = DBEF_UTF;
- dbei.eventType = EVENTTYPE_AUTHREQUEST;
- dbei.cbBlob = (DWORD)(sizeof(DWORD)*2 + fbu->real_name.length() + 5);
-
- PBYTE pCurBlob = dbei.pBlob = (PBYTE) mir_alloc(dbei.cbBlob);
- *(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD); // UID
- *(PDWORD)pCurBlob = (DWORD)hContact; pCurBlob += sizeof(DWORD); // Contact Handle
- strcpy((char*)pCurBlob, fbu->real_name.data()); pCurBlob += fbu->real_name.length()+1; // Nickname
- *pCurBlob = '\0'; pCurBlob++; // First Name
- *pCurBlob = '\0'; pCurBlob++; // Last Name
- *pCurBlob = '\0'; pCurBlob++; // E-mail
- *pCurBlob = '\0'; // Reason
-
- db_event_add(0, &dbei);
-
- debugLogA(" (New) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
- } else {
- debugLogA(" (Old) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
- }
- } else {
- debugLogA(" !!! Wrong friendship request");
- debugLogA("%s", req.c_str());
- }
- }
-
- facy.handle_success("friendRequests");
-}
-
-void FacebookProto::ProcessFeeds(void* data)
-{
- if (!isOnline())
- return;
-
- facy.handle_entry("feeds");
-
- // Get feeds
- http::response resp = facy.flap(REQUEST_FEEDS);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("feeds");
- return;
- }
-
- if (resp.data.empty() /*|| resp.data.find("\"num_stories\":0") != std::string::npos*/)
- return;
-
- CODE_BLOCK_TRY
-
- debugLogA("***** Starting processing feeds");
-
- std::vector< facebook_newsfeed* > news;
-
- std::string::size_type pos = 0;
- UINT limit = 0;
-
- DWORD new_time = facy.last_feeds_update_;
-
- while ((pos = resp.data.find("<div class=\"userContentWrapper", pos)) != std::string::npos && limit <= 25)
- {
- /*std::string::size_type pos2 = resp.data.find("<div class=\"userContentWrapper", pos+5);
- if (pos2 == std::string::npos)
- pos2 = resp.data.length();
-
- std::string post = resp.data.substr(pos, pos2 - pos);*/
- std::string post = resp.data.substr(pos, resp.data.find("</form>", pos) - pos);
- pos += 5;
-
- std::string post_header = utils::text::source_get_value(&post, 3, "<h5", ">", "</h5>");
- std::string post_message = utils::text::source_get_value(&post, 3, " userContent\"", ">", "<form");
- std::string post_link = utils::text::source_get_value(&post, 4, "</h5>", "<a", "href=\"", "\"");
- //std::string post_attach = utils::text::source_get_value(&post, 4, "<div class=", "uiStreamAttachments", ">", "<form");
-
- std::string post_time = utils::text::source_get_value(&post, 3, "</h5>", "<abbr", "</a>");
-
- std::string time = utils::text::source_get_value(&post_time, 2, "data-utime=\"", "\"");
- std::string time_text = utils::text::source_get_value(&post_time, 2, ">", "</abbr>");
-
- DWORD ttime = utils::conversion::to_timestamp(time);
- if (ttime > facy.last_feeds_update_)
- new_time = ttime;
- else
- continue;
-
- std::string post_place = utils::text::source_get_value(&post, 4, "</abbr>", "<a", ">", "</a>");
-
- std::string premsg = "\n" + time_text;
-
- post_place = utils::text::trim(
- utils::text::remove_html(post_place));
- if (!post_place.empty()) {
- premsg += " - " + post_place;
- }
- premsg += "\n";
-
- // in title keep only name, end of events like "X shared link" put into message
- std::string::size_type pos2 = post_header.find("‎");
-
- if (pos2 != std::string::npos) {
- utils::text::replace_first(&post_header, "‎", " → ");
- } else {
- pos2 = post_header.find("</a></");
- if (pos2 != std::string::npos) {
- pos2 += 4;
- std::string a = utils::text::trim(utils::text::remove_html(post_header.substr(pos2, post_header.length() - pos2)));
- if (a.length() > 2)
- premsg += a;
- post_header = post_header.substr(0, pos2);
- }
- }
-
- // Strip "Translate" link
- pos2 = post_message.find("role=\"button\">");
- if (pos2 != std::string::npos) {
- post_message = post_message.substr(0, pos2 + 14);
- }
-
- post_message = premsg + post_message;
-
- // append attachement to message (if any)
- //post_message += utils::text::trim(post_attach);
-
- facebook_newsfeed* nf = new facebook_newsfeed;
-
- nf->title = utils::text::trim(
- utils::text::special_expressions_decode(
- utils::text::remove_html(post_header)));
-
- nf->user_id = utils::text::source_get_value(&post_header, 2, "user.php?id=", "&amp;");
-
- nf->link = utils::text::special_expressions_decode(post_link);
- //utils::text::source_get_value(&post_link, 2, "href=\\\"", "\\\">"));
-
- nf->text = utils::text::trim(
- utils::text::special_expressions_decode(
- utils::text::remove_html(
- utils::text::edit_html(post_message))));
-
- if (!nf->title.length() || !nf->text.length())
- {
- delete nf;
- continue;
- }
-
- if (nf->text.length() > 500)
- {
- nf->text = nf->text.substr(0, 500);
- nf->text += "...";
- }
-
- news.push_back(nf);
- pos++;
- limit++;
- }
-
- for(std::vector<facebook_newsfeed*>::size_type i=0; i<news.size(); i++)
- {
- debugLogA(" Got newsfeed: %s %s", news[i]->title.c_str(), news[i]->text.c_str());
- ptrT szTitle( mir_utf8decodeT(news[i]->title.c_str()));
- ptrT szText( mir_utf8decodeT(news[i]->text.c_str()));
- NotifyEvent(szTitle,szText,this->ContactIDToHContact(news[i]->user_id),FACEBOOK_EVENT_NEWSFEED, &news[i]->link);
- delete news[i];
- }
- news.clear();
-
- this->facy.last_feeds_update_ = new_time;
- //this->facy.last_feeds_update_ = ::time(NULL);
-
- debugLogA("***** Feeds processed");
-
- CODE_BLOCK_CATCH
-
- debugLogA("***** Error processing feeds: %s", e.what());
-
- CODE_BLOCK_END
-
- facy.handle_success("feeds");
-}
-
-void FacebookProto::ProcessPages(void *data)
-{
- if (!getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
- return;
-
- facy.handle_entry("load_pages");
-
- // Get feeds
- http::response resp = facy.flap(REQUEST_PAGES);
-
- if (resp.code != HTTP_CODE_OK) {
- facy.handle_error("load_pages");
- return;
- }
-
- std::string content = utils::text::source_get_value(&resp.data, 2, "id=\"bookmarksSeeAllSection\"", "</code>");
-
- std::string::size_type start, end;
- start = content.find("<li", 0);
- while (start != std::string::npos) {
- end = content.find("<li", start + 1);
- if (end == std::string::npos)
- end = content.length();
-
- std::string item = content.substr(start, end - start);
- //item = utils::text::source_get_value(&item, 2, "data-gt=", ">");
-
- start = content.find("<li", start + 1);
-
- std::string id = utils::text::source_get_value(&item, 3, "data-gt=", "bmid&quot;:&quot;", "&quot;");
- std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&item, 3, "data-gt=", "title=\"", "\"")));
- std::string href = utils::text::source_get_value(&item, 3, "data-gt=", "href=\"", "\"");
-
- // Ignore pages channel
- if (href.find("/pages/feed") != std::string::npos)
- continue;
-
- if (id.empty() || title.empty())
- continue;
-
- debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
- facy.pages[id] = title;
- }
-
- facy.handle_success("load_pages");
-}
-
-void FacebookProto::SearchAckThread(void *targ)
-{
- facy.handle_entry("searchAckThread");
-
- int count = 0;
-
- char *arg = mir_utf8encodeT((TCHAR*)targ);
- std::string search = utils::url::encode(arg);
- std::string ssid;
-
- while (count < 50 && !isOffline())
- {
- std::string get_data = search + "&s=" + utils::conversion::to_string(&count, UTILS_CONV_UNSIGNED_NUMBER);
- if (!ssid.empty())
- get_data += "&ssid=" + ssid;
-
- http::response resp = facy.flap(REQUEST_SEARCH, NULL, &get_data);
-
- if (resp.code == HTTP_CODE_OK)
- {
- std::string items = utils::text::source_get_value(&resp.data, 2, "<body", "</body>");
-
- std::string::size_type pos = 0;
- std::string::size_type pos2 = 0;
-
- while ((pos = items.find("<td class=\"pic\">", pos)) != std::string::npos) {
- std::string item = items.substr(pos, items.find("</tr>", pos) - pos);
- pos++; count++;
-
- std::string id = utils::text::source_get_value2(&item, "?id=", "&\"");
- if (id.empty())
- id = utils::text::source_get_value2(&item, "?ids=", "&\"");
-
- std::string name = utils::text::source_get_value(&item, 4, "<td class=\"name\">", "<a", ">", "</");
- std::string surname;
- std::string nick;
- std::string common = utils::text::source_get_value(&item, 2, "<span class=\"mfss fcg\">", "</span>");
-
- if ((pos2 = name.find("<span class=\"alternate_name\">")) != std::string::npos) {
- nick = name.substr(pos2 + 30, name.length() - pos2 - 31); // also remove brackets around nickname
- name = name.substr(0, pos2 - 1);
- }
-
- if ((pos2 = name.find(" ")) != std::string::npos) {
- surname = name.substr(pos2 + 1, name.length() - pos2 - 1);
- name = name.substr(0, pos2);
- }
-
- // ignore self contact and empty ids
- if (id.empty() || id == facy.self_.user_id)
- continue;
-
- ptrT tid( mir_utf8decodeT(id.c_str()));
- ptrT tname( mir_utf8decodeT(utils::text::special_expressions_decode(name).c_str()));
- ptrT tsurname( mir_utf8decodeT(utils::text::special_expressions_decode(surname).c_str()));
- ptrT tnick( mir_utf8decodeT(utils::text::special_expressions_decode(nick).c_str()));
- ptrT tcommon( mir_utf8decodeT(utils::text::special_expressions_decode(common).c_str()));
-
- PROTOSEARCHRESULT isr = {0};
- isr.cbSize = sizeof(isr);
- isr.flags = PSR_TCHAR;
- isr.id = tid;
- isr.nick = tnick;
- isr.firstName = tname;
- isr.lastName = tsurname;
- isr.email = tcommon;
-
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
- }
-
- ssid = utils::text::source_get_value(&items, 3, "id=\"more_objects\"", "ssid=", "&");
- if (ssid.empty())
- break; // No more results
- }
- }
-
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
-
- facy.handle_success("searchAckThread");
-
- mir_free(targ);
- mir_free(arg);
-}
-
-void FacebookProto::SearchIdAckThread(void *targ)
-{
- facy.handle_entry("searchIdAckThread");
-
- char *arg = mir_utf8encodeT((TCHAR*)targ);
- std::string search = utils::url::encode(arg) + "?";
-
- if (!isOffline())
- {
- http::response resp = facy.flap(REQUEST_USER_INFO, NULL, &search);
-
- if (resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end()) {
- search = utils::text::source_get_value(&resp.headers["Location"], 2, FACEBOOK_SERVER_MOBILE"/", "_rdr", true);
- resp = facy.flap(REQUEST_USER_INFO, NULL, &search);
- }
-
- if (resp.code == HTTP_CODE_OK)
- {
- std::string about = utils::text::source_get_value(&resp.data, 2, "<div class=\"timeline", "id=\"footer");
-
- std::string id = utils::text::source_get_value2(&about, ";id=", "&\"");
- if (id.empty())
- id = utils::text::source_get_value2(&about, "?bid=", "&\"");
- std::string name = utils::text::source_get_value(&about, 3, "class=\"profileName", ">", "</");
- std::string surname;
-
- std::string::size_type pos;
- if ((pos = name.find(" ")) != std::string::npos) {
- surname = name.substr(pos + 1, name.length() - pos - 1);
- name = name.substr(0, pos);
- }
-
- // ignore self contact and empty ids
- if (!id.empty() && id != facy.self_.user_id){
- ptrT tid( mir_utf8decodeT(id.c_str()));
- ptrT tname( mir_utf8decodeT(name.c_str()));
- ptrT tsurname( mir_utf8decodeT(surname.c_str()));
-
- PROTOSEARCHRESULT isr = {0};
- isr.cbSize = sizeof(isr);
- isr.flags = PSR_TCHAR;
- isr.id = tid;
- isr.firstName = tname;
- isr.lastName = tsurname;
-
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
- }
- }
- }
-
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
-
- facy.handle_success("searchIdAckThread");
-
- mir_free(targ);
- mir_free(arg);
-}
+/*
+
+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"
+
+#define MAX_NEWSFEED_LEN 500
+
+void FacebookProto::ProcessBuddyList(void*)
+{
+ ScopedLock s(facy.buddies_lock_);
+
+ if (isOffline())
+ return;
+
+ facy.handle_entry("ProcessBuddyList");
+
+ // Prepare update data
+ std::string post_data = "user=" + facy.self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + facy.dtsg_ + "&__user=" + facy.self_.user_id + "&cached_user_info_ids=";
+
+ int counter = 0;
+ for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL; i = i->next, counter++)
+ {
+ post_data += i->data->user_id + "%2C";
+ }
+
+ // Get buddy list
+ http::response resp = facy.flap(REQUEST_BUDDY_LIST, &post_data);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("buddy_list");
+ return;
+ }
+
+ debugLogA("***** Starting processing buddy list");
+
+ CODE_BLOCK_TRY
+
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_buddy_list(&resp.data, &facy.buddies);
+ delete p;
+
+ for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL;)
+ {
+ facebook_user* fbu = i->data;
+
+ std::string status;
+ switch (fbu->status_id) {
+ case ID_STATUS_OFFLINE:
+ status = "Offline"; break;
+ case ID_STATUS_ONLINE:
+ status = "Online"; break;
+ case ID_STATUS_ONTHEPHONE:
+ status = "Mobile"; break;
+ }
+
+ if (fbu->idle)
+ status += " (idle)";
+
+ debugLogA(" Now %s: %s", status.c_str(), fbu->user_id.c_str());
+
+ if (!fbu->deleted)
+ {
+ if (!fbu->handle) // just been added
+ fbu->handle = AddToContactList(fbu, CONTACT_FRIEND);
+
+ ptrT client( getTStringA(fbu->handle, "MirVer"));
+ if (!client || _tcscmp(client, fbu->getMirVer()))
+ setTString(fbu->handle, "MirVer", fbu->getMirVer());
+
+ if (getDword(fbu->handle, "IdleTS", 0) != fbu->last_active) {
+ if ((fbu->idle || fbu->status_id == ID_STATUS_OFFLINE) && fbu->last_active > 0)
+ setDword(fbu->handle, "IdleTS", fbu->last_active);
+ else
+ delSetting(fbu->handle, "IdleTS");
+ }
+ }
+
+ 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;
+
+ ptrT szTitle( mir_utf8decodeT(fbu->real_name.c_str()));
+ NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), fbu->handle, FACEBOOK_EVENT_OTHER, &url);
+ }
+
+ // Check avatar change
+ CheckAvatarChange(fbu->handle, fbu->image_url);
+ }
+ }
+
+ debugLogA("***** Buddy list processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing buddy list: %s", e.what());
+
+ CODE_BLOCK_END
+}
+
+void FacebookProto::ProcessFriendList(void*)
+{
+ ScopedLock s(facy.buddies_lock_);
+
+ if (isOffline())
+ return;
+
+ facy.handle_entry("load_friends");
+
+ // Get buddy list
+ http::response resp = facy.flap(REQUEST_LOAD_FRIENDS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("load_friends");
+ return;
+ }
+
+ debugLogA("***** Starting processing friend list");
+
+ CODE_BLOCK_TRY
+
+ std::map<std::string, facebook_user*> friends;
+
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_friends(&resp.data, &friends);
+ delete p;
+
+
+ // Check and update old contacts
+ for (MCONTACT 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;
+
+ // 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);
+
+ // TODO: remove this in some future version?
+ // Remove old useless "RealName" field
+ ptrA realname(getStringA(hContact, "RealName"));
+ if (realname != NULL) {
+ delSetting(hContact, "RealName");
+ }
+
+ // Update real name and nick
+ if (!fbu->real_name.empty()) {
+ SaveName(hContact, fbu);
+ }
+
+ 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;
+
+ ptrT szTitle( mir_utf8decodeT(fbu->real_name.c_str()));
+ NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url);
+ }
+
+ // Check avatar change
+ CheckAvatarChange(hContact, fbu->image_url);
+
+ // Mark this contact as deleted ("processed") and delete them later (as there may be some duplicit contacts to use)
+ fbu->deleted = true;
+ } 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 (!getStringUtf(hContact, FACEBOOK_KEY_NICK, &dbv)) {
+ contactname = dbv.pszVal;
+ db_free(&dbv);
+ }
+
+ std::string url = FACEBOOK_URL_PROFILE + id;
+
+ ptrT szTitle( mir_utf8decodeT(contactname.c_str()));
+ NotifyEvent(szTitle, TranslateT("Contact is no longer on server-list."), hContact, FACEBOOK_EVENT_OTHER, &url);
+ }
+ }
+ }
+ }
+
+ // Check remaining contacts in map and add them to contact list
+ for (std::map< std::string, facebook_user* >::iterator it = friends.begin(); it != friends.end(); ) {
+ if (!it->second->deleted)
+ AddToContactList(it->second, CONTACT_FRIEND, true); // we know this contact doesn't exists, so we force add it
+
+ delete it->second;
+ it = friends.erase(it);
+ }
+ friends.clear();
+
+ debugLogA("***** Friend list processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing friend list: %s", e.what());
+
+ CODE_BLOCK_END
+}
+
+void FacebookProto::ProcessUnreadMessages(void*)
+{
+ facy.handle_entry("ProcessUnreadMessages");
+
+ // receive messages from all folders by default, use hidden setting to receive only inbox messages
+ bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
+
+ std::string data = "folders[0]=inbox";
+ if (!inboxOnly)
+ data += "&folders[1]=other";
+ 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";
+
+ http::response resp = facy.flap(REQUEST_UNREAD_THREADS, &data);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("ProcessUnreadMessages");
+ return;
+ }
+
+ CODE_BLOCK_TRY
+
+ std::vector<std::string> threads;
+
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_unread_threads(&resp.data, &threads, inboxOnly);
+ delete p;
+
+ ForkThread(&FacebookProto::ProcessUnreadMessage, new std::vector<std::string>(threads));
+
+ debugLogA("***** Unread threads list processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing unread threads list: %s", e.what());
+
+ CODE_BLOCK_END
+
+ facy.handle_success("ProcessUnreadMessages");
+}
+
+void FacebookProto::ProcessUnreadMessage(void *p)
+{
+ if (p == NULL)
+ return;
+
+ facy.handle_entry("ProcessUnreadMessage");
+
+ std::vector<std::string> threads = *(std::vector<std::string>*)p;
+ delete (std::vector<std::string>*)p;
+
+ int offset = 0;
+ int limit = 21;
+
+ bool local_timestamp = getBool(FACEBOOK_KEY_LOCAL_TIMESTAMP_UNREAD, 0);
+
+ // receive messages from all folders by default, use hidden setting to receive only inbox messages
+ bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
+
+ http::response resp;
+
+ while (!threads.empty()) {
+ 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";
+
+ for (std::vector<std::string>::size_type i = 0; i < threads.size(); i++) {
+ std::string thread_id = utils::url::encode(threads[i]);
+
+ // request messages from thread
+ data += "&messages[thread_ids][" + thread_id;
+ data += "][offset]=" + utils::conversion::to_string(&offset, UTILS_CONV_SIGNED_NUMBER);
+ data += "&messages[thread_ids][" + thread_id;
+ data += "][limit]=" + utils::conversion::to_string(&limit, UTILS_CONV_SIGNED_NUMBER);
+
+ // request info about thread
+ data += "&threads[thread_ids][" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
+ data += "]=" + thread_id;
+ }
+
+ resp = facy.flap(REQUEST_THREAD_INFO, &data);
+
+ if (resp.code == HTTP_CODE_OK) {
+
+ 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, true, inboxOnly, limit);
+ 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();
+
+ ReceiveMessages(messages, local_timestamp, true);
+
+ debugLogA("***** Unread messages processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing unread messages: %s", e.what());
+
+ CODE_BLOCK_END
+
+ facy.handle_success("ProcessUnreadMessage");
+ } else {
+ facy.handle_error("ProcessUnreadMessage");
+ }
+
+ offset += limit;
+ limit = 20; // TODO: use better limits?
+
+ threads.clear(); // TODO: if we have limit messages from one user, there may be more unread messages... continue with it... otherwise remove that threadd from threads list -- or do it in json parser? hm = allow more than "limit" unread messages to be parsed
+ }
+}
+
+void FacebookProto::ReceiveMessages(std::vector<facebook_message*> messages, bool local_timestamp, bool check_duplicates)
+{
+ bool naseemsSpamMode = getBool(FACEBOOK_KEY_NASEEMS_SPAM_MODE, false);
+
+ // TODO: make this checking more lightweight as now it is not effective at all...
+ if (check_duplicates) {
+ // 1. check if there are some message that we already have (compare FACEBOOK_KEY_MESSAGE_ID = last received message ID)
+ for (std::vector<facebook_message*>::size_type i = 0; i < messages.size(); i++) {
+
+ MCONTACT hContact = messages[i]->isChat
+ ? ChatIDToHContact(std::tstring(_A2T(messages[i]->thread_id.c_str())))
+ : ContactIDToHContact(messages[i]->user_id);
+
+ if (hContact == NULL)
+ continue;
+
+ ptrA lastId(getStringA(hContact, FACEBOOK_KEY_MESSAGE_ID));
+ if (lastId == NULL)
+ continue;
+
+ if (!messages[i]->message_id.compare(lastId)) {
+ // Equal, ignore all older messages (including this) from same contact
+ for (std::vector<facebook_message*>::size_type j = 0; j < messages.size(); j++) {
+ bool equalsId = messages[i]->isChat
+ ? (messages[j]->thread_id == messages[i]->thread_id)
+ : (messages[j]->user_id == messages[i]->user_id);
+
+ if (equalsId && messages[j]->time <= messages[i]->time)
+ messages[j]->flag_ = 1;
+ }
+ }
+ }
+
+ // 2. remove all marked messages from list
+ for (std::vector<facebook_message*>::iterator it = messages.begin(); it != messages.end();) {
+ if ((*it)->flag_ == 1)
+ it = messages.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ for(std::vector<facebook_message*>::size_type i = 0; i < messages.size(); i++) {
+ DWORD timestamp = local_timestamp || !messages[i]->time ? ::time(NULL) : messages[i]->time;
+
+ if (messages[i]->isChat) {
+ // Multi-user message
+ debugLogA(" Got chat message: %s", messages[i]->message_text.c_str());
+
+ std::tstring tthread_id = _A2T(messages[i]->thread_id.c_str());
+
+ // RM TODO: better use check if chatroom exists/is in db/is online... no?
+ // like: if (ChatIDToHContact(tthread_id) == NULL) {
+ if (GetChatUsers(tthread_id.c_str()) == NULL) {
+ // In Naseem's spam mode we ignore outgoing messages sent from other instances
+ if (naseemsSpamMode && !messages[i]->isIncoming) {
+ delete messages[i];
+ continue;
+ }
+
+ // TODO: We don't have this chat, lets load info about it (name, list of users, last messages, etc.)
+ //LoadChatInfo(&fbu); // In this method use json's parse_chat_info
+ std::tstring tthread_name = tthread_id; // TODO: use correct name for chat, not thread_id
+
+ AddChat(tthread_id.c_str(), tthread_name.c_str());
+ }
+
+ MCONTACT hChatContact = ChatIDToHContact(tthread_id);
+
+ // Save last (this) message ID
+ setString(hChatContact, FACEBOOK_KEY_MESSAGE_ID, messages[i]->message_id.c_str());
+
+ // Save TID if not exists already
+ ptrA tid(getStringA(hChatContact, FACEBOOK_KEY_TID));
+ if (!tid || strcmp(tid, messages[i]->thread_id.c_str()))
+ setString(hChatContact, FACEBOOK_KEY_TID, messages[i]->thread_id.c_str());
+
+ ParseSmileys(messages[i]->message_text, hChatContact);
+
+ // TODO: support also system messages (rename chat, user quit, etc.)! (here? or it is somewhere else?
+ // ... we must add some new "type" field into facebook_message structure and use it also for Pokes and similar)
+ UpdateChat(tthread_id.c_str(), messages[i]->user_id.c_str(), messages[i]->sender_name.c_str(), messages[i]->message_text.c_str(), timestamp);
+
+ // Automatically mark message as read because chatroom doesn't support onRead event (yet)
+ ForkThread(&FacebookProto::ReadMessageWorker, (void*)hChatContact);
+ } else {
+ // Single-user message
+ debugLogA(" Got message: %s", messages[i]->message_text.c_str());
+
+ facebook_user fbu;
+ fbu.user_id = messages[i]->user_id;
+ fbu.real_name = messages[i]->sender_name;
+
+ MCONTACT hContact = ContactIDToHContact(fbu.user_id);
+ if (hContact == NULL) {
+ // In Naseem's spam mode we ignore outgoing messages sent from other instances
+ if (naseemsSpamMode && !messages[i]->isIncoming) {
+ delete messages[i];
+ continue;
+ }
+
+ // We don't have this contact, lets load info about him
+ LoadContactInfo(&fbu);
+
+ hContact = AddToContactList(&fbu, CONTACT_NONE);
+ }
+
+ // Save last (this) message ID
+ setString(hContact, FACEBOOK_KEY_MESSAGE_ID, messages[i]->message_id.c_str());
+
+ // Save TID if not exists already
+ ptrA tid(getStringA(hContact, FACEBOOK_KEY_TID));
+ if (!tid || strcmp(tid, messages[i]->thread_id.c_str()))
+ setString(hContact, FACEBOOK_KEY_TID, messages[i]->thread_id.c_str());
+
+ ParseSmileys(messages[i]->message_text, hContact);
+
+ if (messages[i]->isIncoming) {
+ PROTORECVEVENT recv = { 0 };
+ recv.flags = PREF_UTF;
+ recv.szMessage = const_cast<char*>(messages[i]->message_text.c_str());
+ recv.timestamp = timestamp;
+ ProtoChainRecvMsg(hContact, &recv);
+ } else {
+ DBEVENTINFO dbei = { 0 };
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = DBEF_SENT | DBEF_UTF;
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = timestamp;
+ dbei.cbBlob = (DWORD)messages[i]->message_text.length() + 1;
+ dbei.pBlob = (PBYTE)messages[i]->message_text.c_str();
+ db_event_add(hContact, &dbei);
+ }
+ }
+ delete messages[i];
+ }
+ messages.clear();
+}
+
+void FacebookProto::ProcessMessages(void* data)
+{
+ if (data == NULL)
+ return;
+
+ std::string* resp = (std::string*)data;
+
+ // receive messages from all folders by default, use hidden setting to receive only inbox messages
+ bool inboxOnly = getBool(FACEBOOK_KEY_INBOX_ONLY, 0);
+
+ if (isOffline())
+ goto exit;
+
+ debugLogA("***** Starting processing messages");
+
+ CODE_BLOCK_TRY
+
+ std::vector< facebook_message* > messages;
+
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_messages(data, &messages, &facy.notifications, inboxOnly);
+ delete p;
+
+ bool local_timestamp = getBool(FACEBOOK_KEY_LOCAL_TIMESTAMP, 0);
+
+ ReceiveMessages(messages, local_timestamp);
+
+ ShowNotifications();
+
+ debugLogA("***** Messages processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing messages: %s", e.what());
+
+ CODE_BLOCK_END
+
+exit:
+ delete resp;
+}
+
+void FacebookProto::ShowNotifications() {
+ if (!getByte(FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
+ return;
+
+ for (std::map<std::string, facebook_notification*>::iterator it = facy.notifications.begin(); it != facy.notifications.end(); ++it) {
+ if (it->second != NULL && !it->second->seen) {
+ debugLogA(" Got notification: %s", it->second->text.c_str());
+ ptrT szText(mir_utf8decodeT(it->second->text.c_str()));
+ it->second->hWndPopup = NotifyEvent(m_tszUserName, szText, ContactIDToHContact(it->second->user_id), FACEBOOK_EVENT_NOTIFICATION, &it->second->link, &it->second->id);
+ it->second->seen = true;
+ }
+ }
+}
+
+void FacebookProto::ProcessNotifications(void*)
+{
+ if (isOffline())
+ return;
+
+ facy.handle_entry("notifications");
+
+ // Get notifications
+ http::response resp = facy.flap(REQUEST_NOTIFICATIONS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("notifications");
+ return;
+ }
+
+
+ // Process notifications
+ debugLogA("***** Starting processing notifications");
+
+ CODE_BLOCK_TRY
+
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_notifications(&(resp.data), &facy.notifications);
+ delete p;
+
+ ShowNotifications();
+
+ debugLogA("***** Notifications processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing notifications: %s", e.what());
+
+ CODE_BLOCK_END
+}
+
+void FacebookProto::ProcessFriendRequests(void*)
+{
+ facy.handle_entry("friendRequests");
+
+ // Get notifications
+ http::response resp = facy.flap(REQUEST_LOAD_REQUESTS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("friendRequests");
+ return;
+ }
+
+ // Parse it
+ std::string reqs = utils::text::source_get_value(&resp.data, 2, "class=\"mRequestItem", "al aps\">");
+
+ std::string::size_type pos = 0;
+ std::string::size_type pos2 = 0;
+ bool last = false;
+
+ while (!last && !reqs.empty()) {
+ std::string req;
+ if ((pos2 = reqs.find("class=\"mRequestItem", pos)) != std::string::npos) {
+ req = reqs.substr(pos, pos2 - pos);
+ pos = pos2 + 19;
+ } else {
+ req = reqs.substr(pos);
+ last = true;
+ }
+
+ std::string get = utils::text::source_get_value(&req, 3, "<form", "action=\"", "\">");
+ std::string time = utils::text::source_get_value2(&get, "seenrequesttime=", "&\"");
+
+ facebook_user *fbu = new facebook_user();
+ fbu->real_name = utils::text::source_get_value(&req, 2, "class=\"actor\">", "</");
+ fbu->user_id = utils::text::source_get_value(&get, 2, "id=", "&");
+
+ if (fbu->user_id.length() && fbu->real_name.length())
+ {
+ MCONTACT hContact = AddToContactList(fbu, CONTACT_APPROVE);
+ setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, CONTACT_APPROVE);
+
+ bool seen = false;
+
+ DBVARIANT dbv;
+ if (!getString(hContact, "RequestTime", &dbv)) {
+ seen = !strcmp(dbv.pszVal, time.c_str());
+ db_free(&dbv);
+ }
+
+ if (!seen) {
+ // This is new request
+ setString(hContact, "RequestTime", time.c_str());
+
+ //blob is: uin(DWORD), hContact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)
+ //blob is: 0(DWORD), hContact(HANDLE), nick(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ), ""(ASCIIZ)
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(DBEVENTINFO);
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = ::time(NULL);
+ dbei.flags = DBEF_UTF;
+ dbei.eventType = EVENTTYPE_AUTHREQUEST;
+ dbei.cbBlob = (DWORD)(sizeof(DWORD)*2 + fbu->real_name.length() + 5);
+
+ PBYTE pCurBlob = dbei.pBlob = (PBYTE) mir_alloc(dbei.cbBlob);
+ *(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD); // UID
+ *(PDWORD)pCurBlob = (DWORD)hContact; pCurBlob += sizeof(DWORD); // Contact Handle
+ strcpy((char*)pCurBlob, fbu->real_name.data()); pCurBlob += fbu->real_name.length()+1; // Nickname
+ *pCurBlob = '\0'; pCurBlob++; // First Name
+ *pCurBlob = '\0'; pCurBlob++; // Last Name
+ *pCurBlob = '\0'; pCurBlob++; // E-mail
+ *pCurBlob = '\0'; // Reason
+
+ db_event_add(0, &dbei);
+
+ debugLogA(" (New) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+ } else {
+ debugLogA(" (Old) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+ }
+ } else {
+ debugLogA(" !!! Wrong friendship request");
+ debugLogA("%s", req.c_str());
+ }
+ }
+
+ facy.handle_success("friendRequests");
+}
+
+void FacebookProto::ProcessFeeds(void* data)
+{
+ if (!isOnline())
+ return;
+
+ facy.handle_entry("feeds");
+
+ // Get feeds
+ http::response resp = facy.flap(REQUEST_FEEDS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("feeds");
+ return;
+ }
+
+ if (resp.data.empty() /*|| resp.data.find("\"num_stories\":0") != std::string::npos*/)
+ return;
+
+ CODE_BLOCK_TRY
+
+ debugLogA("***** Starting processing feeds");
+
+ std::vector< facebook_newsfeed* > news;
+
+ std::string::size_type pos = 0;
+ UINT limit = 0;
+
+ DWORD new_time = facy.last_feeds_update_;
+
+ while ((pos = resp.data.find("<div class=\"userContentWrapper", pos)) != std::string::npos && limit <= 25)
+ {
+ /*std::string::size_type pos2 = resp.data.find("<div class=\"userContentWrapper", pos+5);
+ if (pos2 == std::string::npos)
+ pos2 = resp.data.length();
+
+ std::string post = resp.data.substr(pos, pos2 - pos);*/
+ std::string post = resp.data.substr(pos, resp.data.find("</form>", pos) - pos);
+ pos += 5;
+
+ std::string post_header = utils::text::source_get_value(&post, 3, "<h5", ">", "</h5>");
+ std::string post_message = utils::text::source_get_value(&post, 3, " userContent\"", ">", "<form");
+ std::string post_link = utils::text::source_get_value(&post, 4, "</h5>", "<a", "href=\"", "\"");
+ //std::string post_attach = utils::text::source_get_value(&post, 4, "<div class=", "uiStreamAttachments", ">", "<form");
+
+ std::string post_time = utils::text::source_get_value(&post, 3, "</h5>", "<abbr", "</a>");
+
+ std::string time = utils::text::source_get_value(&post_time, 2, "data-utime=\"", "\"");
+ std::string time_text = utils::text::source_get_value(&post_time, 2, ">", "</abbr>");
+
+ DWORD ttime = utils::conversion::to_timestamp(time);
+ if (ttime > facy.last_feeds_update_)
+ new_time = ttime;
+ else
+ continue;
+
+ std::string post_place = utils::text::source_get_value(&post, 4, "</abbr>", "<a", ">", "</a>");
+
+ std::string premsg = "\n" + time_text;
+
+ post_place = utils::text::trim(
+ utils::text::remove_html(post_place));
+ if (!post_place.empty()) {
+ premsg += " - " + post_place;
+ }
+ premsg += "\n";
+
+ // in title keep only name, end of events like "X shared link" put into message
+ std::string::size_type pos2 = post_header.find("‎");
+
+ if (pos2 != std::string::npos) {
+ utils::text::replace_first(&post_header, "‎", " → ");
+ } else {
+ pos2 = post_header.find("</a></");
+ if (pos2 != std::string::npos) {
+ pos2 += 4;
+ std::string a = utils::text::trim(utils::text::remove_html(post_header.substr(pos2, post_header.length() - pos2)));
+ if (a.length() > 2)
+ premsg += a;
+ post_header = post_header.substr(0, pos2);
+ }
+ }
+
+ // Strip "Translate" link
+ pos2 = post_message.find("role=\"button\">");
+ if (pos2 != std::string::npos) {
+ post_message = post_message.substr(0, pos2 + 14);
+ }
+
+ post_message = premsg + post_message;
+
+ // append attachement to message (if any)
+ //post_message += utils::text::trim(post_attach);
+
+ facebook_newsfeed* nf = new facebook_newsfeed;
+
+ nf->title = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html(post_header)));
+
+ nf->user_id = utils::text::source_get_value(&post_header, 2, "user.php?id=", "&amp;");
+
+ nf->link = utils::text::special_expressions_decode(post_link);
+ //utils::text::source_get_value(&post_link, 2, "href=\\\"", "\\\">"));
+
+ nf->text = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html(
+ utils::text::edit_html(post_message))));
+
+ if (!nf->title.length() || !nf->text.length())
+ {
+ delete nf;
+ continue;
+ }
+
+ news.push_back(nf);
+ pos++;
+ limit++;
+ }
+
+ for(std::vector<facebook_newsfeed*>::size_type i=0; i<news.size(); i++)
+ {
+ debugLogA(" Got newsfeed: %s %s", news[i]->title.c_str(), news[i]->text.c_str());
+ ptrT tszTitle( mir_utf8decodeT(news[i]->title.c_str()));
+ ptrT tszText( mir_utf8decodeT(news[i]->text.c_str()));
+
+ // Truncate text of newsfeed when it's too long
+ if (_tcslen(tszText) > MAX_NEWSFEED_LEN) {
+ TCHAR buf[MAX_NEWSFEED_LEN + 3 + 1] = { 0 };
+ _tcsncpy(buf, tszText, MAX_NEWSFEED_LEN);
+ _tcsncpy(&buf[MAX_NEWSFEED_LEN], _T("..."), 3);
+ tszText = buf;
+ }
+
+ NotifyEvent(tszTitle, tszText, this->ContactIDToHContact(news[i]->user_id), FACEBOOK_EVENT_NEWSFEED, &news[i]->link);
+ delete news[i];
+ }
+ news.clear();
+
+ this->facy.last_feeds_update_ = new_time;
+ //this->facy.last_feeds_update_ = ::time(NULL);
+
+ debugLogA("***** Feeds processed");
+
+ CODE_BLOCK_CATCH
+
+ debugLogA("***** Error processing feeds: %s", e.what());
+
+ CODE_BLOCK_END
+
+ facy.handle_success("feeds");
+}
+
+void FacebookProto::ProcessPages(void *data)
+{
+ if (!getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
+ return;
+
+ facy.handle_entry("load_pages");
+
+ // Get feeds
+ http::response resp = facy.flap(REQUEST_PAGES);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("load_pages");
+ return;
+ }
+
+ std::string content = utils::text::source_get_value(&resp.data, 2, "id=\"bookmarksSeeAllSection\"", "</code>");
+
+ std::string::size_type start, end;
+ start = content.find("<li", 0);
+ while (start != std::string::npos) {
+ end = content.find("<li", start + 1);
+ if (end == std::string::npos)
+ end = content.length();
+
+ std::string item = content.substr(start, end - start);
+ //item = utils::text::source_get_value(&item, 2, "data-gt=", ">");
+
+ start = content.find("<li", start + 1);
+
+ std::string id = utils::text::source_get_value(&item, 3, "data-gt=", "bmid&quot;:&quot;", "&quot;");
+ std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&item, 3, "data-gt=", "title=\"", "\"")));
+ std::string href = utils::text::source_get_value(&item, 3, "data-gt=", "href=\"", "\"");
+
+ // Ignore pages channel
+ if (href.find("/pages/feed") != std::string::npos)
+ continue;
+
+ if (id.empty() || title.empty())
+ continue;
+
+ debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
+ facy.pages[id] = title;
+ }
+
+ facy.handle_success("load_pages");
+}
+
+void FacebookProto::SearchAckThread(void *targ)
+{
+ facy.handle_entry("searchAckThread");
+
+ int count = 0;
+
+ char *arg = mir_utf8encodeT((TCHAR*)targ);
+ std::string search = utils::url::encode(arg);
+ std::string ssid;
+
+ while (count < 50 && !isOffline())
+ {
+ std::string get_data = search + "&s=" + utils::conversion::to_string(&count, UTILS_CONV_UNSIGNED_NUMBER);
+ if (!ssid.empty())
+ get_data += "&ssid=" + ssid;
+
+ http::response resp = facy.flap(REQUEST_SEARCH, NULL, &get_data);
+
+ if (resp.code == HTTP_CODE_OK)
+ {
+ std::string items = utils::text::source_get_value(&resp.data, 2, "<body", "</body>");
+
+ std::string::size_type pos = 0;
+ std::string::size_type pos2 = 0;
+
+ while ((pos = items.find("<td class=\"pic\">", pos)) != std::string::npos) {
+ std::string item = items.substr(pos, items.find("</tr>", pos) - pos);
+ pos++; count++;
+
+ std::string id = utils::text::source_get_value2(&item, "?id=", "&\"");
+ if (id.empty())
+ id = utils::text::source_get_value2(&item, "?ids=", "&\"");
+
+ std::string name = utils::text::source_get_value(&item, 4, "<td class=\"name\">", "<a", ">", "</");
+ std::string surname;
+ std::string nick;
+ std::string common = utils::text::source_get_value(&item, 2, "<span class=\"mfss fcg\">", "</span>");
+
+ if ((pos2 = name.find("<span class=\"alternate_name\">")) != std::string::npos) {
+ nick = name.substr(pos2 + 30, name.length() - pos2 - 31); // also remove brackets around nickname
+ name = name.substr(0, pos2 - 1);
+ }
+
+ if ((pos2 = name.find(" ")) != std::string::npos) {
+ surname = name.substr(pos2 + 1, name.length() - pos2 - 1);
+ name = name.substr(0, pos2);
+ }
+
+ // ignore self contact and empty ids
+ if (id.empty() || id == facy.self_.user_id)
+ continue;
+
+ ptrT tid( mir_utf8decodeT(id.c_str()));
+ ptrT tname( mir_utf8decodeT(utils::text::special_expressions_decode(name).c_str()));
+ ptrT tsurname( mir_utf8decodeT(utils::text::special_expressions_decode(surname).c_str()));
+ ptrT tnick( mir_utf8decodeT(utils::text::special_expressions_decode(nick).c_str()));
+ ptrT tcommon( mir_utf8decodeT(utils::text::special_expressions_decode(common).c_str()));
+
+ PROTOSEARCHRESULT isr = {0};
+ isr.cbSize = sizeof(isr);
+ isr.flags = PSR_TCHAR;
+ isr.id = tid;
+ isr.nick = tnick;
+ isr.firstName = tname;
+ isr.lastName = tsurname;
+ isr.email = tcommon;
+
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
+ }
+
+ ssid = utils::text::source_get_value(&items, 3, "id=\"more_objects\"", "ssid=", "&");
+ if (ssid.empty())
+ break; // No more results
+ }
+ }
+
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
+
+ facy.handle_success("searchAckThread");
+
+ mir_free(targ);
+ mir_free(arg);
+}
+
+void FacebookProto::SearchIdAckThread(void *targ)
+{
+ facy.handle_entry("searchIdAckThread");
+
+ char *arg = mir_utf8encodeT((TCHAR*)targ);
+ std::string search = utils::url::encode(arg) + "?";
+
+ if (!isOffline())
+ {
+ http::response resp = facy.flap(REQUEST_USER_INFO, NULL, &search);
+
+ if (resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end()) {
+ search = utils::text::source_get_value(&resp.headers["Location"], 2, FACEBOOK_SERVER_MOBILE"/", "_rdr", true);
+ resp = facy.flap(REQUEST_USER_INFO, NULL, &search);
+ }
+
+ if (resp.code == HTTP_CODE_OK)
+ {
+ std::string about = utils::text::source_get_value(&resp.data, 2, "<div class=\"timeline", "id=\"footer");
+
+ std::string id = utils::text::source_get_value2(&about, ";id=", "&\"");
+ if (id.empty())
+ id = utils::text::source_get_value2(&about, "?bid=", "&\"");
+ std::string name = utils::text::source_get_value(&about, 3, "class=\"profileName", ">", "</");
+ std::string surname;
+
+ std::string::size_type pos;
+ if ((pos = name.find(" ")) != std::string::npos) {
+ surname = name.substr(pos + 1, name.length() - pos - 1);
+ name = name.substr(0, pos);
+ }
+
+ // ignore self contact and empty ids
+ if (!id.empty() && id != facy.self_.user_id){
+ ptrT tid( mir_utf8decodeT(id.c_str()));
+ ptrT tname( mir_utf8decodeT(name.c_str()));
+ ptrT tsurname( mir_utf8decodeT(surname.c_str()));
+
+ PROTOSEARCHRESULT isr = {0};
+ isr.cbSize = sizeof(isr);
+ isr.flags = PSR_TCHAR;
+ isr.id = tid;
+ isr.firstName = tname;
+ isr.lastName = tsurname;
+
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
+ }
+ }
+ }
+
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
+
+ facy.handle_success("searchIdAckThread");
+
+ mir_free(targ);
+ mir_free(arg);
+}