From 5e346ea6fbcd4a028bc7dd63efd09ff42770f8ef Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Tue, 24 May 2016 23:08:48 +0600 Subject: CLOUD: Add OneDrive refresh_token support It might be not that easy to restart the request after new token received, though. --- backends/cloud/onedrive/onedrivestorage.cpp | 139 +++++++++++++++++++--------- backends/cloud/onedrive/onedrivestorage.h | 26 ++++-- 2 files changed, 115 insertions(+), 50 deletions(-) (limited to 'backends/cloud') diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index b632c74580..2f10841cc9 100644 --- a/backends/cloud/onedrive/onedrivestorage.cpp +++ b/backends/cloud/onedrive/onedrivestorage.cpp @@ -38,50 +38,111 @@ namespace OneDrive { Common::String OneDriveStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth Common::String OneDriveStorage::SECRET; //TODO: hide these secrets somehow -static void saveAccessTokenCallback(void *ptr) { - Common::JSONValue *json = (Common::JSONValue *)ptr; - if (json) { - debug("saveAccessTokenCallback:"); - debug("%s", json->stringify(true).c_str()); - - //TODO: do something about refresh token - Common::JSONObject result = json->asObject(); - if (!result.contains("access_token") || !result.contains("user_id")) { - warning("Bad response, no token/user_id passed"); - } else { - OneDriveStorage::addStorage(result.getVal("access_token")->asString(), result.getVal("user_id")->asString()); - ConfMan.removeKey("onedrive_code", "cloud"); - debug("Done! You can use OneDrive now! Look:"); - g_system->getCloudManager()->syncSaves(); - } +OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken): + _token(accessToken), _uid(userId), _refreshToken(refreshToken) {} - delete json; +OneDriveStorage::OneDriveStorage(Common::String code) { + getAccessToken(new Common::Callback(this, &OneDriveStorage::codeFlowComplete), code); +} + +OneDriveStorage::~OneDriveStorage() {} + +void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) { + bool codeFlow = (code != ""); + + if (!codeFlow && _refreshToken == "") { + warning("OneDriveStorage: no refresh token available to get new access token."); + if (callback) (*callback)(false); + return; + } + + Common::BaseCallback<> *innerCallback = new Common::CallbackBridge(this, &OneDriveStorage::tokenRefreshed, callback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf"); + if (codeFlow) { + request->addPostField("code=" + code); + request->addPostField("grant_type=authorization_code"); } else { - debug("saveAccessTokenCallback: got NULL instead of JSON!"); + request->addPostField("refresh_token=" + _refreshToken); + request->addPostField("grant_type=refresh_token"); } + request->addPostField("client_id=" + KEY); + request->addPostField("client_secret=" + SECRET); + request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"); + ConnMan.addRequest(request); } -OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) { - curl_global_init(CURL_GLOBAL_ALL); +void OneDriveStorage::tokenRefreshed(BoolCallback callback, void *jsonPointer) { + Common::JSONValue *json = (Common::JSONValue *)jsonPointer; + if (!json) { + warning("OneDriveStorage: got NULL instead of JSON"); + if (callback) (*callback)(false); + return; + } + + Common::JSONObject result = json->asObject(); + if (!result.contains("access_token") || !result.contains("user_id") || !result.contains("refresh_token")) { + warning("Bad response, no token or user_id passed"); + debug("%s", json->stringify().c_str()); + if (callback) (*callback)(false); + } else { + _token = result.getVal("access_token")->asString(); + _uid = result.getVal("user_id")->asString(); + _refreshToken = result.getVal("refresh_token")->asString(); + g_system->getCloudManager()->save(); //ask CloudManager to save our new refreshToken + if (callback) (*callback)(true); + } + delete json; } -OneDriveStorage::~OneDriveStorage() { - curl_global_cleanup(); +void OneDriveStorage::codeFlowComplete(bool success) { + if (!success) { + warning("OneDriveStorage: failed to get access token through code flow"); + return; + } + + g_system->getCloudManager()->addStorage(this); + ConfMan.removeKey("onedrive_code", "cloud"); + debug("Done! You can use OneDrive now! Look:"); + g_system->getCloudManager()->syncSaves(); } void OneDriveStorage::saveConfig(Common::String keyPrefix) { ConfMan.set(keyPrefix + "type", "OneDrive", "cloud"); ConfMan.set(keyPrefix + "access_token", _token, "cloud"); ConfMan.set(keyPrefix + "user_id", _uid, "cloud"); + ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud"); } -void OneDriveStorage::syncSaves(BoolCallback callback) { - //this is not the real syncSaves() implementation +void OneDriveStorage::printJsonTokenReceived(bool success) { + if (success) syncSaves(0); //try again +} + +void OneDriveStorage::printJson(void *jsonPointer) { + Common::JSONValue *json = (Common::JSONValue *)jsonPointer; + if (!json) { + warning("printJson: NULL"); + return; + } + + Common::JSONObject result = json->asObject(); + if (result.contains("error")) { + //Common::JSONObject error = result.getVal("error")->asObject(); + debug("bad token, trying again..."); + getAccessToken(new Common::Callback(this, &OneDriveStorage::printJsonTokenReceived)); + delete json; + return; + } + + debug("%s", json->stringify().c_str()); + delete json; } -void OneDriveStorage::addStorage(Common::String token, Common::String uid) { - Storage *storage = new OneDriveStorage(token, uid); - g_system->getCloudManager()->addStorage(storage); +void OneDriveStorage::syncSaves(BoolCallback callback) { + //this is not the real syncSaves() implementation + Common::BaseCallback<> *innerCallback = new Common::Callback(this, &OneDriveStorage::printJson); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.onedrive.com/v1.0/drives/"); + request->addHeader("Authorization: bearer " + _token); + ConnMan.addRequest(request); } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { @@ -98,9 +159,15 @@ OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { return 0; } + if (!ConfMan.hasKey(keyPrefix + "refresh_token", "cloud")) { + warning("No refresh_token found"); + return 0; + } + Common::String accessToken = ConfMan.get(keyPrefix + "access_token", "cloud"); Common::String userId = ConfMan.get(keyPrefix + "user_id", "cloud"); - return new OneDriveStorage(accessToken, userId); + Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", "cloud"); + return new OneDriveStorage(accessToken, userId, refreshToken); } Common::String OneDriveStorage::getAuthLink() { @@ -109,7 +176,7 @@ Common::String OneDriveStorage::getAuthLink() { url += "&redirect_uri=http://localhost:12345/"; //that's for copy-pasting //url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening url += "&client_id=" + KEY; - url += "&scope=onedrive.appfolder"; //TODO + url += "&scope=onedrive.appfolder%20offline_access"; //TODO return url; } @@ -124,7 +191,7 @@ void OneDriveStorage::authThroughConsole() { if (ConfMan.hasKey("onedrive_code", "cloud")) { //phase 2: get access_token using specified code - getAccessToken(ConfMan.get("onedrive_code", "cloud")); + new OneDriveStorage(ConfMan.get("onedrive_code", "cloud")); return; } @@ -135,17 +202,5 @@ void OneDriveStorage::authThroughConsole() { debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n"); } -void OneDriveStorage::getAccessToken(Common::String code) { - Common::BaseCallback<> *callback = new Common::GlobalFunctionCallback(saveAccessTokenCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://login.live.com/oauth20_token.srf"); - //Content-Type: application/x-www-form-urlencoded - request->addPostField("code=" + code); - request->addPostField("grant_type=authorization_code"); - request->addPostField("client_id=" + KEY); - request->addPostField("client_secret=" + SECRET); - request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"); - ConnMan.addRequest(request); -} - } //end of namespace OneDrive } //end of namespace Cloud diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index 99f8476bc1..28f37aee2c 100644 --- a/backends/cloud/onedrive/onedrivestorage.h +++ b/backends/cloud/onedrive/onedrivestorage.h @@ -32,13 +32,28 @@ namespace OneDrive { class OneDriveStorage: public Cloud::Storage { static Common::String KEY, SECRET; - Common::String _token, _uid; + Common::String _token, _uid, _refreshToken; /** This private constructor is called from loadFromConfig(). */ - OneDriveStorage(Common::String token, Common::String uid); + OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken); - static void getAccessToken(Common::String code); + /** + * This private constructor is called from authThroughConsole() (phase 2). + * It uses OAuth code flow to get tokens. + */ + OneDriveStorage(Common::String code); + + /** + * Gets new access_token. If passed is "", refresh_token is used. + * Use "" in order to refresh token and pass a callback, so you could + * continue your work when new token is available. + */ + void getAccessToken(BoolCallback callback, Common::String code = ""); + void tokenRefreshed(BoolCallback callback, void *jsonPointer); + void codeFlowComplete(bool success); + void printJson(void *jsonPointer); + void printJsonTokenReceived(bool success); public: virtual ~OneDriveStorage(); @@ -91,11 +106,6 @@ public: /** Returns whether there are any requests running. */ virtual bool isWorking() { return false; } //TODO - /** - * Add OneDriveStorage with given token and uid into Cloud::Manager. - */ - static void addStorage(Common::String token, Common::String uid); - /** * Load token and user id from configs and return OneDriveStorage for those. * @return pointer to the newly created OneDriveStorage or 0 if some problem occured. -- cgit v1.2.3