From 1218cb54337c4683baf728bec19701da1f10e5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20P=C3=B6sel?= Date: Sun, 17 Apr 2016 11:14:33 +0000 Subject: Facebook: Implement login with two-way authorization It shows dialog that asks for verification code. User can press button to request code via SMS. Allows 3 attempts to entering correct code, then plugin disconnects. git-svn-id: http://svn.miranda-ng.org/main/trunk@16685 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/FacebookRM/res/facebook.rc | 20 ++++++- protocols/FacebookRM/src/client.h | 1 + protocols/FacebookRM/src/communication.cpp | 95 +++++++++++++++++++++++------- protocols/FacebookRM/src/constants.h | 3 +- protocols/FacebookRM/src/dialogs.cpp | 46 +++++++++++++++ protocols/FacebookRM/src/dialogs.h | 28 +++++++++ protocols/FacebookRM/src/resource.h | 7 ++- protocols/FacebookRM/src/stdafx.h | 1 + 8 files changed, 176 insertions(+), 25 deletions(-) diff --git a/protocols/FacebookRM/res/facebook.rc b/protocols/FacebookRM/res/facebook.rc index b6d0fcc0d8..fb1ed5cd1a 100644 --- a/protocols/FacebookRM/res/facebook.rc +++ b/protocols/FacebookRM/res/facebook.rc @@ -120,7 +120,7 @@ BEGIN CONTROL "Automatically set 'Ignore status change' flag",IDC_SET_IGNORE_STATUS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,107,271,10 CONTROL "Use bigger avatars",IDC_BIGGER_AVATARS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,120,271,10 - CONTROL "Prefer real names instead of nicknames",IDC_NAME_AS_NICK, + CONTROL "Prefer real names instead of nicknames",IDC_NAME_AS_NICK, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,134,271,10 END @@ -209,6 +209,18 @@ BEGIN CONTROL "",IDC_MESSAGES_COUNT_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,283,160,11,14 END +IDD_GUARD DIALOGEX 0, 0, 193, 94 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Facebook Login Approval" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Your security settings requires to input a security code to access your account from unknown browsers.\n\nYou can get the code via Facebook application on your phone or click button below to receive it via SMS.", IDC_STATIC, 7, 7, 179, 40 + PUSHBUTTON "Send SMS", IDSMS, 7, 53, 59, 15 + EDITTEXT IDC_TEXT, 72, 54, 114, 12, ES_AUTOHSCROLL | ES_NUMBER + DEFPUSHBUTTON "OK",IDOK,82,75,50,14 + PUSHBUTTON "Cancel",IDCANCEL,136,75,50,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -296,6 +308,12 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 224 END + + IDD_GUARD, DIALOG + BEGIN + LEFTMARGIN, 7 + TOPMARGIN, 7 + END END #endif // APSTUDIO_INVOKED diff --git a/protocols/FacebookRM/src/client.h b/protocols/FacebookRM/src/client.h index f393aa0a67..3aedecceac 100644 --- a/protocols/FacebookRM/src/client.h +++ b/protocols/FacebookRM/src/client.h @@ -167,6 +167,7 @@ public: bool login(const char *username, const char *password); bool logout(); + bool sms_code(const char *fb_dtsg); const std::string & get_username() const; diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp index 179d9ee273..602c7892e1 100644 --- a/protocols/FacebookRM/src/communication.cpp +++ b/protocols/FacebookRM/src/communication.cpp @@ -276,6 +276,7 @@ std::string facebook_client::choose_server(RequestType request_type) // case REQUEST_FRIENDSHIP: // case REQUEST_UNREAD_THREADS: // case REQUEST_ON_THIS_DAY: + // case REQUEST_LOGIN_SMS: default: return FACEBOOK_SERVER_REGULAR; } @@ -526,6 +527,11 @@ std::string facebook_client::choose_action(RequestType request_type, std::string return action; } + case REQUEST_LOGIN_SMS: + { + return "/ajax/login/approvals/send_sms?dpr=1"; + } + default: return "/?_fb_noscript=1"; } @@ -823,17 +829,8 @@ bool facebook_client::login(const char *username, const char *password) return handle_error("login", FORCE_QUIT); } - // Check whether some Facebook things are required - if (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 (location.find("/checkpoint/") != std::string::npos) - { + // Check whether login checks are required + if (location.find("/checkpoint/") != std::string::npos) { resp = flap(REQUEST_SETUP_MACHINE, NULL, NULL); if (resp.data.find("login_approvals_no_phones") != std::string::npos) { @@ -842,9 +839,48 @@ bool facebook_client::login(const char *username, const char *password) return handle_error("login", FORCE_QUIT); } - std::string inner_data; 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=\"", "\"")); + + CFacebookGuardDialog guardDialog(parent, fb_dtsg.c_str()); + 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(); + + 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); + 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. + if (++attempt >= 3) { + client_notify(TranslateT("You entered too many invalid verification codes. Plugin will disconnect.")); + parent->debugLogA("!!! Login error: Too many invalid attempts to verification code."); + return handle_error("login", FORCE_QUIT); + } + else { + client_notify(TranslateT("You entered wrong verification code. Try it again.")); + } + } + else { + // After successfull verification is showed different page - classic form to save device (as handled at the bottom) + break; + } + } + // Check if we need to approve also last unapproved device if (resp.data.find("name=\"name_action_selected\"") == std::string::npos) { // 1) Continue @@ -876,21 +912,13 @@ bool facebook_client::login(const char *username, const char *password) 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::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); - - } - else if (resp.data.find("name=\"submit[OK]\"") != std::string::npos) { - // TODO: not sure this branch could happen anymore - inner_data = "submit[OK]=OK"; - 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); } else if (resp.data.find("name=\"submit[Get Started]\"") != std::string::npos) { if (!parent->getBool(FACEBOOK_KEY_TRIED_DELETING_DEVICE_ID)) { @@ -1599,3 +1627,28 @@ bool facebook_client::save_url(const std::string &url, const std::tstring &filen return ret; } + +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); + + if (resp.data.find("\"is_valid\":true", 0) == std::string::npos) { + // Code wasn't send + client_notify(TranslateT("Error occurred when requesting verification SMS code.")); + return false; + } + + parent->NotifyEvent(parent->m_tszUserName, TranslateT("Verification SMS code was sent to your mobile phone."), NULL, FACEBOOK_EVENT_OTHER); + return true; +} \ No newline at end of file diff --git a/protocols/FacebookRM/src/constants.h b/protocols/FacebookRM/src/constants.h index 0f55b97a98..aa25defd68 100644 --- a/protocols/FacebookRM/src/constants.h +++ b/protocols/FacebookRM/src/constants.h @@ -128,7 +128,8 @@ enum RequestType { REQUEST_VISIBILITY, // setting chat visibility REQUEST_IDENTITY_SWITCH, // changing identity to post status for pages REQUEST_CAPTCHA_REFRESH, // refreshing captcha dialog (changing captcha type) - + REQUEST_LOGIN_SMS, // request to receive login code via SMS + REQUEST_FEEDS, // getting feeds REQUEST_NOTIFICATIONS, // getting notifications REQUEST_LOAD_FRIENDSHIPS, // getting friendship requests diff --git a/protocols/FacebookRM/src/dialogs.cpp b/protocols/FacebookRM/src/dialogs.cpp index 2b8ec51728..9ce23811a7 100644 --- a/protocols/FacebookRM/src/dialogs.cpp +++ b/protocols/FacebookRM/src/dialogs.cpp @@ -635,3 +635,49 @@ INT_PTR CALLBACK FBOptionsMessagingProc(HWND hwnd, UINT message, WPARAM wparam, return FALSE; } + +///////////////////////////////////////////////////////////////////////////////// + +CFacebookGuardDialog::CFacebookGuardDialog(FacebookProto *proto, const char *fb_dtsg) + : CFacebookDlgBase(proto, IDD_GUARD, false), + m_ok(this, IDOK), + m_sms(this, IDSMS), + m_text(this, IDC_TEXT), + m_fb_dtsg(fb_dtsg) +{ + memset(m_code, 0, sizeof(m_code)); + m_ok.OnClick = Callback(this, &CFacebookGuardDialog::OnOk); + m_sms.OnClick = Callback(this, &CFacebookGuardDialog::OnSms); +} + +void CFacebookGuardDialog::OnInitDialog() +{ + SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)IcoLib_GetIconByHandle(GetIconHandle("facebook"), TRUE)); + SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)IcoLib_GetIconByHandle(GetIconHandle("facebook"))); + + SendMessage(m_text.GetHwnd(), EM_LIMITTEXT, 6, 0); + + Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "GuardWindow"); +} + +void CFacebookGuardDialog::OnOk(CCtrlButton*) +{ + mir_strncpy(m_code, ptrA(m_text.GetTextA()), _countof(m_code)); + EndDialog(m_hwnd, DIALOG_RESULT_OK); +} + +void CFacebookGuardDialog::OnSms(CCtrlButton *btn) +{ + btn->Disable(); + m_proto->facy.sms_code(m_fb_dtsg); +} + +void CFacebookGuardDialog::OnClose() +{ + Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "GuardWindow"); +} + +const char* CFacebookGuardDialog::GetCode() +{ + return m_code; +} diff --git a/protocols/FacebookRM/src/dialogs.h b/protocols/FacebookRM/src/dialogs.h index 41c5e8006b..ee7d4da144 100644 --- a/protocols/FacebookRM/src/dialogs.h +++ b/protocols/FacebookRM/src/dialogs.h @@ -28,3 +28,31 @@ INT_PTR CALLBACK FBOptionsProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lp INT_PTR CALLBACK FBOptionsMessagingProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); INT_PTR CALLBACK FBOptionsEventsProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); INT_PTR CALLBACK FBOptionsStatusesProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + +///////////////////////////////////////////////////////////////////////////////// + +typedef CProtoDlgBase CFacebookDlgBase; + +#define DIALOG_RESULT_OK 1 + +class CFacebookGuardDialog : public CFacebookDlgBase +{ +private: + const char *m_fb_dtsg; + char m_code[7]; + + CCtrlEdit m_text; + CCtrlButton m_ok; + CCtrlButton m_sms; + +protected: + void OnInitDialog(); + void OnOk(CCtrlButton*); + void OnSms(CCtrlButton*); + void OnClose(); + +public: + CFacebookGuardDialog(FacebookProto *proto, const char *fb_dtsg); + + const char *GetCode(); +}; diff --git a/protocols/FacebookRM/src/resource.h b/protocols/FacebookRM/src/resource.h index 0c89884724..ba66e994a0 100644 --- a/protocols/FacebookRM/src/resource.h +++ b/protocols/FacebookRM/src/resource.h @@ -19,6 +19,7 @@ #define IDI_CONVERSATION 131 #define IDI_READ 132 #define IDI_KEYS 133 +#define IDD_GUARD 134 #define IDC_UN 1001 #define IDC_PW 1002 #define IDC_NEWACCOUNTLINK 1003 @@ -65,14 +66,16 @@ #define IDC_INSTRUCTION 1214 #define IDC_FRAME1 1215 #define IDC_FRAME2 1216 +#define IDC_TEXT 1217 +#define IDC_SEND_SMS 1218 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 134 +#define _APS_NEXT_RESOURCE_VALUE 135 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1218 +#define _APS_NEXT_CONTROL_VALUE 1219 #define _APS_NEXT_SYMED_VALUE 134 #endif #endif diff --git a/protocols/FacebookRM/src/stdafx.h b/protocols/FacebookRM/src/stdafx.h index 23868df198..1549e7ccf9 100644 --- a/protocols/FacebookRM/src/stdafx.h +++ b/protocols/FacebookRM/src/stdafx.h @@ -61,6 +61,7 @@ along with this program. If not, see . #include #include #include +#include class FacebookProto; -- cgit v1.2.3