diff options
Diffstat (limited to 'protocols/FacebookRM/src/communication.cpp')
-rw-r--r-- | protocols/FacebookRM/src/communication.cpp | 712 |
1 files changed, 117 insertions, 595 deletions
diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp index a7002d41e1..452a6fe24a 100644 --- a/protocols/FacebookRM/src/communication.cpp +++ b/protocols/FacebookRM/src/communication.cpp @@ -27,7 +27,7 @@ void facebook_client::client_notify(wchar_t* message) parent->NotifyEvent(parent->m_tszUserName, message, NULL, EVENT_CLIENT); } -http::response facebook_client::flap(RequestType request_type, std::string *post_data, std::string *get_data) +http::response facebook_client::sendRequest(HttpRequest *request) { http::response resp; @@ -36,80 +36,56 @@ http::response facebook_client::flap(RequestType request_type, std::string *post return resp; } - // Prepare the request - NETLIBHTTPREQUEST nlhr = { sizeof(NETLIBHTTPREQUEST) }; - - std::string server = choose_server(request_type); - - // Set request URL - std::string url = HTTP_PROTO_SECURE + server + choose_action(request_type, get_data); if (!parent->m_locale.empty()) - url += "&locale=" + parent->m_locale; - - nlhr.szUrl = (char*)url.c_str(); - - // Set timeout (bigger for channel request) - switch (request_type) { - case REQUEST_MESSAGES_RECEIVE: - nlhr.timeout = 1000 * 65; - break; - - default: - nlhr.timeout = 1000 * 20; - break; - } - - // Set request type (GET/POST) and eventually also POST data - if (post_data != NULL) { - nlhr.requestType = REQUEST_POST; - nlhr.pData = (char*)(*post_data).c_str(); - nlhr.dataLength = (int)post_data->length(); - } else { - nlhr.requestType = REQUEST_GET; - } - - // Set headers - it depends on requestType so it must be after setting that - nlhr.headers = get_request_headers(nlhr.requestType, &nlhr.headersCount); + request->Url << CHAR_VALUE("locale", parent->m_locale.c_str()); - // Set flags - nlhr.flags = NLHRF_HTTP11 | NLHRF_SSL; + request->Headers + << CHAR_VALUE("Accept-Language", "en,en-US;q=0.9") + << CHAR_VALUE("Accept", "*/*") + << CHAR_VALUE("User-Agent", g_strUserAgent.c_str()) + << CHAR_VALUE("Cookie", ptrA(load_cookies())); // FIXME: Rework load_cookies to not do strdup - if (server == FACEBOOK_SERVER_MBASIC || server == FACEBOOK_SERVER_MOBILE) { - nlhr.flags |= NLHRF_REDIRECT; + if (request->requestType == REQUEST_POST) { + request->Headers + << CHAR_VALUE("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); } + // TODO: rather change http_request than doing this ifdef magic here? #ifdef _DEBUG - nlhr.flags |= NLHRF_DUMPASTEXT; + request->flags &= ~NLHRF_NODUMP; + request->flags |= NLHRF_DUMPASTEXT; #else - nlhr.flags |= NLHRF_NODUMP; + request->flags &= ~NLHRF_DUMPASTEXT; + request->flags |= NLHRF_NODUMP; #endif + // FIXME: Support persistent connection for various requests + /* // Set persistent connection (or not) switch (request_type) { case REQUEST_LOGIN: - nlhr.nlc = NULL; + request->nlc = NULL; break; case REQUEST_MESSAGES_RECEIVE: - nlhr.nlc = hMsgCon; - nlhr.flags |= NLHRF_PERSISTENT; + request->nlc = hMsgCon; + request->flags |= NLHRF_PERSISTENT; break; default: WaitForSingleObject(fcb_conn_lock_, INFINITE); - nlhr.nlc = hFcbCon; - nlhr.flags |= NLHRF_PERSISTENT; + request->nlc = hFcbCon; + request->flags |= NLHRF_PERSISTENT; break; } - - parent->debugLogA("@@@ Sending request to '%s'", nlhr.szUrl); + */ + parent->debugLogA("@@@ Sending request to '%s'", request->szUrl); // Send the request - NETLIBHTTPREQUEST *pnlhr = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)handle_, (LPARAM)&nlhr); - - mir_free(nlhr.headers[3].szValue); - mir_free(nlhr.headers); + NETLIBHTTPREQUEST *pnlhr = request->Send(handle_); + // FIXME: Support persistent connection for various requests + /* // Remember the persistent connection handle (or not) switch (request_type) { case REQUEST_LOGIN: @@ -125,6 +101,7 @@ http::response facebook_client::flap(RequestType request_type, std::string *post hFcbCon = pnlhr ? pnlhr->nlc : NULL; break; } + */ // Check and copy response data if (pnlhr != NULL) { @@ -134,12 +111,16 @@ http::response facebook_client::flap(RequestType request_type, std::string *post resp.data = pnlhr->pData ? pnlhr->pData : ""; CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)pnlhr); - } else { + } + 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 } + // Delete the request object + delete request; + // Get Facebook's error message if (resp.code == HTTP_CODE_OK) { std::string::size_type pos = resp.data.find("\"error\":"); @@ -180,7 +161,7 @@ http::response facebook_client::flap(RequestType request_type, std::string *post resp.code = HTTP_CODE_FAKE_ERROR; parent->debugLogA("!!! Received Facebook error: %d -- %s", error_num, error.c_str()); - if (notify_errors(request_type) && !silent) + if (request->NotifyErrors && !silent) client_notify(_A2T(error.c_str())); } } @@ -223,359 +204,6 @@ bool facebook_client::handle_error(const std::string &method, int action) ////////////////////////////////////////////////////////////////////////////// -std::string facebook_client::choose_server(RequestType request_type) -{ - switch (request_type) - { - case REQUEST_LOGIN: - return FACEBOOK_SERVER_LOGIN; - - case REQUEST_MESSAGES_RECEIVE: - case REQUEST_ACTIVE_PING: - { - 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: - return FACEBOOK_SERVER_MOBILE; - - case REQUEST_LOAD_FRIENDSHIPS: - case REQUEST_SEARCH: - case REQUEST_USER_INFO_MOBILE: - case REQUEST_PROFILE_PICTURE: - return this->mbasicWorks ? FACEBOOK_SERVER_MBASIC : FACEBOOK_SERVER_MOBILE; - - // case REQUEST_LOGOUT: - // case REQUEST_USER_INFO: - // case REQUEST_USER_INFO_ALL: - // case REQUEST_FEEDS: - // case REQUEST_PAGES: - // case REQUEST_NOTIFICATIONS: - // case REQUEST_RECONNECT: - // case REQUEST_POST_STATUS: - // case REQUEST_IDENTITY_SWITCH: - // case REQUEST_CAPTCHA_REFRESH: - // case REQUEST_LINK_SCRAPER: - // case REQUEST_MESSAGES_SEND: - // case REQUEST_THREAD_INFO: - // case REQUEST_THREAD_SYNC: - // case REQUEST_VISIBILITY: - // case REQUEST_POKE: - // case REQUEST_MARK_READ: - // case REQUEST_NOTIFICATIONS_READ: - // case REQUEST_TYPING_SEND: - // case REQUEST_SETUP_MACHINE: - // case REQUEST_DELETE_FRIEND: - // case REQUEST_ADD_FRIEND: - // case REQUEST_CANCEL_FRIENDSHIP: - // case REQUEST_FRIENDSHIP: - // case REQUEST_UNREAD_THREADS: - // case REQUEST_ON_THIS_DAY: - // case REQUEST_LOGIN_SMS: - default: - return FACEBOOK_SERVER_REGULAR; - } -} - -std::string facebook_client::choose_action(RequestType request_type, std::string *get_data) -{ - // NOTE: Parameter "client" used in some requests's POST data could be "jewel" (top bar?), "mercury" (chat window, source=source:chat:web) or "web_messenger" (whole chat messages page, source=source:titan:web) - - switch (request_type) - { - case REQUEST_LOGIN: - { - std::string action = "/login.php?login_attempt=1"; - if (get_data != NULL) { - action += *get_data; - } - return action; - } - - 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_USER_INFO: // ok, 17.8.2016 - return "/chat/user_info/?dpr=1"; - - case REQUEST_USER_INFO_ALL: // ok, 17.8.2016 - return "/chat/user_info_all/?dpr=1&viewer=" + self_.user_id; - - case REQUEST_USER_INFO_MOBILE: - { - std::string action = "/%sv=info"; - if (get_data != NULL) { - utils::text::replace_all(&action, "%s", *get_data); - } - return action; - } - - case REQUEST_LOAD_FRIENDSHIPS: - { - return "/friends/center/requests/?"; - } - - case REQUEST_SEARCH: - { - std::string action = "/search/?search=people&query="; - if (get_data != NULL) { - action += *get_data; - } - return action; - } - - case REQUEST_UNREAD_THREADS: // ok, 17.8.2016 - { - return "/ajax/mercury/unread_threads.php?dpr=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_ADD_FRIEND: - { - return "/ajax/add_friend/action.php?__a=1"; - } - - case REQUEST_CANCEL_FRIENDSHIP: - { - return "/ajax/friends/requests/cancel.php?__a=1"; - } - - case REQUEST_FRIENDSHIP: - { - return "/requests/friends/ajax/?__a=1"; - } - - case REQUEST_FEEDS: - { - std::string action = "/ajax/home/generic.php?" + get_newsfeed_type(); - action += "&__user=" + self_.user_id + "&__a=1"; - - /*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: // ok, 17.8.2016 - { - return "/ajax/notifications/client/get.php?dpr=1"; - } - - case REQUEST_RECONNECT: // ok, 17.8.2016 - { - 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); - - action += "&__dyn=" + __dyn(); - action += "&__req=" + __req(); - action += "&__rev=" + __rev(); - action += "&__pc=PHASED:DEFAULT&__be=-1&__a=1"; - - return action; - } - - case REQUEST_POST_STATUS: - return "/ajax/updatestatus.php?__a=1"; - - case REQUEST_IDENTITY_SWITCH: - return "/identity_switch.php?__a=1"; - - case REQUEST_CAPTCHA_REFRESH: - { - std::string action = "/captcha/refresh_ajax.php?__a=1"; - if (get_data != NULL) { - action += "&" + (*get_data); - } - return action; - } - - 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_MESSAGES_SEND: - return "/messaging/send/?dpr=1"; - - case REQUEST_THREAD_INFO: // ok, 17.8.2016 - return "/ajax/mercury/thread_info.php?dpr=1"; - - case REQUEST_THREAD_SYNC: // TODO: This doesn't work anymore - return "/ajax/mercury/thread_sync.php?__a=1"; - - case REQUEST_MESSAGES_RECEIVE: - case REQUEST_ACTIVE_PING: - { - bool isPing = (request_type == REQUEST_ACTIVE_PING); - - std::string action = (isPing ? "/active_ping" : "/pull"); - action += "?channel=" + (this->chat_channel_.empty() ? "p_" + self_.user_id : this->chat_channel_); - if (!isPing) - 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", &this->random_); - - /* - original cb = return (1048576 * Math.random() | 0).toString(36); - char buffer[10]; - itoa(((int)(1048576 * (((double)rand()) / (RAND_MAX + 1))) | 0), buffer, 36); - action += "&cb=" + buffer; - */ - - int idleSeconds = parent->IdleSeconds(); - if (idleSeconds > 0 && !parent->isInvisible()) - action += "&idle=" + utils::conversion::to_string(&idleSeconds, UTILS_CONV_UNSIGNED_NUMBER); - - if (!isPing) { - action += "&qp=y"; // TODO: what's this item? - action += "&pws=fresh"; // TODO: what's this item? - action += "&isq=487632"; // TODO: what's this item? - action += "&msgs_recv=" + utils::conversion::to_string(&this->chat_msgs_recv_, UTILS_CONV_UNSIGNED_NUMBER); - // TODO: sometimes there is &tur=1711 and &qpmade=<some actual timestamp> and &isq=487632 - // action += "&request_batch=1"; // it somehow batches up more responses to one - then response has special "t=batched" type and "batches" array with the data - // action += "&msgr_region=LLA"; // it was here only for first pull, same as request_batch - } - - action += "&cap=8"; // TODO: what's this item? Sometimes it's 0, sometimes 8 - action += "&uid=" + self_.user_id; - action += "&viewer_uid=" + self_.user_id; - - if (!this->chat_sticky_num_.empty() && !this->chat_sticky_pool_.empty()) { - action += "&sticky_token=" + this->chat_sticky_num_; - action += "&sticky_pool=" + this->chat_sticky_pool_; - } - - if (!isPing && !this->chat_traceid_.empty()) - action += "&traceid=" + this->chat_traceid_; - - if (parent->isInvisible()) - action += "&state=offline"; - else if (isPing || idleSeconds < 60) - action += "&state=active"; - - return action; - } - - case REQUEST_VISIBILITY: - return "/ajax/chat/privacy/visibility.php?dpr=1"; // ok, 17.8.2016 - - case REQUEST_POKE: - return "/pokes/dialog/?__a=1"; - - 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?dpr=1"; // ok, 17.8.2016 - - case REQUEST_ON_THIS_DAY: - { - std::string action = "/onthisday/story/query/?__a=1"; - if (get_data != NULL) { - action += "&" + (*get_data); - } - return action; - } - - case REQUEST_LOGIN_SMS: - { - return "/ajax/login/approvals/send_sms?dpr=1"; - } - - case REQUEST_PROFILE_PICTURE: - { - return "/profile/picture/view/?profile_id=" + self_.user_id; - } - - default: - return "/?_fb_noscript=1"; - } -} - -bool facebook_client::notify_errors(RequestType request_type) -{ - switch (request_type) - { - case REQUEST_MESSAGES_SEND: - 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); @@ -803,12 +431,8 @@ bool facebook_client::login(const char *username, const char *password) username_ = username; password_ = password; - // Prepare login data - std::string data = "persistent=1"; - data += "&email=" + utils::url::encode(username); - data += "&pass=" + utils::url::encode(password); - - std::string get_data = ""; + std::string postData; + std::string getData; if (cookies.empty()) { // Set device ID @@ -817,7 +441,8 @@ bool facebook_client::login(const char *username, const char *password) cookies["datr"] = device; // Get initial cookies - http::response resp = flap(REQUEST_LOGIN); + LoginRequest *request = new LoginRequest(); + http::response resp = sendRequest(request); // Also parse cookies set by JavaScript (more variant exists in time, so check all known now) parseJsCookies("[\"DeferredCookie\",\"addToQueue\",[],[\"", resp.data, cookies); @@ -827,14 +452,13 @@ bool facebook_client::login(const char *username, const char *password) 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, true); - get_data += "&" + utils::text::source_get_value(&form, 2, "login.php?login_attempt=1&", "\""); + postData = utils::text::source_get_form_data(&form, true); + getData = utils::text::source_get_value(&form, 2, "login.php?login_attempt=1&", "\""); } - data += "&lgndim=eyJ3IjoxOTIwLCJoIjoxMDgwLCJhdyI6MTgzNCwiYWgiOjEwODAsImMiOjMyfQ=="; // means base64 encoded: {"w":1920,"h":1080,"aw":1834,"ah":1080,"c":32} - // Send validation - http::response resp = flap(REQUEST_LOGIN, &data, &get_data); + HttpRequest *request = new LoginRequest(username, password, getData.c_str(), postData.c_str()); + http::response resp = sendRequest(request); // Save Device ID if (!cookies["datr"].empty()) @@ -853,7 +477,8 @@ bool facebook_client::login(const char *username, const char *password) // Check whether login checks are required if (location.find("/checkpoint/") != std::string::npos) { - resp = flap(REQUEST_SETUP_MACHINE, NULL, NULL); + request = new SetupMachineRequest(); + resp = sendRequest(request); if (resp.data.find("login_approvals_no_phones") != std::string::npos) { // Code approval - but no phones in account @@ -862,29 +487,26 @@ bool facebook_client::login(const char *username, const char *password) } if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) { - std::string inner_data; - int attempt = 0; // Check if we need to put approval code (aka "two-factor auth") while (resp.data.find("id=\"approvals_code\"") != std::string::npos) { parent->debugLogA(" Login info: Approval code required."); - std::string fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")); + const char *fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")).c_str(); + const char *nh = utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"").c_str(); - CFacebookGuardDialog guardDialog(parent, fb_dtsg.c_str()); + CFacebookGuardDialog guardDialog(parent, fb_dtsg); if (guardDialog.DoModal() != DIALOG_RESULT_OK) { parent->SetStatus(ID_STATUS_OFFLINE); return false; } // We need verification code from user (he can get it via Facebook application on phone or by requesting code via SMS) - std::string givenCode = guardDialog.GetCode(); + const char *givenCode = guardDialog.GetCode(); - inner_data = "submit[Continue]=Continue"; - inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\""); - inner_data += "&fb_dtsg=" + fb_dtsg; - inner_data += "&approvals_code=" + givenCode; - resp = flap(REQUEST_SETUP_MACHINE, &inner_data); + request = new SetupMachineRequest(fb_dtsg, nh, "Continue"); + request->Body << CHAR_VALUE("approvals_code", givenCode); + resp = sendRequest(request); if (resp.data.find("id=\"approvals_code\"") != std::string::npos) { // We get no error message if we put wrong code. Facebook just shows same form again. @@ -898,7 +520,7 @@ bool facebook_client::login(const char *username, const char *password) } } else { - // After successfull verification is showed different page - classic form to save device (as handled at the bottom) + // After successful verification is showed different page - classic form to save device (as handled at the bottom) break; } } @@ -907,10 +529,11 @@ bool facebook_client::login(const char *username, const char *password) // 1) Continue (it might have been sent with approval code above already) if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) { - inner_data = "submit[Continue]=Continue"; - inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\""); - inner_data += "&fb_dtsg=" + utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")); - resp = flap(REQUEST_SETUP_MACHINE, &inner_data); + const char *fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")).c_str(); + const char *nh = utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"").c_str(); + + request = new SetupMachineRequest(fb_dtsg, nh, "Continue"); + resp = sendRequest(request); } // In this step might be needed identity confirmation @@ -933,36 +556,35 @@ bool facebook_client::login(const char *username, const char *password) tszMessage.AppendFormat(L"\n\n%s", ptrW(mir_utf8decodeW(activity.c_str()))); } - if (MessageBox(0, tszMessage, tszTitle, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON1) == IDYES) { - // Recognized activity, will continue with login - inner_data = "submit[This was me]=This was me"; - } else { - // Don't recognize - this will force to change account password - inner_data = "submit[This wasn't me]=This wasn't me"; - - // We won't continue with this and rather cancel connecting right away, because we don't want to handle password changing via Miranda + if (MessageBox(0, tszMessage, tszTitle, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON1) != IDYES) { + // We will cancel connecting right away, because we don't want to handle password changing via Miranda client_notify(TranslateT("Login error: You need to confirm last unknown login or revoke it from web browser.")); return handle_error("login", FORCE_QUIT); } - inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\""); - inner_data += "&fb_dtsg=" + utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")); - resp = flap(REQUEST_SETUP_MACHINE, &inner_data); + + const char *fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")).c_str(); + const char *nh = utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"").c_str(); + + request = new SetupMachineRequest(fb_dtsg, nh, "This was me"); // Recognize device (or "This wasn't me" - this will force to change account password) + resp = sendRequest(request); // 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::url::encode(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); + fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")).c_str(); + nh = utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"").c_str(); + + request = new SetupMachineRequest(fb_dtsg, nh, "Continue"); + request->Body << "&name_action_selected=save_device"; // Save device - or "dont_save" + resp = sendRequest(request); } // Save this actual device if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) { - inner_data = "submit[Continue]=Continue"; - inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\""); - inner_data += "&fb_dtsg=" + utils::url::encode(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); + const char *fb_dtsg = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")).c_str(); + const char *nh = utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"").c_str(); + + request = new SetupMachineRequest(fb_dtsg, nh, "Continue"); + request->Body << "&name_action_selected=save_device"; // Save device - or "dont_save" + resp = sendRequest(request); } } else if (resp.data.find("name=\"submit[Get Started]\"") != std::string::npos) { @@ -1056,10 +678,8 @@ bool facebook_client::logout() { handle_entry("logout"); - std::string data = "fb_dtsg=" + this->dtsg_; - data += "&ref=mb&h=" + this->logout_hash_; - - http::response resp = flap(REQUEST_LOGOUT, &data); + LogoutRequest *request = new LogoutRequest(this->dtsg_.c_str(), this->logout_hash_.c_str()); + http::response resp = sendRequest(request); this->username_.clear(); this->password_.clear(); @@ -1081,7 +701,7 @@ bool facebook_client::home() handle_entry("home"); // get fb_dtsg - http::response resp = flap(REQUEST_DTSG); + http::response resp = sendRequest(new DtsgRequest()); this->dtsg_ = utils::url::encode(utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"")); { @@ -1101,7 +721,7 @@ bool facebook_client::home() parent->debugLogA(" Got self dtsg"); } - resp = flap(REQUEST_HOME); + resp = sendRequest(new HomeRequest()); switch (resp.code) { @@ -1163,7 +783,8 @@ bool facebook_client::home() // Final attempt to get avatar as on some pages is only link to photo page and not link to image itself if (this->self_.image_url.empty()) { - http::response resp2 = flap(REQUEST_PROFILE_PICTURE); + HttpRequest *request = new ProfilePictureRequest(this->mbasicWorks, self_.user_id.c_str()); + http::response resp2 = sendRequest(request); // Get avatar (from mbasic version of photo page) this->self_.image_url = utils::text::html_entities_decode(utils::text::source_get_value(&resp2.data, 3, "id=\"root", "<img src=\"", "\"")); @@ -1209,16 +830,8 @@ 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_; - data += "&__user=" + self_.user_id; - data += "&__dyn=" + __dyn(); - data += "&__req=" + __req(); - data += "&ttstamp=" + ttstamp_; - data += "&__rev=" + __rev(); - data += "&__a=1&__pc=PHASED:DEFAULT&__be=-1"; - http::response resp = flap(REQUEST_VISIBILITY, &data); // NOTE: Request revised 17.8.2016 + HttpRequest *request = new SetVisibilityRequest(this, online); + http::response resp = sendRequest(request); if (!resp.error_title.empty()) return handle_error("chat_state"); @@ -1231,7 +844,7 @@ bool facebook_client::reconnect() handle_entry("reconnect"); // Request reconnect - http::response resp = flap(REQUEST_RECONNECT); + http::response resp = sendRequest(new ReconnectRequest(this)); switch (resp.code) { @@ -1276,8 +889,9 @@ bool facebook_client::channel() { handle_entry("channel"); - // Get update - http::response resp = flap(REQUEST_MESSAGES_RECEIVE); + // Get updates + ChannelRequest *request = new ChannelRequest(this, ChannelRequest::PULL); + http::response resp = sendRequest(request); if (resp.data.empty()) { // Something went wrong @@ -1385,7 +999,8 @@ bool facebook_client::activity_ping() handle_entry("activity_ping"); - http::response resp = flap(REQUEST_ACTIVE_PING); + ChannelRequest *request = new ChannelRequest(this, ChannelRequest::PING); + http::response resp = sendRequest(request); // Remember this last ping time parent->m_pingTS = ::time(NULL); @@ -1402,16 +1017,7 @@ int facebook_client::send_message(int seqid, MCONTACT hContact, const std::strin { handle_entry("send_message"); - http::response resp; - std::string data; - - if (!captcha.empty()) { - data += "&captcha_persist_data=" + captcha_persist_data; - data += "&recaptcha_challenge_field="; - data += "&captcha_response=" + captcha; - } - - boolean isChatRoom = parent->isChatRoom(hContact); + bool isChatRoom = parent->isChatRoom(hContact); ptrA userId( parent->getStringA(hContact, FACEBOOK_KEY_ID)); ptrA threadId( parent->getStringA(hContact, FACEBOOK_KEY_TID)); @@ -1426,59 +1032,15 @@ int facebook_client::send_message(int seqid, MCONTACT hContact, const std::strin return SEND_MESSAGE_ERROR; } - data += "&client=mercury"; // or "web_messenger" (whole messages page) - data += "&action_type=ma-type:user-generated-message"; - - // Experimental sticker sending support - if (message_text.substr(0, 10) == "[[sticker:" && message_text.substr(message_text.length() - 2) == "]]") { - data += "&body="; - data += "&sticker_id=" + utils::url::encode(message_text.substr(10, message_text.length() - 10 - 2)); - data += "&has_attachment=true"; - // TODO: For sending GIF images instead of "sticker_id=" there is "image_ids[0]=", otherwise it's same - } - else { - data += "&body=" + utils::url::encode(message_text); - data += "&has_attachment=false"; - } - - data += "&ephemeral_ttl_mode=0"; - // data += "&force_sms=true" // TODO: This is present always when sending via "web_messenger" - // Probably we can generate any random messageID, it just have to be numeric and don't start with "0". We will receive it in response as "client_message_id". std::string messageId = utils::text::rand_string(10, "123456789", &this->random_); - data += "&message_id=" + messageId; - data += "&offline_threading_id=" + messageId; // Same as message ID - - if (isChatRoom) { - // NOTE: Remove "id." prefix as here we need to give threadFbId and not threadId - std::string thread_fbid = threadId; - if (thread_fbid.substr(0, 3) == "id.") - thread_fbid = thread_fbid.substr(3); - - data += "&thread_fbid=" + thread_fbid; - } else { - data += "&other_user_fbid=" + std::string(userId); - data += "&specific_to_list[0]=fbid:" + std::string(userId); - data += "&specific_to_list[1]=fbid:" + this->self_.user_id; - } - data += "&signature_id="; // TODO: How to generate signature ID? It is present only when sending via "mercury" - data += "&source=source:chat:web"; // or "source:titan:web" for web_messenger - data += "×tamp=" + utils::time::mili_timestamp(); - data += "&ui_push_phase=V3"; - data += "&__user=" + this->self_.user_id; - data += "&__a=1"; - data += "&__dyn=" + __dyn(); - data += "&__req=" + __req(); - data += "&__be=-1"; - data += "&__pc=PHASED:DEFAULT"; - data += "&fb_dtsg=" + this->dtsg_; - data += "&ttstamp=" + ttstamp_; - data += "&__rev=" + __rev(); + http::response resp; { - ScopedLock s(send_message_lock_); - resp = flap(REQUEST_MESSAGES_SEND, &data); // NOTE: Request revised 17.8.2016 + HttpRequest *request = new SendMessageRequest(this, userId, threadId, messageId.c_str(), message_text.c_str(), isChatRoom, captcha.c_str(), captcha_persist_data.c_str()); + ScopedLock s(send_message_lock_); + resp = sendRequest(request); *error_text = resp.error_text; @@ -1532,12 +1094,8 @@ int facebook_client::send_message(int seqid, MCONTACT hContact, const std::strin parent->debugLogA(" Got imageUrl (first): %s", imageUrl.c_str()); parent->debugLogA(" Got captchaPersistData (first): %s", captchaPersistData.c_str()); - std::string capStr = "new_captcha_type=TFBCaptcha&skipped_captcha_data=" + captchaPersistData; - capStr += "&__dyn=" + __dyn(); - capStr += "&__req=" + __req(); - capStr += "&__rev=" + __rev(); - capStr += "&__user=" + this->self_.user_id; - http::response capResp = flap(REQUEST_CAPTCHA_REFRESH, NULL, &capStr); + HttpRequest *request = new RefreshCaptchaRequest(this, captchaPersistData.c_str()); + http::response capResp = sendRequest(request); if (capResp.code == HTTP_CODE_OK) { imageUrl = utils::text::html_entities_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&capResp.data, 3, "img class=\\\"img\\\"", "src=\\\"", "\\\""))); @@ -1587,63 +1145,38 @@ bool facebook_client::post_status(status_data *status) handle_entry("post_status"); if (status->isPage) { - std::string data = "fb_dtsg=" + this->dtsg_; - data += "&user_id=" + status->user_id; - data += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE); - flap(REQUEST_IDENTITY_SWITCH, &data); + // Switch to page identity by which name we will share this post + HttpRequest *request = new SwitchIdentityRequest(this->dtsg_.c_str(), status->user_id.c_str()); + sendRequest(request); } - std::string data; + std::string linkData; if (!status->url.empty()) { - data = "fb_dtsg=" + this->dtsg_; - data += "&targetid=" + (status->user_id.empty() ? this->self_.user_id : status->user_id); - data += "&xhpc_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=" + ttstamp_; - 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); - std::string temp = utils::text::html_entities_decode(utils::text::slashu_to_utf8(resp.data)); + HttpRequest *request = new LinkScraperRequest(this, status); + http::response resp = sendRequest(request); - 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=" + ttstamp_; + std::string temp = utils::text::html_entities_decode(utils::text::slashu_to_utf8(resp.data)); std::string form = utils::text::source_get_value(&temp, 2, "<form", "</form>"); utils::text::replace_all(&form, "\\\"", "\""); - data += "&" + utils::text::source_get_form_data(&form) + "&"; - //data += "&no_picture=0"; + linkData += utils::text::source_get_form_data(&form); + // FIXME: Rework to some "scraped_link" structure to simplify working with it? } - std::string text = utils::url::encode(status->text); - - data += "fb_dtsg=" + this->dtsg_; - 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); + HttpRequest *request = new SharePostRequest(this, status, linkData.c_str()); + http::response resp = sendRequest(request); + + if (status->isPage) { + // Switch back to our identity + HttpRequest *request = new SwitchIdentityRequest(this->dtsg_.c_str(), this->self_.user_id.c_str()); + sendRequest(request); } + + // cleanup status elements (delete users) 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_POST_STATUS, &data); - - if (status->isPage) { - std::string query = "fb_dtsg=" + this->dtsg_; - query += "&user_id=" + this->self_.user_id; - query += "&url=" + std::string(FACEBOOK_URL_HOMEPAGE); - flap(REQUEST_IDENTITY_SWITCH, &query); - } - if (resp.isValid()) { parent->NotifyEvent(parent->m_tszUserName, TranslateT("Status update was successful."), NULL, EVENT_OTHER); return handle_success("post_status"); @@ -1697,18 +1230,7 @@ bool facebook_client::save_url(const std::string &url, const std::wstring &filen bool facebook_client::sms_code(const char *fb_dtsg) { - std::string inner_data = "method_requested=sms_requested"; - inner_data += "¤t_time=" + (utils::time::unix_timestamp() + ".000"); - inner_data += "&__a=1"; - inner_data += "&__user=0"; - inner_data += "&__dyn=" + __dyn(); - inner_data += "&__req=" + __req(); - inner_data += "&__be=0"; - inner_data += "&__pc=EXP1:DEFAULT"; - inner_data += "&fb_dtsg=" + std::string(fb_dtsg); - inner_data += "&ttstamp=" + ttstamp_; - inner_data += "&__rev=" + __rev(); - http::response resp = flap(REQUEST_LOGIN_SMS, &inner_data); + http::response resp = sendRequest(new LoginSmsRequest(this, fb_dtsg)); if (resp.data.find("\"is_valid\":true", 0) == std::string::npos) { // Code wasn't send |