summaryrefslogtreecommitdiff
path: root/protocols/FacebookRM/src/communication.cpp
diff options
context:
space:
mode:
authorRobert Pösel <robyer@seznam.cz>2014-05-10 17:38:54 +0000
committerRobert Pösel <robyer@seznam.cz>2014-05-10 17:38:54 +0000
commite3c0c006f5e87f16060b09b59f24e02a3faccf44 (patch)
tree4a893b9f7cbccad54f9737620917aa70cbdca58f /protocols/FacebookRM/src/communication.cpp
parent4ff152f48ca945be4a0065a5f57246449c741f8a (diff)
Facebook: Fix not working login for some people
git-svn-id: http://svn.miranda-ng.org/main/trunk@9156 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/FacebookRM/src/communication.cpp')
-rw-r--r--protocols/FacebookRM/src/communication.cpp2916
1 files changed, 1460 insertions, 1456 deletions
diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp
index 5b44a7e9f1..6eb267b1e0 100644
--- a/protocols/FacebookRM/src/communication.cpp
+++ b/protocols/FacebookRM/src/communication.cpp
@@ -1,1456 +1,1460 @@
-/*
-
-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 facebook_client::client_notify(TCHAR* message)
-{
- parent->NotifyEvent(parent->m_tszUserName, message, NULL, FACEBOOK_EVENT_CLIENT);
-}
-
-http::response facebook_client::flap(RequestType request_type, std::string* request_data, std::string* request_get_data, int method)
-{
- NETLIBHTTPREQUEST nlhr = {sizeof(NETLIBHTTPREQUEST)};
- nlhr.requestType = !method ? choose_method(request_type) : method;
-
- std::string url = choose_proto(request_type);
- url.append(choose_server(request_type, request_data, request_get_data));
- url.append(choose_action(request_type, request_data, request_get_data));
-
- nlhr.szUrl = (char*)url.c_str();
- nlhr.flags = NLHRF_HTTP11 | choose_security_level(request_type);
- nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount);
-
- #ifdef _DEBUG
- nlhr.flags |= NLHRF_DUMPASTEXT;
- #else
- nlhr.flags |= NLHRF_NODUMP;
- #endif
-
- switch (request_type)
- {
- case REQUEST_MESSAGES_RECEIVE:
- nlhr.timeout = 1000 * 65; break;
- default:
- nlhr.timeout = 1000 * 20; break;
- }
-
- if (request_data != NULL)
- {
- nlhr.pData = (char*)(*request_data).c_str();
- nlhr.dataLength = (int)request_data->length();
- }
-
- parent->debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl);
-
- switch (request_type)
- {
- case REQUEST_LOGIN:
- nlhr.nlc = NULL;
- break;
-
- case REQUEST_MESSAGES_RECEIVE:
- nlhr.nlc = hMsgCon;
- nlhr.flags |= NLHRF_PERSISTENT;
- break;
-
- default:
- WaitForSingleObject(fcb_conn_lock_, INFINITE);
- nlhr.nlc = hFcbCon;
- nlhr.flags |= NLHRF_PERSISTENT;
- break;
- }
-
- NETLIBHTTPREQUEST* pnlhr = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)handle_, (LPARAM)&nlhr);
-
- mir_free(nlhr.headers[3].szValue);
- mir_free(nlhr.headers);
-
- http::response resp;
-
- switch (request_type)
- {
- case REQUEST_LOGIN:
- case REQUEST_SETUP_MACHINE:
- break;
-
- case REQUEST_MESSAGES_RECEIVE:
- hMsgCon = pnlhr ? pnlhr->nlc : NULL;
- break;
-
- default:
- ReleaseMutex(fcb_conn_lock_);
- hFcbCon = pnlhr ? pnlhr->nlc : NULL;
- break;
- }
-
- if (pnlhr != NULL)
- {
- parent->debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode);
- store_headers(&resp, pnlhr->headers, pnlhr->headersCount);
- resp.code = pnlhr->resultCode;
- resp.data = pnlhr->pData ? pnlhr->pData : "";
-
- CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)pnlhr);
- } else {
- parent->debugLogA("!!!!! No response from server (time-out)");
- resp.code = HTTP_CODE_FAKE_DISCONNECTED;
- // Better to have something set explicitely as this value is compaired in all communication requests
- }
-
- // Get Facebook's error message
- if (resp.code == HTTP_CODE_OK) {
- std::string::size_type pos = resp.data.find("\"error\":");
- if (pos != std::string::npos) {
- pos += 8;
- int error_num = atoi(resp.data.substr(pos, resp.data.find(",", pos) - pos).c_str());
- if (error_num != 0) {
- std::string error = "";
-
- pos = resp.data.find("\"errorDescription\":\"", pos);
- if (pos != std::string::npos) {
- pos += 20;
- error = resp.data.substr(pos, resp.data.find("\"", pos) - pos);
- error = utils::text::trim(utils::text::special_expressions_decode(utils::text::slashu_to_utf8(error)));
- error = ptrA( mir_utf8decodeA(error.c_str()));
- }
-
- std::string title = "";
- pos = resp.data.find("\"errorSummary\":\"", pos);
- if (pos != std::string::npos) {
- pos += 16;
- title = resp.data.substr(pos, resp.data.find("\"", pos) - pos);
- title = utils::text::trim(utils::text::special_expressions_decode(utils::text::slashu_to_utf8(title)));
- title = ptrA( mir_utf8decodeA(title.c_str()));
- }
-
- bool silent = resp.data.find("\"silentError\":1") != std::string::npos;
-
- resp.error_number = error_num;
- resp.error_text = error;
- resp.error_title = title;
- resp.code = HTTP_CODE_FAKE_ERROR;
-
- parent->debugLogA(" ! ! Received Facebook error: %d -- %s", error_num, error.c_str());
- if (notify_errors(request_type) && !silent)
- client_notify(_A2T(error.c_str()));
- }
- }
- }
-
- return resp;
-}
-
-bool facebook_client::handle_entry(std::string method)
-{
- parent->debugLogA(" >> Entering %s()", method.c_str());
- return true;
-}
-
-bool facebook_client::handle_success(std::string method)
-{
- parent->debugLogA(" << Quitting %s()", method.c_str());
- reset_error();
- return true;
-}
-
-bool facebook_client::handle_error(std::string method, int action)
-{
- increment_error();
- parent->debugLogA("!!!!! %s(): Something with Facebook went wrong", method.c_str());
-
- bool result = (error_count_ <= (UINT)parent->getByte(FACEBOOK_KEY_TIMEOUTS_LIMIT, FACEBOOK_TIMEOUTS_LIMIT));
- if (action == FORCE_DISCONNECT || action == FORCE_QUIT)
- result = false;
-
- if (!result) {
- reset_error();
-
- if (action != FORCE_QUIT)
- parent->SetStatus(ID_STATUS_OFFLINE);
- }
-
- return result;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-DWORD facebook_client::choose_security_level(RequestType request_type)
-{
- if (this->https_)
- if (request_type != REQUEST_MESSAGES_RECEIVE || parent->getByte(FACEBOOK_KEY_FORCE_HTTPS_CHANNEL, DEFAULT_FORCE_HTTPS_CHANNEL))
- return NLHRF_SSL;
-
- switch (request_type) {
- case REQUEST_LOGIN:
- case REQUEST_SETUP_MACHINE:
- return NLHRF_SSL;
-
-// case REQUEST_LOGOUT:
-// case REQUEST_HOME:
-// case REQUEST_DTSG:
-// case REQUEST_BUDDY_LIST:
-// case REQUEST_LOAD_FRIEND:
-// case REQUEST_LOAD_FRIENDS:
-// case REQUEST_USER_INFO:
-// case REQUEST_LOAD_REQUESTS:
-// case REQUEST_SEARCH:
-// case REQUEST_DELETE_FRIEND:
-// case REQUEST_REQUEST_FRIEND:
-// case REQUEST_APPROVE_FRIEND:
-// case REQUEST_CANCEL_REQUEST:
-// case REQUEST_FEEDS:
-// case REQUEST_PAGES:
-// case REQUEST_NOTIFICATIONS:
-// case REQUEST_RECONNECT:
-// case REQUEST_POST_STATUS:
-// case REQUEST_IDENTITY_SWITCH:
-// case REQUEST_STATUS_COMPOSER:
-// case REQUEST_LINK_SCRAPER:
-// case REQUEST_MESSAGE_SEND:
-// case REQUEST_MESSAGE_SEND2:
-// case REQUEST_THREAD_INFO:
-// case REQUEST_MESSAGES_RECEIVE:
-// case REQUEST_VISIBILITY:
-// case REQUEST_POKE:
-// case REQUEST_ASYNC:
-// case REQUEST_MARK_READ:
-// case REQUEST_NOTIFICATIONS_READ:
-// case REQUEST_UNREAD_THREADS:
-// case REQUEST_TYPING_SEND:
- default:
- return (DWORD)0;
- }
-}
-
-int facebook_client::choose_method(RequestType request_type)
-{
- switch (request_type)
- {
- case REQUEST_LOGIN:
- case REQUEST_SETUP_MACHINE:
- case REQUEST_BUDDY_LIST:
- case REQUEST_POST_STATUS:
- case REQUEST_IDENTITY_SWITCH:
- case REQUEST_STATUS_COMPOSER:
- case REQUEST_LINK_SCRAPER:
- case REQUEST_MESSAGE_SEND:
- case REQUEST_MESSAGE_SEND2:
- case REQUEST_THREAD_INFO:
- case REQUEST_VISIBILITY:
- case REQUEST_POKE:
- case REQUEST_ASYNC:
- case REQUEST_MARK_READ:
- case REQUEST_NOTIFICATIONS_READ:
- case REQUEST_TYPING_SEND:
- case REQUEST_LOGOUT:
- case REQUEST_DELETE_FRIEND:
- case REQUEST_REQUEST_FRIEND:
- case REQUEST_APPROVE_FRIEND:
- case REQUEST_CANCEL_REQUEST:
- case REQUEST_UNREAD_THREADS:
- return REQUEST_POST;
-
-// case REQUEST_HOME:
-// case REQUEST_DTSG:
-// case REQUEST_MESSAGES_RECEIVE:
-// case REQUEST_FEEDS:
-// case REQUEST_PAGES:
-// case REQUEST_NOTIFICATIONS:
-// case REQUEST_RECONNECT:
-// case REQUEST_LOAD_FRIEND:
-// case REQUEST_LOAD_FRIENDS:
-// case REQUEST_USER_INFO:
-// case REQUEST_LOAD_REQUESTS:
-// case REQUEST_SEARCH:
- default:
- return REQUEST_GET;
- }
-}
-
-std::string facebook_client::choose_proto(RequestType request_type)
-{
- if (choose_security_level(request_type) == NLHRF_SSL)
- return HTTP_PROTO_SECURE;
- else
- return HTTP_PROTO_REGULAR;
-}
-
-std::string facebook_client::choose_server(RequestType request_type, std::string* data, std::string* get_data)
-{
- switch (request_type)
- {
- case REQUEST_LOGIN:
- return FACEBOOK_SERVER_LOGIN;
-
- case REQUEST_MESSAGES_RECEIVE:
- {
- std::string server = FACEBOOK_SERVER_CHAT;
- utils::text::replace_first(&server, "%s", this->chat_conn_num_.empty() ? "0" : this->chat_conn_num_);
- utils::text::replace_first(&server, "%s", this->chat_channel_host_);
- return server;
- }
-
- case REQUEST_HOME:
- case REQUEST_DTSG:
- case REQUEST_APPROVE_FRIEND:
- case REQUEST_LOAD_REQUESTS:
- case REQUEST_SEARCH:
- case REQUEST_USER_INFO:
- return FACEBOOK_SERVER_MOBILE;
-
-// case REQUEST_LOGOUT:
-// case REQUEST_BUDDY_LIST:
-// case REQUEST_LOAD_FRIEND:
-// case REQUEST_LOAD_FRIENDS:
-// case REQUEST_FEEDS:
-// case REQUEST_PAGES:
-// case REQUEST_NOTIFICATIONS:
-// case REQUEST_RECONNECT:
-// case REQUEST_POST_STATUS:
-// case REQUEST_IDENTITY_SWITCH:
-// case REQUEST_STATUS_COMPOSER:
-// case REQUEST_LINK_SCRAPER:
-// case REQUEST_MESSAGE_SEND:
-// case REQUEST_MESSAGE_SEND2:
-// case REQUEST_THREAD_INFO:
-// case REQUEST_VISIBILITY:
-// case REQUEST_POKE:
-// case REQUEST_ASYNC:
-// case REQUEST_MARK_READ:
-// case REQUEST_NOTIFICATIONS_READ:
-// case REQUEST_TYPING_SEND:
-// case REQUEST_SETUP_MACHINE:
-// case REQUEST_DELETE_FRIEND:
-// case REQUEST_REQUEST_FRIEND:
-// case REQUEST_CANCEL_REQUEST:
-// case REQUEST_UNREAD_THREADS:
- default:
- return FACEBOOK_SERVER_REGULAR;
- }
-}
-
-std::string facebook_client::choose_action(RequestType request_type, std::string* data, std::string* get_data)
-{
- switch (request_type)
- {
- case REQUEST_LOGIN:
- return "/login.php?login_attempt=1";
-
- case REQUEST_SETUP_MACHINE:
- return "/checkpoint/?next";
-
- case REQUEST_LOGOUT:
- return "/logout.php";
-
- case REQUEST_HOME:
- return "/profile.php?v=info";
-
- case REQUEST_DTSG:
- return "/editprofile.php?edit=current_city&type=basic";
-
- case REQUEST_BUDDY_LIST:
- return "/ajax/chat/buddy_list.php?__a=1";
-
- case REQUEST_LOAD_FRIEND:
- {
- std::string action = "/ajax/chat/user_info.php?__a=1&viewer=%s&__user=%s";
- utils::text::replace_all(&action, "%s", self_.user_id);
- if (get_data != NULL) {
- action += "&" + (*get_data);
- }
- return action;
- }
-
- case REQUEST_LOAD_FRIENDS:
- {
- std::string action = "/ajax/chat/user_info_all.php?__a=1&viewer=%s&__user=%s";
- utils::text::replace_all(&action, "%s", self_.user_id);
- return action;
- }
-
- case REQUEST_USER_INFO:
- {
- std::string action = "/%sv=info";
- if (get_data != NULL) {
- utils::text::replace_all(&action, "%s", *get_data);
- }
- return action;
- }
-
- case REQUEST_LOAD_REQUESTS:
- {
- return "/friends/";
- }
-
- case REQUEST_SEARCH:
- {
- std::string action = "/search/?search=people&query=";
- if (get_data != NULL) {
- action += *get_data;
- }
- return action;
- }
-
- case REQUEST_UNREAD_THREADS:
- {
- return "/ajax/mercury/unread_threads.php?__a=1";
- }
-
- case REQUEST_DELETE_FRIEND:
- {
- std::string action = "/ajax/profile/removefriendconfirm.php?__a=1";
- if (get_data != NULL) {
- action += *get_data;
- }
- return action;
- }
-
- case REQUEST_REQUEST_FRIEND:
- {
- return "/ajax/add_friend/action.php?__a=1";
- }
-
- case REQUEST_APPROVE_FRIEND:
- {
- std::string action = "/a/notifications.php?__a=1";
- if (get_data != NULL) {
- action += "&" + (*get_data);
- }
- return action;
- }
-
- case REQUEST_CANCEL_REQUEST:
- {
- return "/ajax/friends/requests/cancel.php?__a=1";
- }
-
- case REQUEST_FEEDS:
- {
- std::string action = "/ajax/intent.php?filter=";
- action += get_newsfeed_type();
- action += "&request_type=4&__a=1&newest=%s&ignore_self=true&load_newer=true&__user=%s";
- std::string newest = utils::conversion::to_string((void*)&this->last_feeds_update_, UTILS_CONV_TIME_T);
- utils::text::replace_first(&action, "%s", newest);
- utils::text::replace_first(&action, "%s", self_.user_id);
- return action;
- }
-
- case REQUEST_PAGES:
- {
- return "/bookmarks/pages";
- }
-
- case REQUEST_NOTIFICATIONS:
- {
- std::string action = "/ajax/notifications/get.php?__a=1&user=%s&time=0&version=2&__user=%s";
- // TODO: use better format notifications request
- // std::string action = "/ajax/notifications/client/get.php?__a=1&user=%s&time=0&version=2&__user=%s";
- utils::text::replace_all(&action, "%s", self_.user_id);
- return action;
- }
-
- case REQUEST_RECONNECT:
- {
- std::string action = "/ajax/presence/reconnect.php?__a=1&reason=%s&fb_dtsg=%s&__user=%s";
-
- if (this->chat_reconnect_reason_.empty())
- this->chat_reconnect_reason_ = "6";
-
- utils::text::replace_first(&action, "%s", this->chat_reconnect_reason_);
- utils::text::replace_first(&action, "%s", this->dtsg_);
- utils::text::replace_first(&action, "%s", this->self_.user_id);
- return action;
- }
-
- case REQUEST_POST_STATUS:
- return "/ajax/updatestatus.php?__a=1";
-
- case REQUEST_IDENTITY_SWITCH:
- return "/identity_switch.php?__a=1";
-
- case REQUEST_STATUS_COMPOSER:
- return "/ajax/profile/composer.php?__a=1";
-
- case REQUEST_LINK_SCRAPER:
- {
- std::string action = "/ajax/composerx/attachment/link/scraper/?__a=1&composerurihash=2&scrape_url=";
- if (get_data != NULL) {
- action += utils::url::encode(*get_data);
- }
- return action;
- }
-
- case REQUEST_MESSAGE_SEND:
- return "/ajax/mercury/send_messages.php?__a=1";
-
- case REQUEST_MESSAGE_SEND2:
- return "/ajax/messaging/send.php";
-
- case REQUEST_THREAD_INFO:
- return "/ajax/mercury/thread_info.php?__a=1";
-
- case REQUEST_MESSAGES_RECEIVE:
- {
- std::string action = "/pull?channel=" + (this->chat_channel_.empty() ? "p_" + self_.user_id : this->chat_channel_);
- action += "&seq=" + (this->chat_sequence_num_.empty() ? "0" : this->chat_sequence_num_);
- action += "&partition=" + (this->chat_channel_partition_.empty() ? "0" : this->chat_channel_partition_);
- action += "&clientid=" + this->chat_clientid_;
- action += "&cb=" + utils::text::rand_string(4, "0123456789abcdefghijklmnopqrstuvwxyz");
- action += "&idle=0&state=active";
- if (!this->chat_sticky_num_.empty())
- action += "&sticky_token=" + this->chat_sticky_num_;
-
- return action;
- }
-
- case REQUEST_VISIBILITY:
- return "/ajax/chat/privacy/visibility.php?__a=1";
-
- case REQUEST_POKE:
- return "/pokes/dialog/?__a=1";
-
- case REQUEST_ASYNC:
- {
- std::string action = "/ajax/messaging/async.php?__a=1";
- if (get_data != NULL) {
- action += "&" + (*get_data);
- }
- return action;
- }
-
- case REQUEST_MARK_READ:
- return "/ajax/mercury/change_read_status.php?__a=1";
-
- case REQUEST_NOTIFICATIONS_READ:
- {
- std::string action = "/ajax/notifications/mark_read.php?__a=1";
- if (get_data != NULL) {
- action += "&" + (*get_data);
- }
- return action;
- }
-
- case REQUEST_TYPING_SEND:
- return "/ajax/messaging/typ.php?__a=1";
-
- default:
- return "/?_fb_noscript=1";
- }
-}
-
-bool facebook_client::notify_errors(RequestType request_type)
-{
- switch (request_type)
- {
- case REQUEST_BUDDY_LIST:
- case REQUEST_MESSAGE_SEND2:
- case REQUEST_MESSAGE_SEND:
- case REQUEST_ASYNC:
- return false;
-
- default:
- return true;
- }
-}
-
-NETLIBHTTPHEADER* facebook_client::get_request_headers(int request_type, int* headers_count)
-{
- if (request_type == REQUEST_POST)
- *headers_count = 5;
- else
- *headers_count = 4;
-
- NETLIBHTTPHEADER* headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count));
-
- if (request_type == REQUEST_POST)
- {
- headers[4].szName = "Content-Type";
- headers[4].szValue = "application/x-www-form-urlencoded; charset=utf-8";
- }
-
- headers[3].szName = "Cookie";
- headers[3].szValue = load_cookies();
- headers[2].szName = "User-Agent";
- headers[2].szValue = (char *)g_strUserAgent.c_str();
- headers[1].szName = "Accept";
- headers[1].szValue = "*/*";
- headers[0].szName = "Accept-Language";
- headers[0].szValue = "en,en-US;q=0.9";
-
- return headers;
-}
-
-std::string facebook_client::get_newsfeed_type()
-{
- BYTE feed_type = parent->getByte(FACEBOOK_KEY_FEED_TYPE, 0);
- if (feed_type >= SIZEOF(feed_types))
- feed_type = 0;
- return feed_types[feed_type].id;
-}
-
-std::string facebook_client::get_server_type()
-{
- BYTE server_type = parent->getByte(FACEBOOK_KEY_SERVER_TYPE, 0);
- if (server_type >= SIZEOF(server_types))
- server_type = 0;
- return server_types[server_type].id;
-}
-
-std::string facebook_client::get_privacy_type()
-{
- BYTE privacy_type = parent->getByte(FACEBOOK_KEY_PRIVACY_TYPE, 0);
- if (privacy_type >= SIZEOF(privacy_types))
- privacy_type = 0;
- return privacy_types[privacy_type].id;
-}
-
-
-char* facebook_client::load_cookies()
-{
- ScopedLock s(cookies_lock_);
-
- std::string cookieString = "isfbe=false;";
-
- if (!cookies.empty())
- for (std::map< std::string, std::string >::iterator iter = cookies.begin(); iter != cookies.end(); ++iter)
- {
- cookieString.append(iter->first);
- cookieString.append(1, '=');
- cookieString.append(iter->second);
- cookieString.append(1, ';');
- }
-
- return mir_strdup(cookieString.c_str());
-}
-
-void facebook_client::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount)
-{
- ScopedLock c(cookies_lock_);
-
- for (int i = 0; i < headersCount; i++)
- {
- std::string header_name = headers[i].szName; // TODO: Casting?
- std::string header_value = headers[i].szValue; // TODO: Casting?
-
- if (header_name == "Set-Cookie")
- {
- std::string cookie_name = header_value.substr(0, header_value.find("="));
- std::string cookie_value = header_value.substr(header_value.find("=") + 1, header_value.find(";") - header_value.find("=") - 1);
- if (cookie_value == "deleted")
- {
- parent->debugLogA(" Deleted cookie '%s'", cookie_name.c_str());
- cookies.erase(cookie_name);
- } else {
- parent->debugLogA(" New cookie '%s'", cookie_name.c_str());
- cookies[cookie_name] = cookie_value;
- }
- }
- else
- { // TODO RM: (un)comment
- //parent->debugLogA("----- Got header '%s': %s", header_name.c_str(), header_value.c_str());
- resp->headers[header_name] = header_value;
- }
- }
-}
-
-void facebook_client::clear_cookies()
-{
- ScopedLock s(cookies_lock_);
-
- if (!cookies.empty())
- cookies.clear();
-}
-
-void facebook_client::clear_notifications()
-{
- for (std::map<std::string, facebook_notification*>::iterator it = notifications.begin(); it != notifications.end(); ) {
- if (it->second->hWndPopup != NULL)
- PUDeletePopup(it->second->hWndPopup); // close popup
-
- delete it->second;
- it = notifications.erase(it);
- }
-
- notifications.clear();
-}
-
-void loginError(FacebookProto *proto, std::string error_str) {
- error_str = utils::text::trim(
- utils::text::special_expressions_decode(
- utils::text::remove_html(
- utils::text::edit_html(error_str))));
-
- proto->debugLogA(" ! ! Login error: %s", error_str.length() ? error_str.c_str() : "Unknown error");
-
- TCHAR buf[200];
- mir_sntprintf(buf, SIZEOF(buf), TranslateT("Login error: %s"),
- (!error_str.length()) ? TranslateT("Unknown error") : ptrT(mir_utf8decodeT(error_str.c_str())));
- proto->facy.client_notify(buf);
-}
-
-bool facebook_client::login(const char *username, const char *password)
-{
- handle_entry("login");
-
- username_ = username;
- password_ = password;
-
- if (cookies.empty()) {
- // Set device ID
- ptrA device( parent->getStringA(FACEBOOK_KEY_DEVICE_ID));
- if (device != NULL)
- cookies["datr"] = device;
-
- // Get initial cookies
- flap(REQUEST_HOME);
- }
-
- // Prepare login data
- std::string data = "persistent=1";
- data += "&email=" + utils::url::encode(username);
- data += "&pass=" + utils::url::encode(password);
-
- ptrA locale(parent->getStringA(FACEBOOK_KEY_LOCALE));
- if (locale != NULL)
- data += "&locale=" + std::string(locale);
-
- // Send validation
- http::response resp = flap(REQUEST_LOGIN, &data);
-
- // Save Device ID
- if (cookies["datr"].length())
- parent->setString(FACEBOOK_KEY_DEVICE_ID, cookies["datr"].c_str());
-
- if (resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end())
- {
- // Check for invalid requests
- if (resp.headers["Location"].find("invalid_request.php") != std::string::npos) {
- client_notify(TranslateT("Login error: Invalid request."));
- parent->debugLogA(" ! ! Login error: Invalid request.");
- return handle_error("login", FORCE_QUIT);
- }
-
- // Check whether some Facebook things are required
- if (resp.headers["Location"].find("help.php") != std::string::npos)
- {
- client_notify(TranslateT("Login error: Some Facebook things are required."));
- parent->debugLogA(" ! ! Login error: Some Facebook things are required.");
- // return handle_error("login", FORCE_QUIT);
- }
-
- // Check whether setting Machine name is required
- if (resp.headers["Location"].find("/checkpoint/") != std::string::npos)
- {
- resp = flap(REQUEST_SETUP_MACHINE, NULL, NULL, REQUEST_GET);
-
- if (resp.data.find("login_approvals_no_phones") != std::string::npos) {
- // Code approval - but no phones in account
- loginError(parent, utils::text::source_get_value(&resp.data, 4, "login_approvals_no_phones", "<div", ">", "</div>"));
- return handle_error("login", FORCE_QUIT);
- }
-
- std::string inner_data;
- if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) {
-
- // Check if we need to approve also last unapproved device
- if (resp.data.find("name=\"name_action_selected\"") == std::string::npos) {
- // 1) Continue
- inner_data = "submit[Continue]=Continue";
- inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
- inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
- resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
-
- // 2) Approve last unknown login
- // inner_data = "submit[I%20don't%20recognize]=I%20don't%20recognize"; // Don't recognize - this will force to change account password
- inner_data = "submit[This%20is%20Okay]=This%20is%20Okay"; // Recognize
- inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
- inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
- resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
-
- // 3) Save last device
- inner_data = "submit[Continue]=Continue";
- inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
- inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
- inner_data += "&name_action_selected=save_device"; // Save device - or "dont_save"
- resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
- }
-
- // Save this actual device
- inner_data = "submit[Continue]=Continue";
- inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
- inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
- inner_data += "&name_action_selected=save_device"; // Save device - or "dont_save"
- resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
- }
-
- // Save actual machine name
- // inner_data = "machine_name=Miranda%20NG&submit[Don't%20Save]=Don't%20Save"; // Don't save
- inner_data = "machine_name=Miranda%20NG&submit[Save%20Device]=Save%20Device"; // Save
- inner_data += "&lsd=" + utils::text::source_get_value(&resp.data, 3, "name=\"lsd\"", "value=\"", "\"");
- inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
- inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
-
- resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
- }
- }
-
- switch (resp.code)
- {
- case HTTP_CODE_FAKE_DISCONNECTED:
- {
- // When is error only because timeout, try login once more
- if (handle_error("login"))
- return login(username, password);
- else
- return handle_error("login", FORCE_QUIT);
- }
-
- case HTTP_CODE_OK: // OK page returned, but that is regular login page we don't want in fact
- {
- // Check whether captcha code is required
- if (resp.data.find("id=\"captcha\"") != std::string::npos)
- {
- client_notify(TranslateT("Login error: Captcha code is required. Bad login credentials?"));
- parent->debugLogA(" ! ! Login error: Captcha code is required.");
- return handle_error("login", FORCE_QUIT);
- }
-
- // Get and notify error message
- loginError(parent, utils::text::source_get_value(&resp.data, 4, "login_error_box", "<div", ">", "</div>"));
- }
- case HTTP_CODE_FORBIDDEN: // Forbidden
- case HTTP_CODE_NOT_FOUND: // Not Found
- default:
- return handle_error("login", FORCE_QUIT);
-
- case HTTP_CODE_FOUND: // Found and redirected to Home, Logged in, everything is OK
- {
- if (cookies.find("c_user") != cookies.end()) {
- this->self_.user_id = cookies.find("c_user")->second;
- parent->setString(FACEBOOK_KEY_ID, this->self_.user_id.c_str());
- parent->debugLogA(" Got self user id: %s", this->self_.user_id.c_str());
- return handle_success("login");
- } else {
- client_notify(TranslateT("Login error, probably bad login credentials."));
- parent->debugLogA(" ! ! Login error, probably bad login credentials.");
- return handle_error("login", FORCE_QUIT);
- }
- }
- }
-}
-
-bool facebook_client::logout()
-{
- if (parent->getByte(FACEBOOK_KEY_DISABLE_LOGOUT, 0))
- return true;
-
- handle_entry("logout");
-
- std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
- data += "&ref=mb&h=" + this->logout_hash_;
-
- http::response resp = flap(REQUEST_LOGOUT, &data);
-
- if (hFcbCon)
- Netlib_CloseHandle(hFcbCon);
- hFcbCon = NULL;
-
- // Process result
- username_ = password_ = self_.user_id = "";
-
- switch (resp.code)
- {
- case HTTP_CODE_OK:
- case HTTP_CODE_FOUND:
- return handle_success("logout");
-
- default:
- return false; // Logout not finished properly, but..okay, who cares :P
- }
-}
-
-bool facebook_client::home()
-{
- handle_entry("home");
-
- // get fb_dtsg
- http::response resp = flap(REQUEST_DTSG);
-
- // Check whether HTTPS connection is required and we don't have it enabled
- if (!this->https_ && resp.headers["Location"].find("https://") != std::string::npos) {
- client_notify(TranslateT("Your account requires HTTPS connection. Activating."));
- parent->setByte(FACEBOOK_KEY_FORCE_HTTPS, 1);
- this->https_ = true;
- return home();
- }
-
- this->dtsg_ = utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
- parent->debugLogA(" Got self dtsg: %s", this->dtsg_.c_str());
-
- if (this->dtsg_.empty())
- return false;
-
- resp = flap(REQUEST_HOME);
-
- switch (resp.code)
- {
- case HTTP_CODE_OK:
- {
- // Get real name
- this->self_.real_name = utils::text::source_get_value(&resp.data, 2, "<strong class=\"profileName\">", "</strong>");
-
- // Get and strip optional nickname
- std::string::size_type pos = this->self_.real_name.find("<span class=\"alternate_name\">");
- if (pos != std::string::npos) {
- this->self_.nick = utils::text::source_get_value(&this->self_.real_name, 2, "<span class=\"alternate_name\">(", ")</span>");
- parent->debugLogA(" Got self nick name: %s", this->self_.nick.c_str());
-
- this->self_.real_name = this->self_.real_name.substr(0, pos - 1);
- }
- parent->debugLogA(" Got self real name: %s", this->self_.real_name.c_str());
-
- // Save name and nickname
- parent->SaveName(NULL, &this->self_);
-
- if (this->self_.real_name.empty()) {
- client_notify(TranslateT("Something happened to Facebook. Maybe there was some major update so you should wait for an update."));
- return handle_error("home", FORCE_QUIT);
- }
-
- // Get avatar
- this->self_.image_url = utils::text::source_get_value(&resp.data, 3, "class=\"l\"", "<img src=\"", "\"");
- parent->debugLogA(" Got self avatar: %s", this->self_.image_url.c_str());
- parent->CheckAvatarChange(NULL, this->self_.image_url);
-
- // Get logout hash
- this->logout_hash_ = utils::text::source_get_value2(&resp.data, "/logout.php?h=", "&\"");
- parent->debugLogA(" Got self logout hash: %s", this->logout_hash_.c_str());
-
- return handle_success("home");
- }
- case HTTP_CODE_FOUND:
- // Work-around for replica_down, f**king hell what's that?
- parent->debugLogA(" REPLICA_DOWN is back in force!");
- return this->home();
-
- default:
- return handle_error("home", FORCE_QUIT);
- }
-}
-
-bool facebook_client::chat_state(bool online)
-{
- handle_entry("chat_state");
-
- std::string data = (online ? "visibility=1" : "visibility=0");
- data += "&window_id=0";
- data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
- data += "&phstamp=0&__user=" + self_.user_id;
- http::response resp = flap(REQUEST_VISIBILITY, &data);
-
- return handle_success("chat_state");
-}
-
-bool facebook_client::reconnect()
-{
- handle_entry("reconnect");
-
- // Request reconnect
- http::response resp = flap(REQUEST_RECONNECT);
-
- switch (resp.code)
- {
- case HTTP_CODE_OK:
- {
- this->chat_channel_ = utils::text::source_get_value(&resp.data, 2, "\"user_channel\":\"", "\"");
- parent->debugLogA(" Got self channel: %s", this->chat_channel_.c_str());
-
- this->chat_channel_partition_ = utils::text::source_get_value2(&resp.data, "\"partition\":", ",}");
- parent->debugLogA(" Got self channel partition: %s", this->chat_channel_partition_.c_str());
-
- this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"host\":\"", "\"");
- parent->debugLogA(" Got self channel host: %s", this->chat_channel_host_.c_str());
-
- this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
- parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
-
- this->chat_conn_num_ = utils::text::source_get_value2(&resp.data, "\"max_conn\":", ",}");
- parent->debugLogA(" Got self max_conn: %s", this->chat_conn_num_.c_str());
-
- return handle_success("reconnect");
- }
-
- default:
- return handle_error("reconnect", FORCE_DISCONNECT);
- }
-}
-
-bool facebook_client::buddy_list()
-{
- handle_entry("buddy_list");
-
- // Prepare update data
- std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + this->dtsg_ + "&__user=" + this->self_.user_id;
-
- {
- ScopedLock s(buddies_lock_);
-
- data += "&cached_user_info_ids=";
- int counter = 0;
- for (List::Item< facebook_user >* i = buddies.begin(); i != NULL; i = i->next, counter++)
- {
- data += i->data->user_id + "%2C";
- }
- }
-
- // Get buddy list
- http::response resp = flap(REQUEST_BUDDY_LIST, &data);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- parent->ForkThread(&FacebookProto::ProcessBuddyList, new std::string(resp.data));
- return handle_success("buddy_list");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("buddy_list");
- }
-}
-
-bool facebook_client::load_friends()
-{
- handle_entry("load_friends");
-
- // Get buddy list
- http::response resp = flap(REQUEST_LOAD_FRIENDS);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- parent->ForkThread(&FacebookProto::ProcessFriendList, new std::string(resp.data));
- return handle_success("load_friends");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("load_friends");
- }
-}
-
-bool facebook_client::feeds()
-{
- handle_entry("feeds");
-
- // Get feeds
- http::response resp = flap(REQUEST_FEEDS);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- if (resp.data.find("\"num_stories\":0") == std::string::npos)
- parent->ForkThread(&FacebookProto::ProcessFeeds, new std::string(resp.data));
-
- return handle_success("feeds");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("feeds");
- }
-}
-
-bool facebook_client::load_pages()
-{
- if (!parent->getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
- return true;
-
- handle_entry("load_pages");
-
- // Get feeds
- http::response resp = flap(REQUEST_PAGES);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- {
- 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;
-
- parent->debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
- pages[id] = title;
- }
-
- return handle_success("load_pages");
- }
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("load_pages");
- }
-}
-
-bool facebook_client::channel()
-{
- handle_entry("channel");
-
- // Get update
- http::response resp = flap(REQUEST_MESSAGES_RECEIVE);
-
- if (resp.data.empty()) {
- // Something went wrong
- return handle_error("channel");
- }
-
- std::string type = utils::text::source_get_value(&resp.data, 2, "\"t\":\"", "\"");
- if (type == "continue" || type == "heartbeat") {
- // Everything is OK, no new message received
- }
- else if (type == "lb") {
- // Some new stuff (idk how does it work yet)
- this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"vip\":\"", "\"");
- parent->debugLogA(" Got self channel host: %s", this->chat_channel_host_.c_str());
-
- this->chat_sticky_num_ = utils::text::source_get_value2(&resp.data, "\"sticky\":\"", "\"");
- parent->debugLogA(" Got self sticky number: %s", this->chat_sticky_num_.c_str());
- }
- else if (type == "fullReload" || type == "refresh") {
- // Something went wrong (server flooding?)
- parent->debugLogA("! ! ! Requested %s", type.c_str());
-
- this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
- parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
-
- this->chat_reconnect_reason_ = utils::text::source_get_value2(&resp.data, "\"reason\":", ",}");
- parent->debugLogA(" Reconnect reason: %s", this->chat_reconnect_reason_.c_str());
-
- if (type == "refresh")
- return this->reconnect();
- }
- else {
- // Something has been received, throw to new thread to process
- std::string* response_data = new std::string(resp.data);
- parent->ForkThread(&FacebookProto::ProcessMessages, response_data);
-
- // Increment sequence number
- this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
- parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
- }
-
- // Return
- switch (resp.code)
- {
- case HTTP_CODE_OK:
- return handle_success("channel");
-
- case HTTP_CODE_GATEWAY_TIMEOUT:
- // Maybe we have same clientid as other connected client, try to generate different one
- this->chat_clientid_ = utils::text::rand_string(8, "0123456789abcdef");
-
- // Intentionally fall to handle_error() below
- case HTTP_CODE_FAKE_DISCONNECTED:
- case HTTP_CODE_FAKE_ERROR:
- default:
- return handle_error("channel");
- }
-}
-
-bool facebook_client::send_message(std::string message_recipient, std::string message_text, std::string *error_text, MessageMethod method)
-{
- ScopedLock s(send_message_lock_);
-
- handle_entry("send_message");
-
- http::response resp;
-
- switch (method) {
- case MESSAGE_INBOX:
- {
- parent->debugLogA(" > Sending message through INBOX");
- std::string data = "action=send";
- data += "&body=" + utils::url::encode(message_text);
- data += "&recipients[0]=" + message_recipient;
- data += "&__user=" + this->self_.user_id;
- data += "&__a=1";
- data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
- data += "&phstamp=0";
-
- resp = flap(REQUEST_MESSAGE_SEND2, &data);
- break;
- }
- case MESSAGE_MERCURY:
- {
- parent->debugLogA(" > Sending message through CHAT");
- std::string data = "message_batch[0][action_type]=ma-type:user-generated-message";
- data += "&message_batch[0][thread_id]";
- data += "&message_batch[0][author]=fbid:" + this->self_.user_id;
- data += "&message_batch[0][author_email]";
- data += "&message_batch[0][coordinates]";
- data += "&message_batch[0][timestamp]=" + utils::time::mili_timestamp();
- data += "&message_batch[0][timestamp_absolute]";
- data += "&message_batch[0][timestamp_relative]";
- data += "&message_batch[0][is_unread]=false";
- data += "&message_batch[0][is_cleared]=false";
- data += "&message_batch[0][is_forward]=false";
- data += "&message_batch[0][spoof_warning]=false";
- data += "&message_batch[0][source]=source:chat:web";
- data += "&message_batch[0][source_tags][0]=source:chat";
- data += "&message_batch[0][body]=" + utils::url::encode(message_text);
- data += "&message_batch[0][has_attachment]=false";
- data += "&message_batch[0][html_body]=false";
- data += "&message_batch[0][specific_to_list][0]=fbid:" + message_recipient;
- data += "&message_batch[0][specific_to_list][1]=fbid:" + this->self_.user_id;
- data += "&message_batch[0][status]=0";
- data += "&message_batch[0][message_id]";
- data += "&message_batch[0][client_thread_id]=user:" + message_recipient;
- data += "&client=mercury";
- data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
- data += "&__user=" + this->self_.user_id;
- data += "&__a=1";
- data += "&phstamp=0";
-
- resp = flap(REQUEST_MESSAGE_SEND, &data);
- break;
- }
- case MESSAGE_TID:
- {
- parent->debugLogA(" > Sending message through MERCURY (TID)");
- std::string data = "message_batch[0][action_type]=ma-type:user-generated-message";
- data += "&message_batch[0][thread_id]=" + message_recipient;
- data += "&message_batch[0][author]=fbid:" + this->self_.user_id;
- data += "&message_batch[0][timestamp]=" + utils::time::mili_timestamp();
- data += "&message_batch[0][timestamp_absolute]=";
- data += "&message_batch[0][timestamp_relative]=";
- data += "&message_batch[0][is_unread]=false";
- data += "&message_batch[0][is_cleared]=false";
- data += "&message_batch[0][is_forward]=false";
- data += "&message_batch[0][source]=source:chat:web";
- data += "&message_batch[0][body]=" + utils::url::encode(message_text);
- data += "&message_batch[0][has_attachment]=false";
- data += "&message_batch[0][is_html]=false";
- data += "&message_batch[0][message_id]=";
- data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
- data += "&__user=" + this->self_.user_id;
- data += "&phstamp=0";
-
- resp = flap(REQUEST_MESSAGE_SEND, &data);
- break;
- }
- case MESSAGE_ASYNC:
- {
- parent->debugLogA(" > Sending message through ASYNC");
- std::string data = "action=send";
- data += "&body=" + utils::url::encode(message_text);
- data += "&recipients[0]=" + message_recipient;
- data += "&lsd=";
- data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
-
- resp = flap(REQUEST_ASYNC, &data);
- break;
- }
- }
-
- *error_text = resp.error_text;
-
- switch (resp.error_number)
- {
- case 0: // Everything is OK
- {
- // Remember this message id
- std::string mid = utils::text::source_get_value(&resp.data, 2, "\"message_id\":\"", "\"");
- messages_ignore.insert(std::make_pair(mid, false));
- } break;
-
- //case 1356002: // You are offline - wtf??
-
- case 1356003: // Contact is offline
- {
- MCONTACT hContact = parent->ContactIDToHContact(message_recipient);
- if (hContact != NULL)
- parent->setWord(hContact, "Status", ID_STATUS_OFFLINE);
- return false;
- } break;
-
- case 1356026: // Contact has alternative client
- {
- client_notify(TranslateT("Need confirmation for sending messages to other clients.\nOpen Facebook website and try to send message to this contact again!"));
- return false;
- } break;
-
- default: // Other error
- parent->debugLogA(" !!! Send message error #%d: %s", resp.error_number, resp.error_text);
- return false;
- }
-
- switch (resp.code)
- {
- case HTTP_CODE_OK:
- return handle_success("send_message");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- *error_text = Translate("Timeout when sending message.");
-
- handle_error("send_message");
- return false;
- }
-}
-
-bool facebook_client::post_status(status_data *status)
-{
- if (status == NULL || (status->text.empty() && status->url.empty()))
- return false;
-
- handle_entry("post_status");
-
- if (status->isPage) {
- std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
- data += "&user_id=" + status->user_id;
- data += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE);
- flap(REQUEST_IDENTITY_SWITCH, &data);
- }
-
- std::string data;
- RequestType request = REQUEST_POST_STATUS;
-
- if (!status->url.empty()) {
- data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
- data += "&composerid=u_jsonp_2_b";
- data += "&targetid=" + (status->user_id.empty() ? this->self_.user_id : status->user_id);
- data += "&istimeline=1&composercontext=composer&onecolumn=1&nctr[_mod]=pagelet_timeline_recent&__a=1&ttstamp=0";
- data += "&__user=" + (status->isPage && !status->user_id.empty() ? status->user_id : this->self_.user_id);
- data += "&loaded_components[0]=maininput&loaded_components[1]=backdateicon&loaded_components[2]=withtaggericon&loaded_components[3]=cameraicon&loaded_components[4]=placetaggericon&loaded_components[5]=mainprivacywidget&loaded_components[6]=withtaggericon&loaded_components[7]=backdateicon&loaded_components[8]=placetaggericon&loaded_components[9]=cameraicon&loaded_components[10]=mainprivacywidget&loaded_components[11]=maininput&loaded_components[12]=explicitplaceinput&loaded_components[13]=hiddenplaceinput&loaded_components[14]=placenameinput&loaded_components[15]=hiddensessionid&loaded_components[16]=withtagger&loaded_components[17]=backdatepicker&loaded_components[18]=placetagger&loaded_components[19]=citysharericon";
- http::response resp = flap(REQUEST_LINK_SCRAPER, &data, &status->url);
- resp.data = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(resp.data));
-
- data = "&xhpc_context=profile&xhpc_ismeta=1&xhpc_timeline=1&xhpc_composerid=u_jsonp_2_0&is_explicit_place=&composertags_place=&composer_session_id=&composertags_city=&disable_location_sharing=false&composer_predicted_city=&nctr[_mod]=pagelet_composer&__a=1&__dyn=&__req=1f&ttstamp=0";
- std::string form = utils::text::source_get_value(&resp.data, 2, "<form", "</form>");
- utils::text::replace_all(&form, "\\\"", "\"");
- data += "&" + utils::text::source_get_form_data(&form) + "&";
- //data += "&no_picture=0";
-
- request = REQUEST_STATUS_COMPOSER;
- }
-
- std::string text = utils::url::encode(status->text);
-
- data += "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
- data += "&xhpc_targetid=" + (status->user_id.empty() ? this->self_.user_id : status->user_id);
- data += "&__user=" + (status->isPage && !status->user_id.empty() ? status->user_id : this->self_.user_id);
- data += "&xhpc_message=" + text;
- data += "&xhpc_message_text=" + text;
- if (!status->isPage)
- data += "&audience[0][value]=" + get_privacy_type();
- if (!status->place.empty()) {
- data += "&composertags_place_name=";
- data += utils::url::encode(status->place);
- }
- for(std::vector<facebook_user*>::size_type i = 0; i < status->users.size(); i++) {
- data += "&composertags_with[" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
- data += "]=" + status->users[i]->user_id;
- data += "&text_composertags_with[" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
- data += "]=" + status->users[i]->real_name;
- delete status->users[i];
- }
- status->users.clear();
-
- data += "&xhpc_context=profile&xhpc_ismeta=1&xhpc_timeline=1&xhpc_composerid=u_0_2y&is_explicit_place=&composertags_place=&composertags_city=";
-
- http::response resp = flap(request, &data);
-
- if (status->isPage) {
- std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
- data += "&user_id=" + this->self_.user_id;
- data += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE);
- flap(REQUEST_IDENTITY_SWITCH, &data);
- }
-
- if (resp.isValid()) {
- parent->NotifyEvent(parent->m_tszUserName, TranslateT("Status update was successful."), NULL, FACEBOOK_EVENT_OTHER);
- return handle_success("post_status");
- }
-
- return handle_error("post_status");
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool facebook_client::save_url(const std::string &url,const std::tstring &filename, HANDLE &nlc)
-{
- NETLIBHTTPREQUEST req = {sizeof(req)};
- NETLIBHTTPREQUEST *resp;
- req.requestType = REQUEST_GET;
- req.szUrl = const_cast<char*>(url.c_str());
- req.flags = NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_PERSISTENT | NLHRF_NODUMP;
- req.nlc = nlc;
-
- resp = reinterpret_cast<NETLIBHTTPREQUEST*>(CallService(MS_NETLIB_HTTPTRANSACTION,
- reinterpret_cast<WPARAM>(this->parent->m_hNetlibUser), reinterpret_cast<LPARAM>(&req)));
-
- bool ret = false;
-
- if (resp) {
- nlc = resp->nlc;
- parent->debugLogA("@@@@@ Saving avatar URL %s to path %s", url.c_str(), _T2A(filename.c_str()));
-
- // Create folder if necessary
- std::tstring dir = filename.substr(0,filename.rfind('\\'));
- if (_taccess(dir.c_str(), 0))
- CreateDirectoryTreeT(dir.c_str());
-
- // Write to file
- FILE *f = _tfopen(filename.c_str(), _T("wb"));
- if (f != NULL) {
- fwrite(resp->pData,1,resp->dataLength,f);
- fclose(f);
-
- ret = _taccess(filename.c_str(), 0) == 0;
- }
-
- CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
- } else {
- nlc = NULL;
- }
-
- return ret;
-}
+/*
+
+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 facebook_client::client_notify(TCHAR* message)
+{
+ parent->NotifyEvent(parent->m_tszUserName, message, NULL, FACEBOOK_EVENT_CLIENT);
+}
+
+http::response facebook_client::flap(RequestType request_type, std::string* request_data, std::string* request_get_data, int method)
+{
+ NETLIBHTTPREQUEST nlhr = {sizeof(NETLIBHTTPREQUEST)};
+ nlhr.requestType = !method ? choose_method(request_type) : method;
+
+ std::string url = choose_proto(request_type);
+ url.append(choose_server(request_type, request_data, request_get_data));
+ url.append(choose_action(request_type, request_data, request_get_data));
+
+ nlhr.szUrl = (char*)url.c_str();
+ nlhr.flags = NLHRF_HTTP11 | choose_security_level(request_type);
+ nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount);
+
+ #ifdef _DEBUG
+ nlhr.flags |= NLHRF_DUMPASTEXT;
+ #else
+ nlhr.flags |= NLHRF_NODUMP;
+ #endif
+
+ switch (request_type)
+ {
+ case REQUEST_MESSAGES_RECEIVE:
+ nlhr.timeout = 1000 * 65; break;
+ default:
+ nlhr.timeout = 1000 * 20; break;
+ }
+
+ if (request_data != NULL)
+ {
+ nlhr.pData = (char*)(*request_data).c_str();
+ nlhr.dataLength = (int)request_data->length();
+ }
+
+ parent->debugLogA("@@@@@ Sending request to '%s'", nlhr.szUrl);
+
+ switch (request_type)
+ {
+ case REQUEST_LOGIN:
+ nlhr.nlc = NULL;
+ break;
+
+ case REQUEST_MESSAGES_RECEIVE:
+ nlhr.nlc = hMsgCon;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+
+ default:
+ WaitForSingleObject(fcb_conn_lock_, INFINITE);
+ nlhr.nlc = hFcbCon;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+ }
+
+ NETLIBHTTPREQUEST* pnlhr = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)handle_, (LPARAM)&nlhr);
+
+ mir_free(nlhr.headers[3].szValue);
+ mir_free(nlhr.headers);
+
+ http::response resp;
+
+ switch (request_type)
+ {
+ case REQUEST_LOGIN:
+ case REQUEST_SETUP_MACHINE:
+ break;
+
+ case REQUEST_MESSAGES_RECEIVE:
+ hMsgCon = pnlhr ? pnlhr->nlc : NULL;
+ break;
+
+ default:
+ ReleaseMutex(fcb_conn_lock_);
+ hFcbCon = pnlhr ? pnlhr->nlc : NULL;
+ break;
+ }
+
+ if (pnlhr != NULL)
+ {
+ parent->debugLogA("@@@@@ Got response with code %d", pnlhr->resultCode);
+ store_headers(&resp, pnlhr->headers, pnlhr->headersCount);
+ resp.code = pnlhr->resultCode;
+ resp.data = pnlhr->pData ? pnlhr->pData : "";
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)pnlhr);
+ } else {
+ parent->debugLogA("!!!!! No response from server (time-out)");
+ resp.code = HTTP_CODE_FAKE_DISCONNECTED;
+ // Better to have something set explicitely as this value is compaired in all communication requests
+ }
+
+ // Get Facebook's error message
+ if (resp.code == HTTP_CODE_OK) {
+ std::string::size_type pos = resp.data.find("\"error\":");
+ if (pos != std::string::npos) {
+ pos += 8;
+ int error_num = atoi(resp.data.substr(pos, resp.data.find(",", pos) - pos).c_str());
+ if (error_num != 0) {
+ std::string error = "";
+
+ pos = resp.data.find("\"errorDescription\":\"", pos);
+ if (pos != std::string::npos) {
+ pos += 20;
+ error = resp.data.substr(pos, resp.data.find("\"", pos) - pos);
+ error = utils::text::trim(utils::text::special_expressions_decode(utils::text::slashu_to_utf8(error)));
+ error = ptrA( mir_utf8decodeA(error.c_str()));
+ }
+
+ std::string title = "";
+ pos = resp.data.find("\"errorSummary\":\"", pos);
+ if (pos != std::string::npos) {
+ pos += 16;
+ title = resp.data.substr(pos, resp.data.find("\"", pos) - pos);
+ title = utils::text::trim(utils::text::special_expressions_decode(utils::text::slashu_to_utf8(title)));
+ title = ptrA( mir_utf8decodeA(title.c_str()));
+ }
+
+ bool silent = resp.data.find("\"silentError\":1") != std::string::npos;
+
+ resp.error_number = error_num;
+ resp.error_text = error;
+ resp.error_title = title;
+ resp.code = HTTP_CODE_FAKE_ERROR;
+
+ parent->debugLogA(" ! ! Received Facebook error: %d -- %s", error_num, error.c_str());
+ if (notify_errors(request_type) && !silent)
+ client_notify(_A2T(error.c_str()));
+ }
+ }
+ }
+
+ return resp;
+}
+
+bool facebook_client::handle_entry(std::string method)
+{
+ parent->debugLogA(" >> Entering %s()", method.c_str());
+ return true;
+}
+
+bool facebook_client::handle_success(std::string method)
+{
+ parent->debugLogA(" << Quitting %s()", method.c_str());
+ reset_error();
+ return true;
+}
+
+bool facebook_client::handle_error(std::string method, int action)
+{
+ increment_error();
+ parent->debugLogA("!!!!! %s(): Something with Facebook went wrong", method.c_str());
+
+ bool result = (error_count_ <= (UINT)parent->getByte(FACEBOOK_KEY_TIMEOUTS_LIMIT, FACEBOOK_TIMEOUTS_LIMIT));
+ if (action == FORCE_DISCONNECT || action == FORCE_QUIT)
+ result = false;
+
+ if (!result) {
+ reset_error();
+
+ if (action != FORCE_QUIT)
+ parent->SetStatus(ID_STATUS_OFFLINE);
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+DWORD facebook_client::choose_security_level(RequestType request_type)
+{
+ if (this->https_)
+ if (request_type != REQUEST_MESSAGES_RECEIVE || parent->getByte(FACEBOOK_KEY_FORCE_HTTPS_CHANNEL, DEFAULT_FORCE_HTTPS_CHANNEL))
+ return NLHRF_SSL;
+
+ switch (request_type) {
+ case REQUEST_LOGIN:
+ case REQUEST_SETUP_MACHINE:
+ return NLHRF_SSL;
+
+// case REQUEST_LOGOUT:
+// case REQUEST_HOME:
+// case REQUEST_DTSG:
+// case REQUEST_BUDDY_LIST:
+// case REQUEST_LOAD_FRIEND:
+// case REQUEST_LOAD_FRIENDS:
+// case REQUEST_USER_INFO:
+// case REQUEST_LOAD_REQUESTS:
+// case REQUEST_SEARCH:
+// case REQUEST_DELETE_FRIEND:
+// case REQUEST_REQUEST_FRIEND:
+// case REQUEST_APPROVE_FRIEND:
+// case REQUEST_CANCEL_REQUEST:
+// case REQUEST_FEEDS:
+// case REQUEST_PAGES:
+// case REQUEST_NOTIFICATIONS:
+// case REQUEST_RECONNECT:
+// case REQUEST_POST_STATUS:
+// case REQUEST_IDENTITY_SWITCH:
+// case REQUEST_STATUS_COMPOSER:
+// case REQUEST_LINK_SCRAPER:
+// case REQUEST_MESSAGE_SEND:
+// case REQUEST_MESSAGE_SEND2:
+// case REQUEST_THREAD_INFO:
+// case REQUEST_MESSAGES_RECEIVE:
+// case REQUEST_VISIBILITY:
+// case REQUEST_POKE:
+// case REQUEST_ASYNC:
+// case REQUEST_MARK_READ:
+// case REQUEST_NOTIFICATIONS_READ:
+// case REQUEST_UNREAD_THREADS:
+// case REQUEST_TYPING_SEND:
+ default:
+ return (DWORD)0;
+ }
+}
+
+int facebook_client::choose_method(RequestType request_type)
+{
+ switch (request_type)
+ {
+ case REQUEST_LOGIN:
+ case REQUEST_SETUP_MACHINE:
+ case REQUEST_BUDDY_LIST:
+ case REQUEST_POST_STATUS:
+ case REQUEST_IDENTITY_SWITCH:
+ case REQUEST_STATUS_COMPOSER:
+ case REQUEST_LINK_SCRAPER:
+ case REQUEST_MESSAGE_SEND:
+ case REQUEST_MESSAGE_SEND2:
+ case REQUEST_THREAD_INFO:
+ case REQUEST_VISIBILITY:
+ case REQUEST_POKE:
+ case REQUEST_ASYNC:
+ case REQUEST_MARK_READ:
+ case REQUEST_NOTIFICATIONS_READ:
+ case REQUEST_TYPING_SEND:
+ case REQUEST_LOGOUT:
+ case REQUEST_DELETE_FRIEND:
+ case REQUEST_REQUEST_FRIEND:
+ case REQUEST_APPROVE_FRIEND:
+ case REQUEST_CANCEL_REQUEST:
+ case REQUEST_UNREAD_THREADS:
+ return REQUEST_POST;
+
+// case REQUEST_HOME:
+// case REQUEST_DTSG:
+// case REQUEST_MESSAGES_RECEIVE:
+// case REQUEST_FEEDS:
+// case REQUEST_PAGES:
+// case REQUEST_NOTIFICATIONS:
+// case REQUEST_RECONNECT:
+// case REQUEST_LOAD_FRIEND:
+// case REQUEST_LOAD_FRIENDS:
+// case REQUEST_USER_INFO:
+// case REQUEST_LOAD_REQUESTS:
+// case REQUEST_SEARCH:
+ default:
+ return REQUEST_GET;
+ }
+}
+
+std::string facebook_client::choose_proto(RequestType request_type)
+{
+ if (choose_security_level(request_type) == NLHRF_SSL)
+ return HTTP_PROTO_SECURE;
+ else
+ return HTTP_PROTO_REGULAR;
+}
+
+std::string facebook_client::choose_server(RequestType request_type, std::string* data, std::string* get_data)
+{
+ switch (request_type)
+ {
+ case REQUEST_LOGIN:
+ return FACEBOOK_SERVER_LOGIN;
+
+ case REQUEST_MESSAGES_RECEIVE:
+ {
+ std::string server = FACEBOOK_SERVER_CHAT;
+ utils::text::replace_first(&server, "%s", this->chat_conn_num_.empty() ? "0" : this->chat_conn_num_);
+ utils::text::replace_first(&server, "%s", this->chat_channel_host_);
+ return server;
+ }
+
+ case REQUEST_HOME:
+ case REQUEST_DTSG:
+ case REQUEST_APPROVE_FRIEND:
+ case REQUEST_LOAD_REQUESTS:
+ case REQUEST_SEARCH:
+ case REQUEST_USER_INFO:
+ return FACEBOOK_SERVER_MOBILE;
+
+// case REQUEST_LOGOUT:
+// case REQUEST_BUDDY_LIST:
+// case REQUEST_LOAD_FRIEND:
+// case REQUEST_LOAD_FRIENDS:
+// case REQUEST_FEEDS:
+// case REQUEST_PAGES:
+// case REQUEST_NOTIFICATIONS:
+// case REQUEST_RECONNECT:
+// case REQUEST_POST_STATUS:
+// case REQUEST_IDENTITY_SWITCH:
+// case REQUEST_STATUS_COMPOSER:
+// case REQUEST_LINK_SCRAPER:
+// case REQUEST_MESSAGE_SEND:
+// case REQUEST_MESSAGE_SEND2:
+// case REQUEST_THREAD_INFO:
+// case REQUEST_VISIBILITY:
+// case REQUEST_POKE:
+// case REQUEST_ASYNC:
+// case REQUEST_MARK_READ:
+// case REQUEST_NOTIFICATIONS_READ:
+// case REQUEST_TYPING_SEND:
+// case REQUEST_SETUP_MACHINE:
+// case REQUEST_DELETE_FRIEND:
+// case REQUEST_REQUEST_FRIEND:
+// case REQUEST_CANCEL_REQUEST:
+// case REQUEST_UNREAD_THREADS:
+ default:
+ return FACEBOOK_SERVER_REGULAR;
+ }
+}
+
+std::string facebook_client::choose_action(RequestType request_type, std::string* data, std::string* get_data)
+{
+ switch (request_type)
+ {
+ case REQUEST_LOGIN:
+ return "/login.php?login_attempt=1";
+
+ case REQUEST_SETUP_MACHINE:
+ return "/checkpoint/?next";
+
+ case REQUEST_LOGOUT:
+ return "/logout.php";
+
+ case REQUEST_HOME:
+ return "/profile.php?v=info";
+
+ case REQUEST_DTSG:
+ return "/editprofile.php?edit=current_city&type=basic";
+
+ case REQUEST_BUDDY_LIST:
+ return "/ajax/chat/buddy_list.php?__a=1";
+
+ case REQUEST_LOAD_FRIEND:
+ {
+ std::string action = "/ajax/chat/user_info.php?__a=1&viewer=%s&__user=%s";
+ utils::text::replace_all(&action, "%s", self_.user_id);
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_LOAD_FRIENDS:
+ {
+ std::string action = "/ajax/chat/user_info_all.php?__a=1&viewer=%s&__user=%s";
+ utils::text::replace_all(&action, "%s", self_.user_id);
+ return action;
+ }
+
+ case REQUEST_USER_INFO:
+ {
+ std::string action = "/%sv=info";
+ if (get_data != NULL) {
+ utils::text::replace_all(&action, "%s", *get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_LOAD_REQUESTS:
+ {
+ return "/friends/";
+ }
+
+ case REQUEST_SEARCH:
+ {
+ std::string action = "/search/?search=people&query=";
+ if (get_data != NULL) {
+ action += *get_data;
+ }
+ return action;
+ }
+
+ case REQUEST_UNREAD_THREADS:
+ {
+ return "/ajax/mercury/unread_threads.php?__a=1";
+ }
+
+ case REQUEST_DELETE_FRIEND:
+ {
+ std::string action = "/ajax/profile/removefriendconfirm.php?__a=1";
+ if (get_data != NULL) {
+ action += *get_data;
+ }
+ return action;
+ }
+
+ case REQUEST_REQUEST_FRIEND:
+ {
+ return "/ajax/add_friend/action.php?__a=1";
+ }
+
+ case REQUEST_APPROVE_FRIEND:
+ {
+ std::string action = "/a/notifications.php?__a=1";
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_CANCEL_REQUEST:
+ {
+ return "/ajax/friends/requests/cancel.php?__a=1";
+ }
+
+ case REQUEST_FEEDS:
+ {
+ std::string action = "/ajax/intent.php?filter=";
+ action += get_newsfeed_type();
+ action += "&request_type=4&__a=1&newest=%s&ignore_self=true&load_newer=true&__user=%s";
+ std::string newest = utils::conversion::to_string((void*)&this->last_feeds_update_, UTILS_CONV_TIME_T);
+ utils::text::replace_first(&action, "%s", newest);
+ utils::text::replace_first(&action, "%s", self_.user_id);
+ return action;
+ }
+
+ case REQUEST_PAGES:
+ {
+ return "/bookmarks/pages";
+ }
+
+ case REQUEST_NOTIFICATIONS:
+ {
+ std::string action = "/ajax/notifications/get.php?__a=1&user=%s&time=0&version=2&__user=%s";
+ // TODO: use better format notifications request
+ // std::string action = "/ajax/notifications/client/get.php?__a=1&user=%s&time=0&version=2&__user=%s";
+ utils::text::replace_all(&action, "%s", self_.user_id);
+ return action;
+ }
+
+ case REQUEST_RECONNECT:
+ {
+ std::string action = "/ajax/presence/reconnect.php?__a=1&reason=%s&fb_dtsg=%s&__user=%s";
+
+ if (this->chat_reconnect_reason_.empty())
+ this->chat_reconnect_reason_ = "6";
+
+ utils::text::replace_first(&action, "%s", this->chat_reconnect_reason_);
+ utils::text::replace_first(&action, "%s", this->dtsg_);
+ utils::text::replace_first(&action, "%s", this->self_.user_id);
+ return action;
+ }
+
+ case REQUEST_POST_STATUS:
+ return "/ajax/updatestatus.php?__a=1";
+
+ case REQUEST_IDENTITY_SWITCH:
+ return "/identity_switch.php?__a=1";
+
+ case REQUEST_STATUS_COMPOSER:
+ return "/ajax/profile/composer.php?__a=1";
+
+ case REQUEST_LINK_SCRAPER:
+ {
+ std::string action = "/ajax/composerx/attachment/link/scraper/?__a=1&composerurihash=2&scrape_url=";
+ if (get_data != NULL) {
+ action += utils::url::encode(*get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_MESSAGE_SEND:
+ return "/ajax/mercury/send_messages.php?__a=1";
+
+ case REQUEST_MESSAGE_SEND2:
+ return "/ajax/messaging/send.php";
+
+ case REQUEST_THREAD_INFO:
+ return "/ajax/mercury/thread_info.php?__a=1";
+
+ case REQUEST_MESSAGES_RECEIVE:
+ {
+ std::string action = "/pull?channel=" + (this->chat_channel_.empty() ? "p_" + self_.user_id : this->chat_channel_);
+ action += "&seq=" + (this->chat_sequence_num_.empty() ? "0" : this->chat_sequence_num_);
+ action += "&partition=" + (this->chat_channel_partition_.empty() ? "0" : this->chat_channel_partition_);
+ action += "&clientid=" + this->chat_clientid_;
+ action += "&cb=" + utils::text::rand_string(4, "0123456789abcdefghijklmnopqrstuvwxyz");
+ action += "&idle=0&state=active";
+ if (!this->chat_sticky_num_.empty())
+ action += "&sticky_token=" + this->chat_sticky_num_;
+
+ return action;
+ }
+
+ case REQUEST_VISIBILITY:
+ return "/ajax/chat/privacy/visibility.php?__a=1";
+
+ case REQUEST_POKE:
+ return "/pokes/dialog/?__a=1";
+
+ case REQUEST_ASYNC:
+ {
+ std::string action = "/ajax/messaging/async.php?__a=1";
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_MARK_READ:
+ return "/ajax/mercury/change_read_status.php?__a=1";
+
+ case REQUEST_NOTIFICATIONS_READ:
+ {
+ std::string action = "/ajax/notifications/mark_read.php?__a=1";
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case REQUEST_TYPING_SEND:
+ return "/ajax/messaging/typ.php?__a=1";
+
+ default:
+ return "/?_fb_noscript=1";
+ }
+}
+
+bool facebook_client::notify_errors(RequestType request_type)
+{
+ switch (request_type)
+ {
+ case REQUEST_BUDDY_LIST:
+ case REQUEST_MESSAGE_SEND2:
+ case REQUEST_MESSAGE_SEND:
+ case REQUEST_ASYNC:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+NETLIBHTTPHEADER* facebook_client::get_request_headers(int request_type, int* headers_count)
+{
+ if (request_type == REQUEST_POST)
+ *headers_count = 5;
+ else
+ *headers_count = 4;
+
+ NETLIBHTTPHEADER* headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER)*(*headers_count));
+
+ if (request_type == REQUEST_POST)
+ {
+ headers[4].szName = "Content-Type";
+ headers[4].szValue = "application/x-www-form-urlencoded; charset=utf-8";
+ }
+
+ headers[3].szName = "Cookie";
+ headers[3].szValue = load_cookies();
+ headers[2].szName = "User-Agent";
+ headers[2].szValue = (char *)g_strUserAgent.c_str();
+ headers[1].szName = "Accept";
+ headers[1].szValue = "*/*";
+ headers[0].szName = "Accept-Language";
+ headers[0].szValue = "en,en-US;q=0.9";
+
+ return headers;
+}
+
+std::string facebook_client::get_newsfeed_type()
+{
+ BYTE feed_type = parent->getByte(FACEBOOK_KEY_FEED_TYPE, 0);
+ if (feed_type >= SIZEOF(feed_types))
+ feed_type = 0;
+ return feed_types[feed_type].id;
+}
+
+std::string facebook_client::get_server_type()
+{
+ BYTE server_type = parent->getByte(FACEBOOK_KEY_SERVER_TYPE, 0);
+ if (server_type >= SIZEOF(server_types))
+ server_type = 0;
+ return server_types[server_type].id;
+}
+
+std::string facebook_client::get_privacy_type()
+{
+ BYTE privacy_type = parent->getByte(FACEBOOK_KEY_PRIVACY_TYPE, 0);
+ if (privacy_type >= SIZEOF(privacy_types))
+ privacy_type = 0;
+ return privacy_types[privacy_type].id;
+}
+
+
+char* facebook_client::load_cookies()
+{
+ ScopedLock s(cookies_lock_);
+
+ std::string cookieString = "isfbe=false;";
+
+ if (!cookies.empty())
+ for (std::map< std::string, std::string >::iterator iter = cookies.begin(); iter != cookies.end(); ++iter)
+ {
+ cookieString.append(iter->first);
+ cookieString.append(1, '=');
+ cookieString.append(iter->second);
+ cookieString.append(1, ';');
+ }
+
+ return mir_strdup(cookieString.c_str());
+}
+
+void facebook_client::store_headers(http::response* resp, NETLIBHTTPHEADER* headers, int headersCount)
+{
+ ScopedLock c(cookies_lock_);
+
+ for (int i = 0; i < headersCount; i++)
+ {
+ std::string header_name = headers[i].szName; // TODO: Casting?
+ std::string header_value = headers[i].szValue; // TODO: Casting?
+
+ if (header_name == "Set-Cookie")
+ {
+ std::string cookie_name = header_value.substr(0, header_value.find("="));
+ std::string cookie_value = header_value.substr(header_value.find("=") + 1, header_value.find(";") - header_value.find("=") - 1);
+ if (cookie_value == "deleted")
+ {
+ parent->debugLogA(" Deleted cookie '%s'", cookie_name.c_str());
+ cookies.erase(cookie_name);
+ } else {
+ parent->debugLogA(" New cookie '%s'", cookie_name.c_str());
+ cookies[cookie_name] = cookie_value;
+ }
+ }
+ else
+ { // TODO RM: (un)comment
+ //parent->debugLogA("----- Got header '%s': %s", header_name.c_str(), header_value.c_str());
+ resp->headers[header_name] = header_value;
+ }
+ }
+}
+
+void facebook_client::clear_cookies()
+{
+ ScopedLock s(cookies_lock_);
+
+ if (!cookies.empty())
+ cookies.clear();
+}
+
+void facebook_client::clear_notifications()
+{
+ for (std::map<std::string, facebook_notification*>::iterator it = notifications.begin(); it != notifications.end(); ) {
+ if (it->second->hWndPopup != NULL)
+ PUDeletePopup(it->second->hWndPopup); // close popup
+
+ delete it->second;
+ it = notifications.erase(it);
+ }
+
+ notifications.clear();
+}
+
+void loginError(FacebookProto *proto, std::string error_str) {
+ error_str = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html(
+ utils::text::edit_html(error_str))));
+
+ proto->debugLogA(" ! ! Login error: %s", error_str.length() ? error_str.c_str() : "Unknown error");
+
+ TCHAR buf[200];
+ mir_sntprintf(buf, SIZEOF(buf), TranslateT("Login error: %s"),
+ (!error_str.length()) ? TranslateT("Unknown error") : ptrT(mir_utf8decodeT(error_str.c_str())));
+ proto->facy.client_notify(buf);
+}
+
+bool facebook_client::login(const char *username, const char *password)
+{
+ handle_entry("login");
+
+ username_ = username;
+ password_ = password;
+
+ if (cookies.empty()) {
+ // Set device ID
+ ptrA device( parent->getStringA(FACEBOOK_KEY_DEVICE_ID));
+ if (device != NULL)
+ cookies["datr"] = device;
+
+ // Get initial cookies
+ flap(REQUEST_HOME);
+ }
+
+ // Prepare login data
+ std::string data = "persistent=1";
+ data += "&email=" + utils::url::encode(username);
+ data += "&pass=" + utils::url::encode(password);
+
+ ptrA locale(parent->getStringA(FACEBOOK_KEY_LOCALE));
+ if (locale != NULL)
+ data += "&locale=" + std::string(locale);
+
+ // Send validation
+ http::response resp = flap(REQUEST_LOGIN, &data);
+
+ // Save Device ID
+ if (cookies["datr"].length())
+ parent->setString(FACEBOOK_KEY_DEVICE_ID, cookies["datr"].c_str());
+
+ if (resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end())
+ {
+ // Check for invalid requests
+ if (resp.headers["Location"].find("invalid_request.php") != std::string::npos) {
+ client_notify(TranslateT("Login error: Invalid request."));
+ parent->debugLogA(" ! ! Login error: Invalid request.");
+ return handle_error("login", FORCE_QUIT);
+ }
+
+ // Check whether some Facebook things are required
+ if (resp.headers["Location"].find("help.php") != std::string::npos)
+ {
+ client_notify(TranslateT("Login error: Some Facebook things are required."));
+ parent->debugLogA(" ! ! Login error: Some Facebook things are required.");
+ // return handle_error("login", FORCE_QUIT);
+ }
+
+ // Check whether setting Machine name is required
+ if (resp.headers["Location"].find("/checkpoint/") != std::string::npos)
+ {
+ resp = flap(REQUEST_SETUP_MACHINE, NULL, NULL, REQUEST_GET);
+
+ if (resp.data.find("login_approvals_no_phones") != std::string::npos) {
+ // Code approval - but no phones in account
+ loginError(parent, utils::text::source_get_value(&resp.data, 4, "login_approvals_no_phones", "<div", ">", "</div>"));
+ return handle_error("login", FORCE_QUIT);
+ }
+
+ std::string inner_data;
+ if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) {
+
+ // Check if we need to approve also last unapproved device
+ if (resp.data.find("name=\"name_action_selected\"") == std::string::npos) {
+ // 1) Continue
+ inner_data = "submit[Continue]=Continue";
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+ resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
+
+ // 2) Approve last unknown login
+ // inner_data = "submit[I%20don't%20recognize]=I%20don't%20recognize"; // Don't recognize - this will force to change account password
+ inner_data = "submit[This%20is%20Okay]=This%20is%20Okay"; // Recognize
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+ resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
+
+ // 3) Save last device
+ inner_data = "submit[Continue]=Continue";
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+ inner_data += "&name_action_selected=save_device"; // Save device - or "dont_save"
+ resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
+ }
+
+ // Save this actual device
+ inner_data = "submit[Continue]=Continue";
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+ inner_data += "&name_action_selected=save_device"; // Save device - or "dont_save"
+ resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
+ }
+
+ // Save actual machine name
+ // inner_data = "machine_name=Miranda%20NG&submit[Don't%20Save]=Don't%20Save"; // Don't save
+ inner_data = "machine_name=Miranda%20NG&submit[Save%20Device]=Save%20Device"; // Save
+ inner_data += "&lsd=" + utils::text::source_get_value(&resp.data, 3, "name=\"lsd\"", "value=\"", "\"");
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"");
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+
+ resp = flap(REQUEST_SETUP_MACHINE, &inner_data);
+ }
+ }
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ {
+ // When is error only because timeout, try login once more
+ if (handle_error("login"))
+ return login(username, password);
+ else
+ return handle_error("login", FORCE_QUIT);
+ }
+
+ case HTTP_CODE_OK: // OK page returned, but that is regular login page we don't want in fact
+ {
+ // Check whether captcha code is required
+ if (resp.data.find("id=\"captcha\"") != std::string::npos)
+ {
+ client_notify(TranslateT("Login error: Captcha code is required. Bad login credentials?"));
+ parent->debugLogA(" ! ! Login error: Captcha code is required.");
+ return handle_error("login", FORCE_QUIT);
+ }
+
+ // Get and notify error message
+ loginError(parent, utils::text::source_get_value(&resp.data, 4, "login_error_box", "<div", ">", "</div>"));
+ }
+ case HTTP_CODE_FORBIDDEN: // Forbidden
+ case HTTP_CODE_NOT_FOUND: // Not Found
+ default:
+ return handle_error("login", FORCE_QUIT);
+
+ case HTTP_CODE_FOUND: // Found and redirected to Home, Logged in, everything is OK
+ {
+ if (cookies.find("c_user") != cookies.end()) {
+ this->self_.user_id = cookies.find("c_user")->second;
+ parent->setString(FACEBOOK_KEY_ID, this->self_.user_id.c_str());
+ parent->debugLogA(" Got self user id: %s", this->self_.user_id.c_str());
+ return handle_success("login");
+ } else {
+ client_notify(TranslateT("Login error, probably bad login credentials."));
+ parent->debugLogA(" ! ! Login error, probably bad login credentials.");
+ return handle_error("login", FORCE_QUIT);
+ }
+ }
+ }
+}
+
+bool facebook_client::logout()
+{
+ if (parent->getByte(FACEBOOK_KEY_DISABLE_LOGOUT, 0))
+ return true;
+
+ handle_entry("logout");
+
+ std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&ref=mb&h=" + this->logout_hash_;
+
+ http::response resp = flap(REQUEST_LOGOUT, &data);
+
+ if (hFcbCon)
+ Netlib_CloseHandle(hFcbCon);
+ hFcbCon = NULL;
+
+ // Process result
+ username_ = password_ = self_.user_id = "";
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ case HTTP_CODE_FOUND:
+ return handle_success("logout");
+
+ default:
+ return false; // Logout not finished properly, but..okay, who cares :P
+ }
+}
+
+bool facebook_client::home()
+{
+ handle_entry("home");
+
+ // get fb_dtsg
+ http::response resp = flap(REQUEST_DTSG);
+
+ // Check whether HTTPS connection is required and we don't have it enabled
+ if (!this->https_ && resp.headers["Location"].find("https://") != std::string::npos) {
+ client_notify(TranslateT("Your account requires HTTPS connection. Activating."));
+ parent->setByte(FACEBOOK_KEY_FORCE_HTTPS, 1);
+ this->https_ = true;
+ return home();
+ }
+
+ this->dtsg_ = utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"");
+ parent->debugLogA(" Got self dtsg: %s", this->dtsg_.c_str());
+
+ if (this->dtsg_.empty())
+ return false;
+
+ resp = flap(REQUEST_HOME);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ {
+ // Get real name
+ this->self_.real_name = utils::text::source_get_value(&resp.data, 2, "<strong class=\"profileName\">", "</strong>");
+
+ // Try to get name again, if we've got some some weird version of Facebook
+ if (this->self_.real_name.empty())
+ this->self_.real_name = utils::text::source_get_value(&resp.data, 4, "id=\"root", "<strong", ">", "</strong>");
+
+ // Get and strip optional nickname
+ std::string::size_type pos = this->self_.real_name.find("<span class=\"alternate_name\">");
+ if (pos != std::string::npos) {
+ this->self_.nick = utils::text::source_get_value(&this->self_.real_name, 2, "<span class=\"alternate_name\">(", ")</span>");
+ parent->debugLogA(" Got self nick name: %s", this->self_.nick.c_str());
+
+ this->self_.real_name = this->self_.real_name.substr(0, pos - 1);
+ }
+ parent->debugLogA(" Got self real name: %s", this->self_.real_name.c_str());
+
+ if (this->self_.real_name.empty()) {
+ client_notify(TranslateT("Something happened to Facebook. Maybe there was some major update so you should wait for an update."));
+ return handle_error("home", FORCE_QUIT);
+ }
+
+ // Save name and nickname
+ parent->SaveName(NULL, &this->self_);
+
+ // Get avatar
+ this->self_.image_url = utils::text::source_get_value(&resp.data, 3, "class=\"l\"", "<img src=\"", "\"");
+ parent->debugLogA(" Got self avatar: %s", this->self_.image_url.c_str());
+ parent->CheckAvatarChange(NULL, this->self_.image_url);
+
+ // Get logout hash
+ this->logout_hash_ = utils::text::source_get_value2(&resp.data, "/logout.php?h=", "&\"");
+ parent->debugLogA(" Got self logout hash: %s", this->logout_hash_.c_str());
+
+ return handle_success("home");
+ }
+ case HTTP_CODE_FOUND:
+ // Work-around for replica_down, f**king hell what's that?
+ parent->debugLogA(" REPLICA_DOWN is back in force!");
+ return this->home();
+
+ default:
+ return handle_error("home", FORCE_QUIT);
+ }
+}
+
+bool facebook_client::chat_state(bool online)
+{
+ handle_entry("chat_state");
+
+ std::string data = (online ? "visibility=1" : "visibility=0");
+ data += "&window_id=0";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+ data += "&phstamp=0&__user=" + self_.user_id;
+ http::response resp = flap(REQUEST_VISIBILITY, &data);
+
+ return handle_success("chat_state");
+}
+
+bool facebook_client::reconnect()
+{
+ handle_entry("reconnect");
+
+ // Request reconnect
+ http::response resp = flap(REQUEST_RECONNECT);
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ {
+ this->chat_channel_ = utils::text::source_get_value(&resp.data, 2, "\"user_channel\":\"", "\"");
+ parent->debugLogA(" Got self channel: %s", this->chat_channel_.c_str());
+
+ this->chat_channel_partition_ = utils::text::source_get_value2(&resp.data, "\"partition\":", ",}");
+ parent->debugLogA(" Got self channel partition: %s", this->chat_channel_partition_.c_str());
+
+ this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"host\":\"", "\"");
+ parent->debugLogA(" Got self channel host: %s", this->chat_channel_host_.c_str());
+
+ this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
+ parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+
+ this->chat_conn_num_ = utils::text::source_get_value2(&resp.data, "\"max_conn\":", ",}");
+ parent->debugLogA(" Got self max_conn: %s", this->chat_conn_num_.c_str());
+
+ return handle_success("reconnect");
+ }
+
+ default:
+ return handle_error("reconnect", FORCE_DISCONNECT);
+ }
+}
+
+bool facebook_client::buddy_list()
+{
+ handle_entry("buddy_list");
+
+ // Prepare update data
+ std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + this->dtsg_ + "&__user=" + this->self_.user_id;
+
+ {
+ ScopedLock s(buddies_lock_);
+
+ data += "&cached_user_info_ids=";
+ int counter = 0;
+ for (List::Item< facebook_user >* i = buddies.begin(); i != NULL; i = i->next, counter++)
+ {
+ data += i->data->user_id + "%2C";
+ }
+ }
+
+ // Get buddy list
+ http::response resp = flap(REQUEST_BUDDY_LIST, &data);
+
+ switch (resp.code) {
+ case HTTP_CODE_OK:
+ parent->ForkThread(&FacebookProto::ProcessBuddyList, new std::string(resp.data));
+ return handle_success("buddy_list");
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error("buddy_list");
+ }
+}
+
+bool facebook_client::load_friends()
+{
+ handle_entry("load_friends");
+
+ // Get buddy list
+ http::response resp = flap(REQUEST_LOAD_FRIENDS);
+
+ switch (resp.code) {
+ case HTTP_CODE_OK:
+ parent->ForkThread(&FacebookProto::ProcessFriendList, new std::string(resp.data));
+ return handle_success("load_friends");
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error("load_friends");
+ }
+}
+
+bool facebook_client::feeds()
+{
+ handle_entry("feeds");
+
+ // Get feeds
+ http::response resp = flap(REQUEST_FEEDS);
+
+ switch (resp.code) {
+ case HTTP_CODE_OK:
+ if (resp.data.find("\"num_stories\":0") == std::string::npos)
+ parent->ForkThread(&FacebookProto::ProcessFeeds, new std::string(resp.data));
+
+ return handle_success("feeds");
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error("feeds");
+ }
+}
+
+bool facebook_client::load_pages()
+{
+ if (!parent->getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
+ return true;
+
+ handle_entry("load_pages");
+
+ // Get feeds
+ http::response resp = flap(REQUEST_PAGES);
+
+ switch (resp.code) {
+ case HTTP_CODE_OK:
+ {
+ 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;
+
+ parent->debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
+ pages[id] = title;
+ }
+
+ return handle_success("load_pages");
+ }
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error("load_pages");
+ }
+}
+
+bool facebook_client::channel()
+{
+ handle_entry("channel");
+
+ // Get update
+ http::response resp = flap(REQUEST_MESSAGES_RECEIVE);
+
+ if (resp.data.empty()) {
+ // Something went wrong
+ return handle_error("channel");
+ }
+
+ std::string type = utils::text::source_get_value(&resp.data, 2, "\"t\":\"", "\"");
+ if (type == "continue" || type == "heartbeat") {
+ // Everything is OK, no new message received
+ }
+ else if (type == "lb") {
+ // Some new stuff (idk how does it work yet)
+ this->chat_channel_host_ = utils::text::source_get_value(&resp.data, 2, "\"vip\":\"", "\"");
+ parent->debugLogA(" Got self channel host: %s", this->chat_channel_host_.c_str());
+
+ this->chat_sticky_num_ = utils::text::source_get_value2(&resp.data, "\"sticky\":\"", "\"");
+ parent->debugLogA(" Got self sticky number: %s", this->chat_sticky_num_.c_str());
+ }
+ else if (type == "fullReload" || type == "refresh") {
+ // Something went wrong (server flooding?)
+ parent->debugLogA("! ! ! Requested %s", type.c_str());
+
+ this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
+ parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+
+ this->chat_reconnect_reason_ = utils::text::source_get_value2(&resp.data, "\"reason\":", ",}");
+ parent->debugLogA(" Reconnect reason: %s", this->chat_reconnect_reason_.c_str());
+
+ if (type == "refresh")
+ return this->reconnect();
+ }
+ else {
+ // Something has been received, throw to new thread to process
+ std::string* response_data = new std::string(resp.data);
+ parent->ForkThread(&FacebookProto::ProcessMessages, response_data);
+
+ // Increment sequence number
+ this->chat_sequence_num_ = utils::text::source_get_value2(&resp.data, "\"seq\":", ",}");
+ parent->debugLogA(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+ }
+
+ // Return
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ return handle_success("channel");
+
+ case HTTP_CODE_GATEWAY_TIMEOUT:
+ // Maybe we have same clientid as other connected client, try to generate different one
+ this->chat_clientid_ = utils::text::rand_string(8, "0123456789abcdef");
+
+ // Intentionally fall to handle_error() below
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ case HTTP_CODE_FAKE_ERROR:
+ default:
+ return handle_error("channel");
+ }
+}
+
+bool facebook_client::send_message(std::string message_recipient, std::string message_text, std::string *error_text, MessageMethod method)
+{
+ ScopedLock s(send_message_lock_);
+
+ handle_entry("send_message");
+
+ http::response resp;
+
+ switch (method) {
+ case MESSAGE_INBOX:
+ {
+ parent->debugLogA(" > Sending message through INBOX");
+ std::string data = "action=send";
+ data += "&body=" + utils::url::encode(message_text);
+ data += "&recipients[0]=" + message_recipient;
+ data += "&__user=" + this->self_.user_id;
+ data += "&__a=1";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+ data += "&phstamp=0";
+
+ resp = flap(REQUEST_MESSAGE_SEND2, &data);
+ break;
+ }
+ case MESSAGE_MERCURY:
+ {
+ parent->debugLogA(" > Sending message through CHAT");
+ std::string data = "message_batch[0][action_type]=ma-type:user-generated-message";
+ data += "&message_batch[0][thread_id]";
+ data += "&message_batch[0][author]=fbid:" + this->self_.user_id;
+ data += "&message_batch[0][author_email]";
+ data += "&message_batch[0][coordinates]";
+ data += "&message_batch[0][timestamp]=" + utils::time::mili_timestamp();
+ data += "&message_batch[0][timestamp_absolute]";
+ data += "&message_batch[0][timestamp_relative]";
+ data += "&message_batch[0][is_unread]=false";
+ data += "&message_batch[0][is_cleared]=false";
+ data += "&message_batch[0][is_forward]=false";
+ data += "&message_batch[0][spoof_warning]=false";
+ data += "&message_batch[0][source]=source:chat:web";
+ data += "&message_batch[0][source_tags][0]=source:chat";
+ data += "&message_batch[0][body]=" + utils::url::encode(message_text);
+ data += "&message_batch[0][has_attachment]=false";
+ data += "&message_batch[0][html_body]=false";
+ data += "&message_batch[0][specific_to_list][0]=fbid:" + message_recipient;
+ data += "&message_batch[0][specific_to_list][1]=fbid:" + this->self_.user_id;
+ data += "&message_batch[0][status]=0";
+ data += "&message_batch[0][message_id]";
+ data += "&message_batch[0][client_thread_id]=user:" + message_recipient;
+ data += "&client=mercury";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+ data += "&__user=" + this->self_.user_id;
+ data += "&__a=1";
+ data += "&phstamp=0";
+
+ resp = flap(REQUEST_MESSAGE_SEND, &data);
+ break;
+ }
+ case MESSAGE_TID:
+ {
+ parent->debugLogA(" > Sending message through MERCURY (TID)");
+ std::string data = "message_batch[0][action_type]=ma-type:user-generated-message";
+ data += "&message_batch[0][thread_id]=" + message_recipient;
+ data += "&message_batch[0][author]=fbid:" + this->self_.user_id;
+ data += "&message_batch[0][timestamp]=" + utils::time::mili_timestamp();
+ data += "&message_batch[0][timestamp_absolute]=";
+ data += "&message_batch[0][timestamp_relative]=";
+ data += "&message_batch[0][is_unread]=false";
+ data += "&message_batch[0][is_cleared]=false";
+ data += "&message_batch[0][is_forward]=false";
+ data += "&message_batch[0][source]=source:chat:web";
+ data += "&message_batch[0][body]=" + utils::url::encode(message_text);
+ data += "&message_batch[0][has_attachment]=false";
+ data += "&message_batch[0][is_html]=false";
+ data += "&message_batch[0][message_id]=";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+ data += "&__user=" + this->self_.user_id;
+ data += "&phstamp=0";
+
+ resp = flap(REQUEST_MESSAGE_SEND, &data);
+ break;
+ }
+ case MESSAGE_ASYNC:
+ {
+ parent->debugLogA(" > Sending message through ASYNC");
+ std::string data = "action=send";
+ data += "&body=" + utils::url::encode(message_text);
+ data += "&recipients[0]=" + message_recipient;
+ data += "&lsd=";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+
+ resp = flap(REQUEST_ASYNC, &data);
+ break;
+ }
+ }
+
+ *error_text = resp.error_text;
+
+ switch (resp.error_number)
+ {
+ case 0: // Everything is OK
+ {
+ // Remember this message id
+ std::string mid = utils::text::source_get_value(&resp.data, 2, "\"message_id\":\"", "\"");
+ messages_ignore.insert(std::make_pair(mid, false));
+ } break;
+
+ //case 1356002: // You are offline - wtf??
+
+ case 1356003: // Contact is offline
+ {
+ MCONTACT hContact = parent->ContactIDToHContact(message_recipient);
+ if (hContact != NULL)
+ parent->setWord(hContact, "Status", ID_STATUS_OFFLINE);
+ return false;
+ } break;
+
+ case 1356026: // Contact has alternative client
+ {
+ client_notify(TranslateT("Need confirmation for sending messages to other clients.\nOpen Facebook website and try to send message to this contact again!"));
+ return false;
+ } break;
+
+ default: // Other error
+ parent->debugLogA(" !!! Send message error #%d: %s", resp.error_number, resp.error_text);
+ return false;
+ }
+
+ switch (resp.code)
+ {
+ case HTTP_CODE_OK:
+ return handle_success("send_message");
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ *error_text = Translate("Timeout when sending message.");
+
+ handle_error("send_message");
+ return false;
+ }
+}
+
+bool facebook_client::post_status(status_data *status)
+{
+ if (status == NULL || (status->text.empty() && status->url.empty()))
+ return false;
+
+ handle_entry("post_status");
+
+ if (status->isPage) {
+ std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&user_id=" + status->user_id;
+ data += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE);
+ flap(REQUEST_IDENTITY_SWITCH, &data);
+ }
+
+ std::string data;
+ RequestType request = REQUEST_POST_STATUS;
+
+ if (!status->url.empty()) {
+ data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&composerid=u_jsonp_2_b";
+ data += "&targetid=" + (status->user_id.empty() ? this->self_.user_id : status->user_id);
+ data += "&istimeline=1&composercontext=composer&onecolumn=1&nctr[_mod]=pagelet_timeline_recent&__a=1&ttstamp=0";
+ data += "&__user=" + (status->isPage && !status->user_id.empty() ? status->user_id : this->self_.user_id);
+ data += "&loaded_components[0]=maininput&loaded_components[1]=backdateicon&loaded_components[2]=withtaggericon&loaded_components[3]=cameraicon&loaded_components[4]=placetaggericon&loaded_components[5]=mainprivacywidget&loaded_components[6]=withtaggericon&loaded_components[7]=backdateicon&loaded_components[8]=placetaggericon&loaded_components[9]=cameraicon&loaded_components[10]=mainprivacywidget&loaded_components[11]=maininput&loaded_components[12]=explicitplaceinput&loaded_components[13]=hiddenplaceinput&loaded_components[14]=placenameinput&loaded_components[15]=hiddensessionid&loaded_components[16]=withtagger&loaded_components[17]=backdatepicker&loaded_components[18]=placetagger&loaded_components[19]=citysharericon";
+ http::response resp = flap(REQUEST_LINK_SCRAPER, &data, &status->url);
+ resp.data = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(resp.data));
+
+ data = "&xhpc_context=profile&xhpc_ismeta=1&xhpc_timeline=1&xhpc_composerid=u_jsonp_2_0&is_explicit_place=&composertags_place=&composer_session_id=&composertags_city=&disable_location_sharing=false&composer_predicted_city=&nctr[_mod]=pagelet_composer&__a=1&__dyn=&__req=1f&ttstamp=0";
+ std::string form = utils::text::source_get_value(&resp.data, 2, "<form", "</form>");
+ utils::text::replace_all(&form, "\\\"", "\"");
+ data += "&" + utils::text::source_get_form_data(&form) + "&";
+ //data += "&no_picture=0";
+
+ request = REQUEST_STATUS_COMPOSER;
+ }
+
+ std::string text = utils::url::encode(status->text);
+
+ data += "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&xhpc_targetid=" + (status->user_id.empty() ? this->self_.user_id : status->user_id);
+ data += "&__user=" + (status->isPage && !status->user_id.empty() ? status->user_id : this->self_.user_id);
+ data += "&xhpc_message=" + text;
+ data += "&xhpc_message_text=" + text;
+ if (!status->isPage)
+ data += "&audience[0][value]=" + get_privacy_type();
+ if (!status->place.empty()) {
+ data += "&composertags_place_name=";
+ data += utils::url::encode(status->place);
+ }
+ for(std::vector<facebook_user*>::size_type i = 0; i < status->users.size(); i++) {
+ data += "&composertags_with[" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
+ data += "]=" + status->users[i]->user_id;
+ data += "&text_composertags_with[" + utils::conversion::to_string(&i, UTILS_CONV_UNSIGNED_NUMBER);
+ data += "]=" + status->users[i]->real_name;
+ delete status->users[i];
+ }
+ status->users.clear();
+
+ data += "&xhpc_context=profile&xhpc_ismeta=1&xhpc_timeline=1&xhpc_composerid=u_0_2y&is_explicit_place=&composertags_place=&composertags_city=";
+
+ http::response resp = flap(request, &data);
+
+ if (status->isPage) {
+ std::string data = "fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&user_id=" + this->self_.user_id;
+ data += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE);
+ flap(REQUEST_IDENTITY_SWITCH, &data);
+ }
+
+ if (resp.isValid()) {
+ parent->NotifyEvent(parent->m_tszUserName, TranslateT("Status update was successful."), NULL, FACEBOOK_EVENT_OTHER);
+ return handle_success("post_status");
+ }
+
+ return handle_error("post_status");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool facebook_client::save_url(const std::string &url,const std::tstring &filename, HANDLE &nlc)
+{
+ NETLIBHTTPREQUEST req = {sizeof(req)};
+ NETLIBHTTPREQUEST *resp;
+ req.requestType = REQUEST_GET;
+ req.szUrl = const_cast<char*>(url.c_str());
+ req.flags = NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_PERSISTENT | NLHRF_NODUMP;
+ req.nlc = nlc;
+
+ resp = reinterpret_cast<NETLIBHTTPREQUEST*>(CallService(MS_NETLIB_HTTPTRANSACTION,
+ reinterpret_cast<WPARAM>(this->parent->m_hNetlibUser), reinterpret_cast<LPARAM>(&req)));
+
+ bool ret = false;
+
+ if (resp) {
+ nlc = resp->nlc;
+ parent->debugLogA("@@@@@ Saving avatar URL %s to path %s", url.c_str(), _T2A(filename.c_str()));
+
+ // Create folder if necessary
+ std::tstring dir = filename.substr(0,filename.rfind('\\'));
+ if (_taccess(dir.c_str(), 0))
+ CreateDirectoryTreeT(dir.c_str());
+
+ // Write to file
+ FILE *f = _tfopen(filename.c_str(), _T("wb"));
+ if (f != NULL) {
+ fwrite(resp->pData,1,resp->dataLength,f);
+ fclose(f);
+
+ ret = _taccess(filename.c_str(), 0) == 0;
+ }
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ } else {
+ nlc = NULL;
+ }
+
+ return ret;
+}