diff options
-rw-r--r-- | backends/cloud/dropbox/dropboxstorage.cpp | 110 | ||||
-rw-r--r-- | backends/cloud/dropbox/dropboxstorage.h | 39 | ||||
-rw-r--r-- | backends/cloud/manager.cpp | 15 | ||||
-rw-r--r-- | backends/cloud/manager.h | 2 | ||||
-rw-r--r-- | backends/cloud/storage.h | 6 | ||||
-rw-r--r-- | backends/networking/curl/connectionmanager.cpp | 4 | ||||
-rw-r--r-- | backends/networking/curl/connectionmanager.h | 3 | ||||
-rw-r--r-- | backends/networking/curl/curljsonrequest.cpp | 16 | ||||
-rw-r--r-- | backends/networking/curl/curljsonrequest.h | 8 | ||||
-rw-r--r-- | backends/networking/curl/networkreadstream.cpp | 7 | ||||
-rw-r--r-- | backends/networking/curl/networkreadstream.h | 3 | ||||
-rw-r--r-- | base/main.cpp | 1 | ||||
-rw-r--r-- | common/cloudmanager.h | 12 | ||||
-rw-r--r-- | common/config-manager.cpp | 26 | ||||
-rw-r--r-- | common/config-manager.h | 9 |
15 files changed, 239 insertions, 22 deletions
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index 729283bcfa..add6bff54c 100644 --- a/backends/cloud/dropbox/dropboxstorage.cpp +++ b/backends/cloud/dropbox/dropboxstorage.cpp @@ -23,6 +23,7 @@ #include "backends/cloud/dropbox/dropboxstorage.h" #include "backends/networking/curl/curljsonrequest.h" +#include "common/config-manager.h" #include "common/debug.h" #include "common/json.h" #include <curl/curl.h> @@ -30,18 +31,44 @@ namespace Cloud { namespace Dropbox { -static void curlJsonCallback(void *ptr) { +Common::String DropboxStorage::KEY; //can't use ConfMan there yet, loading it on instance creation/auth +Common::String DropboxStorage::SECRET; //TODO: hide these secrets somehow + +static void printJsonCallback(void *ptr) { + Common::JSONValue *json = (Common::JSONValue *)ptr; + if (json) { + debug("printJsonCallback:"); + debug("%s", json->stringify(true).c_str()); + delete json; + } else { + debug("printJsonCallback: got NULL instead of JSON!"); + } +} + +static void saveAccessTokenCallback(void *ptr) { Common::JSONValue *json = (Common::JSONValue *)ptr; if (json) { - debug("curlJsonCallback:"); + debug("saveAccessTokenCallback:"); debug("%s", json->stringify(true).c_str()); + + Common::JSONObject result = json->asObject(); + if (!result.contains("access_token") || !result.contains("uid")) { + warning("Bad response, no token/uid passed"); + } else { + ConfMan.set("current_storage_type", "Dropbox", "cloud"); + ConfMan.set("current_storage_access_token", result.getVal("access_token")->asString(), "cloud"); + ConfMan.set("current_storage_user_id", result.getVal("uid")->asString(), "cloud"); + ConfMan.removeKey("dropbox_code", "cloud"); + debug("Now please restart ScummVM to apply the changes."); + } + delete json; } else { - debug("curlJsonCallback: got NULL instead of JSON!"); + debug("saveAccessTokenCallback: got NULL instead of JSON!"); } } -DropboxStorage::DropboxStorage() { +DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) { curl_global_init(CURL_GLOBAL_ALL); } @@ -54,9 +81,78 @@ void DropboxStorage::listDirectory(Common::String path) { } void DropboxStorage::syncSaves() { - //not so Dropbox, just testing JSON requesting & parsing: - addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=205387401")); - addRequest(new Networking::CurlJsonRequest(curlJsonCallback, "https://api.vk.com/method/users.get?v=5.50&user_ids=28870501")); + //not syncing, but already something: + printInfo(); +} + +void DropboxStorage::printInfo() { + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(printJsonCallback, "https://api.dropboxapi.com/1/account/info"); + request->addHeader("Authorization: Bearer " + _token); + addRequest(request); +} + +DropboxStorage *DropboxStorage::loadFromConfig() { + KEY = ConfMan.get("DROPBOX_KEY", "cloud"); + SECRET = ConfMan.get("DROPBOX_SECRET", "cloud"); + + if (!ConfMan.hasKey("current_storage_access_token", "cloud")) { + warning("No access_token found"); + return 0; + } + + if (!ConfMan.hasKey("current_storage_user_id", "cloud")) { + warning("No user_id found"); + return 0; + } + + Common::String accessToken = ConfMan.get("current_storage_access_token", "cloud"); + Common::String userId = ConfMan.get("current_storage_user_id", "cloud"); + return new DropboxStorage(accessToken, userId); +} + +Common::String DropboxStorage::getAuthLink() { + Common::String url = "https://www.dropbox.com/1/oauth2/authorize"; + url += "?response_type=code"; + 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; + return url; +} + +DropboxStorage *DropboxStorage::authThroughConsole() { + if (!ConfMan.hasKey("DROPBOX_KEY", "cloud") || !ConfMan.hasKey("DROPBOX_SECRET", "cloud")) { + warning("No Dropbox keys available, cannot do auth"); + return 0; + } + + KEY = ConfMan.get("DROPBOX_KEY", "cloud"); + SECRET = ConfMan.get("DROPBOX_SECRET", "cloud"); + + if (ConfMan.hasKey("dropbox_code", "cloud")) { + //phase 2: get access_token using specified code + return getAccessToken(ConfMan.get("dropbox_code", "cloud")); + } + + debug("Navigate to this URL and press \"Allow\":"); + debug("%s\n", getAuthLink().c_str()); + debug("Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key.\n"); + debug("Navigate to this URL to get more information on ScummVM's configuration files:"); + debug("http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM\n"); + return 0; +} + +DropboxStorage *DropboxStorage::getAccessToken(Common::String code) { + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(saveAccessTokenCallback, "https://api.dropboxapi.com/1/oauth2/token"); + 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"); + + //OK, that's not how I imagined that... + DropboxStorage *storage = new DropboxStorage("", ""); + storage->addRequest(request); + return storage; } } //end of namespace Dropbox diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index a0229438be..f2281f146f 100644 --- a/backends/cloud/dropbox/dropboxstorage.h +++ b/backends/cloud/dropbox/dropboxstorage.h @@ -24,18 +24,47 @@ #define BACKENDS_CLOUD_DROPBOX_STORAGE_H #include "backends/cloud/storage.h" +#include "../manager.h" -namespace Cloud { namespace Dropbox { +namespace Cloud { +namespace Dropbox { -class DropboxStorage: public Cloud::Storage { -public: - DropboxStorage(); +class DropboxStorage: public Cloud::Storage { + static Common::String KEY, SECRET; + + Common::String _token, _uid; + + /** This private constructor is called from loadFromConfig(). */ + DropboxStorage(Common::String token, Common::String uid); + + static DropboxStorage *getAccessToken(Common::String code); + +public: virtual ~DropboxStorage(); virtual void listDirectory(Common::String path); virtual void syncSaves(); + virtual void printInfo(); + /** + * Load token and user id from configs and return DropboxStorage for those. + * @return pointer to the newly created DropboxStorage or 0 if some problem occured. + */ + static DropboxStorage *loadFromConfig(); + + /** + * Returns Dropbox auth link. + */ + static Common::String DropboxStorage::getAuthLink(); + + /** + * Show message with Dropbox auth instructions. (Temporary) + * Returns temporary DropboxStorage, which does network requests + * to get access token. + */ + static DropboxStorage *authThroughConsole(); }; -} } //end of namespace Cloud::Dropbox +} //end of namespace Dropbox +} //end of namespace Cloud #endif diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp index 58bb0ce83f..08340e9288 100644 --- a/backends/cloud/manager.cpp +++ b/backends/cloud/manager.cpp @@ -22,13 +22,26 @@ #include "backends/cloud/manager.h" #include "backends/cloud/dropbox/dropboxstorage.h" +#include "common/config-manager.h" namespace Cloud { -Manager::Manager(): _currentStorage(new Dropbox::DropboxStorage()) {} +Manager::Manager(): _currentStorage(0) {} Manager::~Manager() { delete _currentStorage; } +void Manager::init() { + if (ConfMan.hasKey("current_storage_type", "cloud")) { + Common::String storageType = ConfMan.get("current_storage_type", "cloud"); + if (storageType == "Dropbox") _currentStorage = Dropbox::DropboxStorage::loadFromConfig(); + else warning("Unknown cloud storage type '%s' passed", storageType.c_str()); + } + else { + //this is temporary console offer to auth with Dropbox (because there is no other storage type yet anyway) + Dropbox::DropboxStorage::authThroughConsole(); + } +} + Storage* Manager::getCurrentStorage() { return _currentStorage; } diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h index 11cc595da4..a1f046d71d 100644 --- a/backends/cloud/manager.h +++ b/backends/cloud/manager.h @@ -35,6 +35,8 @@ public: Manager(); virtual ~Manager(); + virtual void init(); + virtual Storage* getCurrentStorage(); virtual void syncSaves(); }; diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 84b6157a22..a5d048d3af 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -63,6 +63,12 @@ public: */ virtual void syncSaves() = 0; + + /** + * Prints user info on console. (Temporary) + */ + + virtual void printInfo() = 0; }; } //end of namespace Cloud diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp index 8c718e019b..d34eab23e8 100644 --- a/backends/networking/curl/connectionmanager.cpp +++ b/backends/networking/curl/connectionmanager.cpp @@ -39,8 +39,8 @@ ConnectionManager::~ConnectionManager() { curl_global_cleanup(); } -NetworkReadStream *ConnectionManager::makeRequest(const char *url) { - NetworkReadStream *stream = new NetworkReadStream(url); +NetworkReadStream *ConnectionManager::makeRequest(const char *url, curl_slist *headersList, Common::String postFields) { + NetworkReadStream *stream = new NetworkReadStream(url, headersList, postFields); curl_multi_add_handle(_multi, stream->getEasyHandle()); return stream; } diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h index fadcdf1372..b83de6191a 100644 --- a/backends/networking/curl/connectionmanager.h +++ b/backends/networking/curl/connectionmanager.h @@ -26,6 +26,7 @@ #include "common/str.h" typedef void CURLM; +struct curl_slist; namespace Networking { @@ -38,7 +39,7 @@ public: ConnectionManager(); virtual ~ConnectionManager(); - NetworkReadStream *makeRequest(const char *url); + NetworkReadStream *makeRequest(const char *url, curl_slist *headersList, Common::String postFields); void handle(); }; diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp index f5a7a7c8d0..0c6363467a 100644 --- a/backends/networking/curl/curljsonrequest.cpp +++ b/backends/networking/curl/curljsonrequest.cpp @@ -30,7 +30,7 @@ namespace Networking { -CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _contentsStream(DisposeAfterUse::YES) { +CurlJsonRequest::CurlJsonRequest(Callback cb, const char *url) : Request(cb), _stream(0), _headersList(0), _contentsStream(DisposeAfterUse::YES) { _url = url; } @@ -57,7 +57,7 @@ char *CurlJsonRequest::getPreparedContents() { } bool CurlJsonRequest::handle(ConnectionManager &manager) { - if (!_stream) _stream = manager.makeRequest(_url); + if (!_stream) _stream = manager.makeRequest(_url, _headersList, _postFields); if (_stream) { const int kBufSize = 16*1024; @@ -83,4 +83,16 @@ bool CurlJsonRequest::handle(ConnectionManager &manager) { return false; } +void CurlJsonRequest::addHeader(Common::String header) { + _headersList = curl_slist_append(_headersList, header.c_str()); +} + +void CurlJsonRequest::addPostField(Common::String keyValuePair) { + if (_postFields == "") + _postFields = keyValuePair; + else + _postFields += "&" + keyValuePair; +} + + } //end of namespace Networking diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h index e9634393dc..17df9693f2 100644 --- a/backends/networking/curl/curljsonrequest.h +++ b/backends/networking/curl/curljsonrequest.h @@ -26,6 +26,8 @@ #include "backends/cloud/request.h" #include "common/memstream.h" +struct curl_slist; + namespace Networking { class NetworkReadStream; @@ -33,6 +35,8 @@ class NetworkReadStream; class CurlJsonRequest : public Cloud::Request { const char *_url; NetworkReadStream *_stream; + curl_slist *_headersList; + Common::String _postFields; Common::MemoryWriteStreamDynamic _contentsStream; /** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */ @@ -43,6 +47,10 @@ public: virtual ~CurlJsonRequest(); virtual bool handle(ConnectionManager &manager); + + void addHeader(Common::String header); + + void addPostField(Common::String header); }; } //end of namespace Networking diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp index b8cb81aca2..f2af4fd50c 100644 --- a/backends/networking/curl/networkreadstream.cpp +++ b/backends/networking/curl/networkreadstream.cpp @@ -34,7 +34,9 @@ static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) { return 0; } -NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _requestComplete(false) { +NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields): + _easy(0), _eos(false), _requestComplete(false) +{ _easy = curl_easy_init(); curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback); curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us @@ -42,6 +44,9 @@ NetworkReadStream::NetworkReadStream(const char *url): _easy(0), _eos(false), _r curl_easy_setopt(_easy, CURLOPT_HEADER, 0L); curl_easy_setopt(_easy, CURLOPT_URL, url); curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList); + curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, postFields.size()); + curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, postFields.c_str()); } NetworkReadStream::~NetworkReadStream() { diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h index 0333e4fb16..bc4b761ba7 100644 --- a/backends/networking/curl/networkreadstream.h +++ b/backends/networking/curl/networkreadstream.h @@ -28,6 +28,7 @@ #include "common/str.h" typedef void CURL; +struct curl_slist; namespace Networking { @@ -36,7 +37,7 @@ class NetworkReadStream: public Common::MemoryReadWriteStream { bool _eos, _requestComplete; public: - NetworkReadStream(const char *url); + NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields); virtual ~NetworkReadStream(); CURL *getEasyHandle() const { return _easy; } diff --git a/base/main.cpp b/base/main.cpp index ac24376e37..f629eb98d8 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -478,6 +478,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { #endif #ifdef USE_CLOUD + system.getCloudManager()->init(); system.getCloudManager()->syncSaves(); #endif diff --git a/common/cloudmanager.h b/common/cloudmanager.h index 6b5768280a..8a10a1a3c7 100644 --- a/common/cloudmanager.h +++ b/common/cloudmanager.h @@ -29,8 +29,16 @@ namespace Common { class CloudManager { public: - CloudManager() {}; - virtual ~CloudManager() {}; + CloudManager() {} + virtual ~CloudManager() {} + + /** + * Loads all information from configs and creates current Storage instance. + * + * @note It's called once on startup in scummvm_main(). + */ + + virtual void init() = 0; /** * Returns active Storage, which could be used to interact diff --git a/common/config-manager.cpp b/common/config-manager.cpp index 24c877a4e9..40d8aca219 100644 --- a/common/config-manager.cpp +++ b/common/config-manager.cpp @@ -45,6 +45,10 @@ char const *const ConfigManager::kTransientDomain = "__TRANSIENT"; char const *const ConfigManager::kKeymapperDomain = "keymapper"; #endif +#ifdef USE_CLOUD +char const *const ConfigManager::kCloudDomain = "cloud"; +#endif + #pragma mark - @@ -67,6 +71,9 @@ void ConfigManager::copyFrom(ConfigManager &source) { #ifdef ENABLE_KEYMAPPER _keymapperDomain = source._keymapperDomain; #endif +#ifdef USE_CLOUD + _cloudDomain = source._cloudDomain; +#endif _domainSaveOrder = source._domainSaveOrder; _activeDomainName = source._activeDomainName; _activeDomain = &_gameDomains[_activeDomainName]; @@ -121,6 +128,10 @@ void ConfigManager::addDomain(const String &domainName, const ConfigManager::Dom } else if (domainName == kKeymapperDomain) { _keymapperDomain = domain; #endif +#ifdef USE_CLOUD + } else if (domainName == kCloudDomain) { + _cloudDomain = domain; +#endif } else if (domain.contains("gameid")) { // If the domain contains "gameid" we assume it's a game domain if (_gameDomains.contains(domainName)) @@ -160,6 +171,9 @@ void ConfigManager::loadFromStream(SeekableReadStream &stream) { #ifdef ENABLE_KEYMAPPER _keymapperDomain.clear(); #endif +#ifdef USE_CLOUD + _cloudDomain.clear(); +#endif // TODO: Detect if a domain occurs multiple times (or likewise, if // a key occurs multiple times inside one domain). @@ -272,6 +286,10 @@ void ConfigManager::flushToDisk() { // Write the keymapper domain writeDomain(*stream, kKeymapperDomain, _keymapperDomain); #endif +#ifdef USE_CLOUD + // Write the cloud domain + writeDomain(*stream, kCloudDomain, _cloudDomain); +#endif DomainMap::const_iterator d; @@ -359,6 +377,10 @@ const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) con if (domName == kKeymapperDomain) return &_keymapperDomain; #endif +#ifdef USE_CLOUD + if (domName == kCloudDomain) + return &_cloudDomain; +#endif if (_gameDomains.contains(domName)) return &_gameDomains[domName]; if (_miscDomains.contains(domName)) @@ -379,6 +401,10 @@ ConfigManager::Domain *ConfigManager::getDomain(const String &domName) { if (domName == kKeymapperDomain) return &_keymapperDomain; #endif +#ifdef USE_CLOUD + if (domName == kCloudDomain) + return &_cloudDomain; +#endif if (_gameDomains.contains(domName)) return &_gameDomains[domName]; if (_miscDomains.contains(domName)) diff --git a/common/config-manager.h b/common/config-manager.h index 14f911f69d..669faaaf88 100644 --- a/common/config-manager.h +++ b/common/config-manager.h @@ -94,6 +94,11 @@ public: static char const *const kKeymapperDomain; #endif +#ifdef USE_CLOUD + /** The name of cloud domain used to store user's tokens */ + static char const *const kCloudDomain; +#endif + void loadDefaultConfigFile(); void loadConfigFile(const String &filename); @@ -188,6 +193,10 @@ private: Domain _keymapperDomain; #endif +#ifdef USE_CLOUD + Domain _cloudDomain; +#endif + Array<String> _domainSaveOrder; String _activeDomainName; |