Initial commit
This commit is contained in:
236
backends/cloud/basestorage.cpp
Normal file
236
backends/cloud/basestorage.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/basestorage.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
BaseStorage::BaseStorage() {}
|
||||
|
||||
BaseStorage::BaseStorage(const Common::String &token, const Common::String &refreshToken, bool enabled):
|
||||
_token(token), _refreshToken(refreshToken) {
|
||||
_isEnabled = enabled;
|
||||
}
|
||||
|
||||
BaseStorage::~BaseStorage() {}
|
||||
|
||||
void BaseStorage::getAccessToken(const Common::String &code, Networking::ErrorCallback callback) {
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BaseStorage, const Networking::ErrorResponse &, const Networking::JsonResponse &>(this, &BaseStorage::codeFlowComplete, callback);
|
||||
Networking::ErrorCallback errorCallback = new Common::CallbackBridge<BaseStorage, const Networking::ErrorResponse &, const Networking::ErrorResponse &>(this, &BaseStorage::codeFlowFailed, callback);
|
||||
|
||||
Common::String url = Common::String::format("https://cloud.scummvm.org/%s/token/%s", cloudProvider().c_str(), code.c_str());
|
||||
Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(innerCallback, errorCallback, url);
|
||||
|
||||
addRequest(request);
|
||||
}
|
||||
|
||||
void BaseStorage::codeFlowComplete(Networking::ErrorCallback callback, const Networking::JsonResponse &response) {
|
||||
bool success = true;
|
||||
Common::String callbackMessage = "OK";
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
debug(9, "BaseStorage::codeFlowComplete: got NULL instead of JSON!");
|
||||
success = false;
|
||||
callbackMessage = "Incorrect JSON.";
|
||||
}
|
||||
|
||||
if (success && !json->isObject()) {
|
||||
debug(9, "BaseStorage::codeFlowComplete: passed JSON is not an object!");
|
||||
success = false;
|
||||
callbackMessage = "Incorrect JSON.";
|
||||
}
|
||||
|
||||
Common::JSONObject result;
|
||||
if (success) {
|
||||
result = json->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::codeFlowComplete")) {
|
||||
warning("BaseStorage: bad response, no 'error' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
callbackMessage = "Incorrect JSON.";
|
||||
}
|
||||
}
|
||||
|
||||
if (success && result.getVal("error")->asBool()) {
|
||||
Common::String errorMessage = "{error: true}, message is missing";
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(result, "message", "BaseStorage::codeFlowComplete")) {
|
||||
errorMessage = result.getVal("message")->asString();
|
||||
}
|
||||
warning("BaseStorage: response says error occurred: %s", errorMessage.c_str());
|
||||
success = false;
|
||||
callbackMessage = errorMessage;
|
||||
}
|
||||
|
||||
if (success && !Networking::HttpJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::codeFlowComplete")) {
|
||||
warning("BaseStorage: bad response, no 'oauth' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
callbackMessage = "Incorrect JSON.";
|
||||
}
|
||||
|
||||
Common::JSONObject oauth;
|
||||
bool requiresRefreshToken = needsRefreshToken();
|
||||
if (success) {
|
||||
oauth = result.getVal("oauth")->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::codeFlowComplete") ||
|
||||
!Networking::HttpJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::codeFlowComplete", !requiresRefreshToken)) {
|
||||
warning("BaseStorage: bad response, no 'access_token' or 'refresh_token' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
callbackMessage = "Incorrect JSON.";
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
_token = oauth.getVal("access_token")->asString();
|
||||
if (requiresRefreshToken) {
|
||||
_refreshToken = oauth.getVal("refresh_token")->asString();
|
||||
}
|
||||
CloudMan.replaceStorage(this, storageIndex());
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
if (!success)
|
||||
CloudMan.removeStorage(this);
|
||||
if (callback)
|
||||
(*callback)(Networking::ErrorResponse(nullptr, false, !success, callbackMessage, -1));
|
||||
delete json;
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void BaseStorage::codeFlowFailed(Networking::ErrorCallback callback, const Networking::ErrorResponse &error) {
|
||||
debug(9, "BaseStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
|
||||
if (callback)
|
||||
(*callback)(error);
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void BaseStorage::refreshAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (_refreshToken == "") {
|
||||
warning("BaseStorage: no refresh token available to get new access token.");
|
||||
if (callback) (*callback)(BoolResponse(nullptr, false));
|
||||
return;
|
||||
}
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BaseStorage, const BoolResponse &, const Networking::JsonResponse &>(this, &BaseStorage::tokenRefreshed, callback);
|
||||
if (errorCallback == nullptr)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
|
||||
Common::String url = Common::String::format("https://cloud.scummvm.org/%s/refresh", cloudProvider().c_str());
|
||||
Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(innerCallback, errorCallback, url);
|
||||
request->addHeader("X-ScummVM-Refresh-Token: " + _refreshToken);
|
||||
addRequest(request);
|
||||
}
|
||||
|
||||
void BaseStorage::tokenRefreshed(BoolCallback callback, const Networking::JsonResponse &response) {
|
||||
bool success = true;
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
debug(9, "BaseStorage::tokenRefreshed: got NULL instead of JSON!");
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && !json->isObject()) {
|
||||
debug(9, "BaseStorage::tokenRefreshed: passed JSON is not an object!");
|
||||
success = false;
|
||||
}
|
||||
|
||||
Common::JSONObject result;
|
||||
if (success) {
|
||||
result = json->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::tokenRefreshed")) {
|
||||
warning("BaseStorage: bad response, no 'error' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success && result.getVal("error")->asBool()) {
|
||||
Common::String errorMessage = "{error: true}, message is missing";
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(result, "message", "BaseStorage::tokenRefreshed")) {
|
||||
errorMessage = result.getVal("message")->asString();
|
||||
}
|
||||
warning("BaseStorage: response says error occurred: %s", errorMessage.c_str());
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && !Networking::HttpJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::tokenRefreshed")) {
|
||||
warning("BaseStorage: bad response, no 'oauth' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
}
|
||||
|
||||
Common::JSONObject oauth;
|
||||
bool requiresRefreshToken = !canReuseRefreshToken();
|
||||
if (success) {
|
||||
oauth = result.getVal("oauth")->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::tokenRefreshed") ||
|
||||
!Networking::HttpJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::tokenRefreshed", !requiresRefreshToken)) {
|
||||
warning("BaseStorage: bad response, no 'access_token' or 'refresh_token' attribute passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
debug(9, "%s", json->stringify(true).c_str()); // TODO: remove when done testing against cloud.scummvm.org
|
||||
|
||||
_token = oauth.getVal("access_token")->asString();
|
||||
if (requiresRefreshToken) {
|
||||
_refreshToken = oauth.getVal("refresh_token")->asString();
|
||||
}
|
||||
CloudMan.save(); //ask CloudManager to save our new access_token and refresh_token
|
||||
}
|
||||
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, success));
|
||||
delete json;
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void BaseStorage::saveIsEnabledFlag(const Common::String &keyPrefix) const {
|
||||
ConfMan.set(keyPrefix + "enabled", _isEnabled ? "true" : "false", ConfMan.kCloudDomain);
|
||||
}
|
||||
|
||||
bool BaseStorage::loadIsEnabledFlag(const Common::String &keyPrefix) {
|
||||
if (!ConfMan.hasKey(keyPrefix + "enabled", ConfMan.kCloudDomain))
|
||||
return false;
|
||||
|
||||
Common::String enabled = ConfMan.get(keyPrefix + "enabled", ConfMan.kCloudDomain);
|
||||
return (enabled == "true");
|
||||
}
|
||||
|
||||
void BaseStorage::removeIsEnabledFlag(const Common::String &keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "enabled", ConfMan.kCloudDomain);
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
103
backends/cloud/basestorage.h
Normal file
103
backends/cloud/basestorage.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_BASE_STORAGE_H
|
||||
#define BACKENDS_CLOUD_BASE_STORAGE_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
class BaseStorage: public Cloud::Storage {
|
||||
protected:
|
||||
/** Storage's access and refresh tokens. */
|
||||
Common::String _token, _refreshToken;
|
||||
|
||||
/**
|
||||
* Gets token from cloud.scummvm.org using given code.
|
||||
* Base implementation for storages with common auth procedure.
|
||||
*/
|
||||
virtual void getAccessToken(const Common::String &code, Networking::ErrorCallback callback);
|
||||
|
||||
/**
|
||||
* Handles JSON response which should contain access token requested
|
||||
* with getAccessToken().
|
||||
*/
|
||||
virtual void codeFlowComplete(Networking::ErrorCallback callback, const Networking::JsonResponse &response);
|
||||
|
||||
/**
|
||||
* Handles network errors occurred while getting access token requested
|
||||
* with getAccessToken().
|
||||
*/
|
||||
virtual void codeFlowFailed(Networking::ErrorCallback callback, const Networking::ErrorResponse &error);
|
||||
|
||||
/**
|
||||
* Return cloud provider name, used in cloud.scummvm.org endpoints.
|
||||
* @return cloud provider (for example, "dropbox").
|
||||
*/
|
||||
virtual Common::String cloudProvider() = 0;
|
||||
|
||||
/**
|
||||
* Return CloudManager's StorageID for this storage.
|
||||
* @return StorageID corresponding to this storage (for example,
|
||||
* kStorageDropboxId).
|
||||
*/
|
||||
virtual uint32 storageIndex() = 0;
|
||||
|
||||
/**
|
||||
* Return whether storage needs refresh_token to work.
|
||||
*/
|
||||
virtual bool needsRefreshToken() = 0;
|
||||
|
||||
/**
|
||||
* Return whether to expect new refresh_token on refresh.
|
||||
*/
|
||||
virtual bool canReuseRefreshToken() = 0;
|
||||
|
||||
private:
|
||||
void tokenRefreshed(BoolCallback callback, const Networking::JsonResponse &response);
|
||||
|
||||
protected:
|
||||
/** Helper function to save Storage::_isEnabled into config. */
|
||||
void saveIsEnabledFlag(const Common::String &keyPrefix) const;
|
||||
|
||||
/** Helper function to load Storage::_isEnabled value from config. */
|
||||
static bool loadIsEnabledFlag(const Common::String &keyPrefix);
|
||||
|
||||
/** Helper function to remove Storage::_isEnabled from config. */
|
||||
static void removeIsEnabledFlag(const Common::String &keyPrefix);
|
||||
|
||||
public:
|
||||
BaseStorage();
|
||||
BaseStorage(const Common::String &token, const Common::String &refreshToken, bool enabled = false);
|
||||
~BaseStorage() override;
|
||||
|
||||
/**
|
||||
* Gets new access_token. Pass a callback, so you could
|
||||
* continue your work when new token is available.
|
||||
*/
|
||||
virtual void refreshAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr);
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
194
backends/cloud/box/boxlistdirectorybyidrequest.cpp
Normal file
194
backends/cloud/box/boxlistdirectorybyidrequest.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/box/boxlistdirectorybyidrequest.h"
|
||||
#include "backends/cloud/box/boxstorage.h"
|
||||
#include "backends/cloud/box/boxtokenrefresher.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
#define BOX_LIST_DIRECTORY_LIMIT 1000
|
||||
#define BOX_FOLDERS_API_LINK "https://api.box.com/2.0/folders/%s/items?offset=%u&limit=%u&fields=%s"
|
||||
|
||||
BoxListDirectoryByIdRequest::BoxListDirectoryByIdRequest(BoxStorage *storage, const Common::String &id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
BoxListDirectoryByIdRequest::~BoxListDirectoryByIdRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest) _workingRequest->finish();
|
||||
delete _listDirectoryCallback;
|
||||
}
|
||||
|
||||
void BoxListDirectoryByIdRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest) _workingRequest->finish();
|
||||
_files.clear();
|
||||
_ignoreCallback = false;
|
||||
|
||||
makeRequest(0);
|
||||
}
|
||||
|
||||
void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
|
||||
Common::String url = Common::String::format(
|
||||
BOX_FOLDERS_API_LINK,
|
||||
_requestedId.c_str(),
|
||||
offset,
|
||||
BOX_LIST_DIRECTORY_LIMIT,
|
||||
"id,type,name,size,modified_at"
|
||||
);
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<BoxListDirectoryByIdRequest, const Networking::JsonResponse &>(this, &BoxListDirectoryByIdRequest::responseCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<BoxListDirectoryByIdRequest, const Networking::ErrorResponse &>(this, &BoxListDirectoryByIdRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void BoxListDirectoryByIdRequest::responseCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete response.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "BoxListDirectoryByIdRequest::responseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject responseObject = json->asObject();
|
||||
//debug(9, "%s", json->stringify(true).c_str());
|
||||
|
||||
//TODO: handle error messages passed as JSON
|
||||
/*
|
||||
if (responseObject.contains("error") || responseObject.contains("error_summary")) {
|
||||
warning("Box returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
|
||||
error.failed = true;
|
||||
error.response = json->stringify();
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
|
||||
if (responseObject.contains("entries")) {
|
||||
if (!responseObject.getVal("entries")->isArray()) {
|
||||
error.response = Common::String::format(
|
||||
"\"entries\" found, but that's not an array!\n%s",
|
||||
responseObject.getVal("entries")->stringify(true).c_str()
|
||||
);
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONArray items = responseObject.getVal("entries")->asArray();
|
||||
for (uint32 i = 0; i < items.size(); ++i) {
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
|
||||
|
||||
Common::JSONObject item = items[i]->asObject();
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
|
||||
|
||||
Common::String id = item.getVal("id")->asString();
|
||||
Common::String name = item.getVal("name")->asString();
|
||||
bool isDirectory = (item.getVal("type")->asString() == "folder");
|
||||
uint32 size;
|
||||
if (item.getVal("size")->isString()) {
|
||||
size = item.getVal("size")->asString().asUint64();
|
||||
} else {
|
||||
size = item.getVal("size")->asIntegerNumber();
|
||||
}
|
||||
uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
|
||||
|
||||
//as we list directory by id, we can't determine full path for the file, so we leave it empty
|
||||
_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
uint32 received = 0;
|
||||
uint32 totalCount = 0;
|
||||
if (responseObject.contains("total_count") && responseObject.getVal("total_count")->isIntegerNumber())
|
||||
totalCount = responseObject.getVal("total_count")->asIntegerNumber();
|
||||
if (responseObject.contains("offset") && responseObject.getVal("offset")->isIntegerNumber())
|
||||
received = responseObject.getVal("offset")->asIntegerNumber();
|
||||
if (responseObject.contains("limit") && responseObject.getVal("limit")->isIntegerNumber())
|
||||
received += responseObject.getVal("limit")->asIntegerNumber();
|
||||
bool hasMore = (received < totalCount);
|
||||
|
||||
if (hasMore) makeRequest(received);
|
||||
else finishListing(_files);
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void BoxListDirectoryByIdRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
if (error.request) _date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void BoxListDirectoryByIdRequest::handle() {}
|
||||
|
||||
void BoxListDirectoryByIdRequest::restart() { start(); }
|
||||
|
||||
Common::String BoxListDirectoryByIdRequest::date() const { return _date; }
|
||||
|
||||
void BoxListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
|
||||
}
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
62
backends/cloud/box/boxlistdirectorybyidrequest.h
Normal file
62
backends/cloud/box/boxlistdirectorybyidrequest.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYBYIDREQUEST_H
|
||||
#define BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYBYIDREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
class BoxStorage;
|
||||
|
||||
class BoxListDirectoryByIdRequest: public Networking::Request {
|
||||
Common::String _requestedId;
|
||||
BoxStorage *_storage;
|
||||
|
||||
Storage::ListDirectoryCallback _listDirectoryCallback;
|
||||
Common::Array<StorageFile> _files;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void makeRequest(uint32 offset);
|
||||
void responseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishListing(Common::Array<StorageFile> &files);
|
||||
public:
|
||||
BoxListDirectoryByIdRequest(BoxStorage *storage, const Common::String &id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb);
|
||||
~BoxListDirectoryByIdRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
243
backends/cloud/box/boxstorage.cpp
Normal file
243
backends/cloud/box/boxstorage.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/box/boxstorage.h"
|
||||
#include "backends/cloud/box/boxlistdirectorybyidrequest.h"
|
||||
#include "backends/cloud/box/boxtokenrefresher.h"
|
||||
#include "backends/cloud/box/boxuploadrequest.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
#define BOX_API_FOLDERS "https://api.box.com/2.0/folders"
|
||||
#define BOX_API_FILES_CONTENT "https://api.box.com/2.0/files/%s/content"
|
||||
#define BOX_API_USERS_ME "https://api.box.com/2.0/users/me"
|
||||
|
||||
BoxStorage::BoxStorage(const Common::String &token, const Common::String &refreshToken, bool enabled):
|
||||
IdStorage(token, refreshToken, enabled) {}
|
||||
|
||||
BoxStorage::BoxStorage(const Common::String &code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
BoxStorage::BoxStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb) {
|
||||
codeFlowComplete(cb, codeFlowJson);
|
||||
}
|
||||
|
||||
BoxStorage::~BoxStorage() {}
|
||||
|
||||
Common::String BoxStorage::cloudProvider() { return "box"; }
|
||||
|
||||
uint32 BoxStorage::storageIndex() { return kStorageBoxId; }
|
||||
|
||||
bool BoxStorage::needsRefreshToken() { return true; }
|
||||
|
||||
bool BoxStorage::canReuseRefreshToken() { return false; }
|
||||
|
||||
void BoxStorage::saveConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String BoxStorage::name() const {
|
||||
return "Box";
|
||||
}
|
||||
|
||||
void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("BoxStorage::infoInnerCallback: NULL passed instead of JSON");
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject jsonInfo = json->asObject();
|
||||
|
||||
Common::String uid, displayName, email;
|
||||
uint64 quotaUsed = 0, quotaAllocated = 0;
|
||||
|
||||
// can check that "type": "user"
|
||||
// there is also "max_upload_size", "phone" and "avatar_url"
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
|
||||
uid = jsonInfo.getVal("id")->asString();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
|
||||
displayName = jsonInfo.getVal("name")->asString();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
|
||||
email = jsonInfo.getVal("login")->asString();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
|
||||
quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
|
||||
quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber();
|
||||
|
||||
Common::String username = email;
|
||||
if (username == "") username = displayName;
|
||||
if (username == "") username = uid;
|
||||
CloudMan.setStorageUsername(kStorageBoxId, username);
|
||||
|
||||
if (outerCallback) {
|
||||
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::listDirectoryById(const Common::String &id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
if (!callback)
|
||||
callback = getPrintFilesCallback();
|
||||
return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
|
||||
}
|
||||
|
||||
void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("BoxStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (outerCallback) {
|
||||
if (Networking::HttpJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
|
||||
Common::JSONObject jsonInfo = json->asObject();
|
||||
(*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
|
||||
} else {
|
||||
(*outerCallback)(BoolResponse(nullptr, false));
|
||||
}
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::createDirectoryWithParentId(const Common::String &parentId, const Common::String &directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
|
||||
Common::String url = BOX_API_FOLDERS;
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, const BoolResponse &, const Networking::JsonResponse &>(this, &BoxStorage::createDirectoryInnerCallback, callback);
|
||||
Networking::HttpJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONObject parentObject;
|
||||
parentObject.setVal("id", new Common::JSONValue(parentId));
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
|
||||
jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::upload(const Common::String &remotePath, const Common::Path &localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new BoxUploadRequest(this, remotePath, localPath, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
warning("BoxStorage::upload(ReadStream) not implemented");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "BoxStorage::upload(ReadStream) not implemented", -1));
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BoxStorage::uploadStreamSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (callback) {
|
||||
Common::String url = Common::String::format(BOX_API_FILES_CONTENT, id.c_str());
|
||||
Common::String header = "Authorization: Bearer " + _token;
|
||||
Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
|
||||
headersList->push_back(header);
|
||||
Networking::NetworkReadStream *stream = Networking::NetworkReadStream::make(url.c_str(), headersList, "");
|
||||
(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
|
||||
}
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &BoxStorage::infoInnerCallback, callback);
|
||||
Networking::HttpJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, BOX_API_USERS_ME);
|
||||
request->addHeader("Authorization: Bearer " + _token);
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; }
|
||||
|
||||
BoxStorage *BoxStorage::loadFromConfig(const Common::String &keyPrefix) {
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("BoxStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
|
||||
warning("BoxStorage: no refresh_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new BoxStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void BoxStorage::removeFromConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String BoxStorage::getRootDirectoryId() {
|
||||
return "0";
|
||||
}
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
123
backends/cloud/box/boxstorage.h
Normal file
123
backends/cloud/box/boxstorage.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_BOX_BOXSTORAGE_H
|
||||
#define BACKENDS_CLOUD_BOX_BOXSTORAGE_H
|
||||
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
class BoxStorage: public Id::IdStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
BoxStorage(const Common::String &token, const Common::String &refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &json);
|
||||
|
||||
void createDirectoryInnerCallback(BoolCallback outerCallback, const Networking::JsonResponse &response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "box"
|
||||
*/
|
||||
Common::String cloudProvider() override;
|
||||
|
||||
/**
|
||||
* @return kStorageBoxId
|
||||
*/
|
||||
uint32 storageIndex() override;
|
||||
|
||||
bool needsRefreshToken() override;
|
||||
|
||||
bool canReuseRefreshToken() override;
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
BoxStorage(const Common::String &code, Networking::ErrorCallback cb);
|
||||
|
||||
/** This constructor extracts tokens from JSON acquired via OAuth code flow. */
|
||||
BoxStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb);
|
||||
|
||||
~BoxStorage() override;
|
||||
|
||||
/**
|
||||
* Storage methods, which are used by CloudManager to save
|
||||
* storage in configuration file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save storage data using ConfMan.
|
||||
* @param keyPrefix all saved keys must start with this prefix.
|
||||
* @note every Storage must write keyPrefix + "type" key
|
||||
* with common value (e.g. "Dropbox").
|
||||
*/
|
||||
void saveConfig(const Common::String &keyPrefix) override;
|
||||
|
||||
/**
|
||||
* Return unique storage name.
|
||||
* @returns some unique storage name (for example, "Dropbox (user@example.com)")
|
||||
*/
|
||||
Common::String name() const override;
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
||||
Networking::Request *listDirectoryById(const Common::String &id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
Networking::Request *createDirectoryWithParentId(const Common::String &parentId, const Common::String &directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns UploadStatus struct with info about uploaded file. */
|
||||
Networking::Request *upload(const Common::String &remotePath, const Common::Path &localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
Networking::Request *upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns whether Storage supports upload(ReadStream). */
|
||||
bool uploadStreamSupported() override;
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
Networking::Request *streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns the StorageInfo struct. */
|
||||
Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath() override;
|
||||
|
||||
/**
|
||||
* Load token and user id from configs and return BoxStorage for those.
|
||||
* @return pointer to the newly created BoxStorage or 0 if some problem occurred.
|
||||
*/
|
||||
static BoxStorage *loadFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
/**
|
||||
* Remove all BoxStorage-related data from config.
|
||||
*/
|
||||
static void removeFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
Common::String getRootDirectoryId() override;
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
120
backends/cloud/box/boxtokenrefresher.cpp
Normal file
120
backends/cloud/box/boxtokenrefresher.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/box/boxtokenrefresher.h"
|
||||
#include "backends/cloud/box/boxstorage.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
BoxTokenRefresher::BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
|
||||
HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
|
||||
|
||||
BoxTokenRefresher::~BoxTokenRefresher() {}
|
||||
|
||||
void BoxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
|
||||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("BoxTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "BoxTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//update headers: first change header with token, then pass those to request
|
||||
for (uint32 i = 0; i < _headersList.size(); ++i) {
|
||||
if (_headersList[i].contains("Authorization")) {
|
||||
_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
|
||||
}
|
||||
}
|
||||
|
||||
//successfully received refreshed token, can restart the original request now
|
||||
retry(0);
|
||||
}
|
||||
|
||||
void BoxTokenRefresher::finishJson(const Common::JSONValue *json) {
|
||||
if (!json) {
|
||||
//that's probably not an error (200 OK)
|
||||
HttpJsonRequest::finishJson(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonIsObject(json, "BoxTokenRefresher")) {
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (result.contains("type") && result.getVal("type")->isString() && result.getVal("type")->asString() == "error") {
|
||||
//new token needed => request token & then retry original request
|
||||
long httpCode = -1;
|
||||
if (_stream) {
|
||||
httpCode = _stream->httpResponseCode();
|
||||
debug(9, "BoxTokenRefresher: code %ld", httpCode);
|
||||
}
|
||||
|
||||
bool irrecoverable = true;
|
||||
|
||||
Common::String code, message;
|
||||
if (jsonContainsString(result, "code", "BoxTokenRefresher")) {
|
||||
code = result.getVal("code")->asString();
|
||||
debug(9, "BoxTokenRefresher: code = %s", code.c_str());
|
||||
}
|
||||
|
||||
if (jsonContainsString(result, "message", "BoxTokenRefresher")) {
|
||||
message = result.getVal("message")->asString();
|
||||
debug(9, "BoxTokenRefresher: message = %s", message.c_str());
|
||||
}
|
||||
|
||||
//TODO: decide when token refreshment will help
|
||||
//for now refreshment is used only when HTTP 401 is passed in finishError()
|
||||
//if (code == "unauthenticated") irrecoverable = false;
|
||||
|
||||
if (irrecoverable) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<BoxTokenRefresher, const Storage::BoolResponse &>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//notify user of success
|
||||
HttpJsonRequest::finishJson(json);
|
||||
}
|
||||
|
||||
void BoxTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
if (error.httpResponseCode == 401) { // invalid_token
|
||||
pause();
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<BoxTokenRefresher, const Storage::BoolResponse &>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
|
||||
// there are also 400 == invalid_request and 403 == insufficient_scope
|
||||
// but TokenRefresher is there to refresh token when it's invalid only
|
||||
|
||||
Request::finishError(error);
|
||||
}
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
48
backends/cloud/box/boxtokenrefresher.h
Normal file
48
backends/cloud/box/boxtokenrefresher.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_BOX_BOXTOKENREFRESHER_H
|
||||
#define BACKENDS_CLOUD_BOX_BOXTOKENREFRESHER_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
class BoxStorage;
|
||||
|
||||
class BoxTokenRefresher: public Networking::HttpJsonRequest {
|
||||
BoxStorage *_parentStorage;
|
||||
|
||||
void tokenRefreshed(const Storage::BoolResponse &response);
|
||||
|
||||
void finishJson(const Common::JSONValue *json) override;
|
||||
void finishError(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED) override;
|
||||
public:
|
||||
BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
|
||||
~BoxTokenRefresher() override;
|
||||
};
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
231
backends/cloud/box/boxuploadrequest.cpp
Normal file
231
backends/cloud/box/boxuploadrequest.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/box/boxuploadrequest.h"
|
||||
#include "backends/cloud/box/boxstorage.h"
|
||||
#include "backends/cloud/box/boxtokenrefresher.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
#define BOX_API_FILES "https://upload.box.com/api/2.0/files"
|
||||
|
||||
BoxUploadRequest::BoxUploadRequest(BoxStorage *storage, const Common::String &path, const Common::Path &localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _localPath(localPath), _uploadCallback(callback),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
BoxUploadRequest::~BoxUploadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _uploadCallback;
|
||||
}
|
||||
|
||||
void BoxUploadRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_resolvedId = ""; //used to update file contents
|
||||
_parentId = ""; //used to create file within parent directory
|
||||
_ignoreCallback = false;
|
||||
|
||||
resolveId();
|
||||
}
|
||||
|
||||
void BoxUploadRequest::resolveId() {
|
||||
//check whether such file already exists
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<BoxUploadRequest, const Storage::UploadResponse &>(this, &BoxUploadRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<BoxUploadRequest, const Networking::ErrorResponse &>(this, &BoxUploadRequest::idResolveFailedCallback);
|
||||
_workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void BoxUploadRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
_resolvedId = response.value.id();
|
||||
upload();
|
||||
}
|
||||
|
||||
void BoxUploadRequest::idResolveFailedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
|
||||
//not resolved => error or no such file
|
||||
if (error.response.contains("no such file found in its parent directory")) {
|
||||
//parent's id after the '\n'
|
||||
Common::String parentId = error.response;
|
||||
for (uint32 i = 0; i < parentId.size(); ++i)
|
||||
if (parentId[i] == '\n') {
|
||||
parentId.erase(0, i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
_parentId = parentId;
|
||||
upload();
|
||||
return;
|
||||
}
|
||||
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void BoxUploadRequest::upload() {
|
||||
Common::String name = _savePath;
|
||||
for (uint32 i = name.size(); i > 0; --i) {
|
||||
if (name[i - 1] == '/' || name[i - 1] == '\\') {
|
||||
name.erase(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String url = BOX_API_FILES;
|
||||
if (_resolvedId != "")
|
||||
url += "/" + _resolvedId;
|
||||
url += "/content";
|
||||
Networking::JsonCallback callback = new Common::Callback<BoxUploadRequest, const Networking::JsonResponse &>(this, &BoxUploadRequest::uploadedCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<BoxUploadRequest, const Networking::ErrorResponse &>(this, &BoxUploadRequest::notUploadedCallback);
|
||||
Networking::HttpJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
if (_resolvedId == "") {
|
||||
Common::JSONObject parentObject;
|
||||
parentObject.setVal("id", new Common::JSONValue(_parentId));
|
||||
jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
|
||||
jsonRequestParameters.setVal("name", new Common::JSONValue(name));
|
||||
}
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addFormField("attributes", Common::JSON::stringify(&value));
|
||||
request->addFormFile("file", _localPath);
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void BoxUploadRequest::uploadedCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "", -1);
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq) {
|
||||
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
|
||||
if (stream) {
|
||||
long code = stream->httpResponseCode();
|
||||
error.httpResponseCode = code;
|
||||
}
|
||||
}
|
||||
|
||||
if (error.httpResponseCode != 200 && error.httpResponseCode != 201)
|
||||
warning("BoxUploadRequest: looks like an error (bad HTTP code)");
|
||||
|
||||
//check JSON and show warnings if it's malformed
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject object = json->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsArray(object, "entries", "BoxUploadRequest")) {
|
||||
Common::JSONArray entries = object.getVal("entries")->asArray();
|
||||
if (entries.size() == 0) {
|
||||
warning("BoxUploadRequest: 'entries' found, but it's empty");
|
||||
} else if (!Networking::HttpJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
|
||||
warning("BoxUploadRequest: 'entries' first item is not an object");
|
||||
} else {
|
||||
Common::JSONObject item = entries[0]->asObject();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(item, "id", "BoxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(item, "name", "BoxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(item, "type", "BoxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(item, "modified_at", "BoxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxUploadRequest")) {
|
||||
|
||||
//finished
|
||||
Common::String id = item.getVal("id")->asString();
|
||||
Common::String name = item.getVal("name")->asString();
|
||||
bool isDirectory = (item.getVal("type")->asString() == "folder");
|
||||
uint32 size;
|
||||
if (item.getVal("size")->isString()) {
|
||||
size = item.getVal("size")->asString().asUint64();
|
||||
} else {
|
||||
size = item.getVal("size")->asIntegerNumber();
|
||||
}
|
||||
uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("modified_at")->asString());
|
||||
|
||||
finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check errors
|
||||
/*
|
||||
if (object.contains("error")) {
|
||||
warning("Box returned error: %s", json->stringify(true).c_str());
|
||||
delete json;
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
warning("BoxUploadRequest: no file info to return");
|
||||
finishUpload(StorageFile(_savePath, 0, 0, false));
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void BoxUploadRequest::notUploadedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void BoxUploadRequest::handle() {}
|
||||
|
||||
void BoxUploadRequest::restart() { start(); }
|
||||
|
||||
void BoxUploadRequest::finishUpload(const StorageFile &file) {
|
||||
Request::finishSuccess();
|
||||
if (_uploadCallback)
|
||||
(*_uploadCallback)(Storage::UploadResponse(this, file));
|
||||
}
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
63
backends/cloud/box/boxuploadrequest.h
Normal file
63
backends/cloud/box/boxuploadrequest.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
class BoxStorage;
|
||||
|
||||
class BoxUploadRequest: public Networking::Request {
|
||||
BoxStorage *_storage;
|
||||
Common::String _savePath;
|
||||
Common::Path _localPath;
|
||||
Storage::UploadCallback _uploadCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _resolvedId, _parentId;
|
||||
|
||||
void start();
|
||||
void resolveId();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveFailedCallback(const Networking::ErrorResponse &error);
|
||||
void upload();
|
||||
void uploadedCallback(const Networking::JsonResponse &response);
|
||||
void notUploadedCallback(const Networking::ErrorResponse &error);
|
||||
void finishUpload(const StorageFile &status);
|
||||
|
||||
public:
|
||||
BoxUploadRequest(BoxStorage *storage, const Common::String &path, const Common::Path &localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
|
||||
~BoxUploadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace Box
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
186
backends/cloud/cloudicon.cpp
Normal file
186
backends/cloud/cloudicon.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/cloudicon.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "image/png.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
const float CloudIcon::ALPHA_SPEED = 0.0005f;
|
||||
const float CloudIcon::ALPHA_MAX = 1.f;
|
||||
const float CloudIcon::ALPHA_MIN = 0.6f;
|
||||
|
||||
CloudIcon::CloudIcon() {
|
||||
initIcons();
|
||||
hide();
|
||||
_lastUpdateTime = g_system->getMillis();
|
||||
}
|
||||
|
||||
CloudIcon::~CloudIcon() {
|
||||
_icon.free();
|
||||
_disabledIcon.free();
|
||||
_alphaIcon.free();
|
||||
}
|
||||
|
||||
void CloudIcon::show(CloudIcon::Type icon, int duration) {
|
||||
if (_type == icon) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
if (icon != kNone) {
|
||||
_state = kShown;
|
||||
_type = icon;
|
||||
|
||||
if (duration) {
|
||||
_hideTime = g_system->getMillis() + duration;
|
||||
} else {
|
||||
_hideTime = 0;
|
||||
}
|
||||
} else {
|
||||
_state = kGoingToHide;
|
||||
}
|
||||
}
|
||||
|
||||
void CloudIcon::hide() {
|
||||
_state = kHidden;
|
||||
_type = kNone;
|
||||
_hideTime = 0;
|
||||
_currentAlpha = 0;
|
||||
_alphaRising = true;
|
||||
}
|
||||
|
||||
CloudIcon::Type CloudIcon::getShownType() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
bool CloudIcon::needsUpdate() const {
|
||||
uint32 delaySinceLastUpdate = g_system->getMillis(true) - _lastUpdateTime;
|
||||
return delaySinceLastUpdate >= UPDATE_DELAY_MIN_MILLIS;
|
||||
}
|
||||
|
||||
void CloudIcon::update() {
|
||||
uint32 currentTime = g_system->getMillis(true);
|
||||
uint32 delaySinceLastUpdate = currentTime - _lastUpdateTime;
|
||||
_lastUpdateTime = currentTime;
|
||||
|
||||
switch (_state) {
|
||||
default:
|
||||
// fallthrough intended
|
||||
case kHidden:
|
||||
return; // Nothing to do
|
||||
case kShown:
|
||||
if (_alphaRising) {
|
||||
if (_currentAlpha < ALPHA_MIN)
|
||||
_currentAlpha += 5 * ALPHA_SPEED * delaySinceLastUpdate;
|
||||
else
|
||||
_currentAlpha += ALPHA_SPEED * delaySinceLastUpdate;
|
||||
if (_currentAlpha > ALPHA_MAX) {
|
||||
_currentAlpha = ALPHA_MAX;
|
||||
_alphaRising = false;
|
||||
}
|
||||
} else {
|
||||
_currentAlpha -= ALPHA_SPEED * delaySinceLastUpdate;
|
||||
if (_currentAlpha < ALPHA_MIN) {
|
||||
_currentAlpha = ALPHA_MIN;
|
||||
_alphaRising = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hideTime != 0 && _hideTime <= currentTime) {
|
||||
_hideTime = 0;
|
||||
_state = kGoingToHide;
|
||||
}
|
||||
break;
|
||||
case kGoingToHide:
|
||||
_currentAlpha -= 5 * ALPHA_SPEED * delaySinceLastUpdate;
|
||||
if (_currentAlpha <= 0) {
|
||||
hide();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_icon.getPixels() || !_disabledIcon.getPixels()) {
|
||||
// Loading the icons failed. Don't try to draw them.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state != kHidden) {
|
||||
makeAlphaIcon((_type == kDisabled ? _disabledIcon : _icon), _currentAlpha);
|
||||
g_system->displayActivityIconOnOSD(&_alphaIcon);
|
||||
} else {
|
||||
g_system->displayActivityIconOnOSD(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
#include "backends/cloud/cloudicon_data.h"
|
||||
#include "backends/cloud/cloudicon_disabled_data.h"
|
||||
|
||||
void CloudIcon::initIcons() {
|
||||
loadIcon(_icon, cloudicon_data, ARRAYSIZE(cloudicon_data));
|
||||
loadIcon(_disabledIcon, cloudicon_disabled_data, ARRAYSIZE(cloudicon_disabled_data));
|
||||
}
|
||||
|
||||
void CloudIcon::loadIcon(Graphics::Surface &icon, const byte *data, uint32 size) {
|
||||
Image::PNGDecoder decoder;
|
||||
Common::MemoryReadStream stream(data, size);
|
||||
if (!decoder.loadStream(stream)) {
|
||||
warning("CloudIcon::loadIcon: error decoding PNG");
|
||||
return;
|
||||
}
|
||||
|
||||
const Graphics::Surface *s = decoder.getSurface();
|
||||
icon.copyFrom(*s);
|
||||
}
|
||||
|
||||
void CloudIcon::makeAlphaIcon(const Graphics::Surface &icon, float alpha) {
|
||||
_alphaIcon.copyFrom(icon);
|
||||
|
||||
byte *pixels = (byte *)_alphaIcon.getPixels();
|
||||
for (int y = 0; y < _alphaIcon.h; y++) {
|
||||
byte *row = pixels + y * _alphaIcon.pitch;
|
||||
for (int x = 0; x < _alphaIcon.w; x++) {
|
||||
uint32 srcColor;
|
||||
if (_alphaIcon.format.bytesPerPixel == 2)
|
||||
srcColor = READ_UINT16(row);
|
||||
else if (_alphaIcon.format.bytesPerPixel == 3)
|
||||
srcColor = READ_UINT24(row);
|
||||
else
|
||||
srcColor = READ_UINT32(row);
|
||||
|
||||
// Update color's alpha
|
||||
byte r, g, b, a;
|
||||
_alphaIcon.format.colorToARGB(srcColor, a, r, g, b);
|
||||
a = (byte)(a * alpha);
|
||||
uint32 color = _alphaIcon.format.ARGBToColor(a, r, g, b);
|
||||
|
||||
if (_alphaIcon.format.bytesPerPixel == 2)
|
||||
*((uint16 *)row) = color;
|
||||
else
|
||||
*((uint32 *)row) = color;
|
||||
|
||||
row += _alphaIcon.format.bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
89
backends/cloud/cloudicon.h
Normal file
89
backends/cloud/cloudicon.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_CLOUDICON_H
|
||||
#define BACKENDS_CLOUD_CLOUDICON_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
class CloudIcon {
|
||||
public:
|
||||
CloudIcon();
|
||||
~CloudIcon();
|
||||
|
||||
/**
|
||||
* The type of cloud icon to show
|
||||
*/
|
||||
enum Type {
|
||||
kNone, /** Hide the currently shown icon if any */
|
||||
kSyncing, /** Cloud syncing icon */
|
||||
kDisabled /** Cloud syncing not available icon */
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the icon to show on the OSD
|
||||
*
|
||||
* @param icon Icon type to show. Use kNone to hide the current icon if any.
|
||||
* @param duration Duration in milliseconds the icon stays visible on screen. 0 means the icon stays indefinitely.
|
||||
*/
|
||||
void show(Type icon, int duration = 0);
|
||||
|
||||
/** The currently visible icon. kNone means no icon is shown. */
|
||||
Type getShownType() const;
|
||||
|
||||
/** Returns true if the icon state needs to be checked for changes */
|
||||
bool needsUpdate() const;
|
||||
|
||||
/** Update the icon visible on the OSD */
|
||||
void update();
|
||||
|
||||
private:
|
||||
static const float ALPHA_SPEED, ALPHA_MAX, ALPHA_MIN;
|
||||
static const int UPDATE_DELAY_MIN_MILLIS = 10;
|
||||
|
||||
enum State {
|
||||
kHidden,
|
||||
kShown,
|
||||
kGoingToHide
|
||||
};
|
||||
|
||||
State _state;
|
||||
Type _type;
|
||||
|
||||
Graphics::Surface _icon, _disabledIcon, _alphaIcon;
|
||||
float _currentAlpha;
|
||||
bool _alphaRising;
|
||||
|
||||
uint32 _hideTime;
|
||||
uint32 _lastUpdateTime;
|
||||
|
||||
void initIcons();
|
||||
void loadIcon(Graphics::Surface &icon, const byte *data, uint32 size);
|
||||
void makeAlphaIcon(const Graphics::Surface &icon, float alpha);
|
||||
|
||||
void hide();
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
110
backends/cloud/cloudicon_data.h
Normal file
110
backends/cloud/cloudicon_data.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// This is a PNG file dumped into array.
|
||||
// $ recode data..d1 <dists/cloudicon.png >cloudicon_data.h
|
||||
// The tool is from https://github.com/pinard/Recode
|
||||
|
||||
static const byte cloudicon_data[] = {
|
||||
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68,
|
||||
82, 0, 0, 0, 32, 0, 0, 0, 32, 8, 6, 0, 0, 0, 115,
|
||||
122, 122, 244, 0, 0, 0, 4, 115, 66, 73, 84, 8, 8, 8, 8,
|
||||
124, 8, 100, 136, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11,
|
||||
18, 0, 0, 11, 18, 1, 210, 221, 126, 252, 0, 0, 0, 22, 116,
|
||||
69, 88, 116, 67, 114, 101, 97, 116, 105, 111, 110, 32, 84, 105, 109,
|
||||
101, 0, 48, 54, 47, 48, 51, 47, 49, 54, 159, 192, 233, 192, 0,
|
||||
0, 0, 28, 116, 69, 88, 116, 83, 111, 102, 116, 119, 97, 114, 101,
|
||||
0, 65, 100, 111, 98, 101, 32, 70, 105, 114, 101, 119, 111, 114, 107,
|
||||
115, 32, 67, 83, 54, 232, 188, 178, 140, 0, 0, 4, 50, 73, 68,
|
||||
65, 84, 88, 133, 197, 151, 109, 104, 150, 101, 20, 199, 127, 247, 227,
|
||||
179, 105, 51, 23, 65, 181, 150, 224, 154, 214, 132, 194, 249, 33, 165,
|
||||
22, 189, 231, 194, 210, 250, 16, 171, 180, 55, 42, 152, 68, 65, 100,
|
||||
52, 233, 5, 146, 144, 144, 26, 249, 169, 62, 164, 80, 89, 152, 25,
|
||||
18, 226, 42, 49, 87, 88, 180, 94, 96, 96, 246, 234, 180, 70, 50,
|
||||
66, 214, 55, 247, 22, 133, 247, 255, 244, 225, 58, 247, 158, 107, 143,
|
||||
219, 243, 60, 186, 192, 3, 135, 251, 220, 215, 117, 223, 231, 127, 238,
|
||||
235, 252, 239, 235, 58, 39, 105, 250, 206, 56, 147, 146, 55, 85, 252,
|
||||
108, 13, 112, 59, 176, 12, 88, 2, 204, 7, 230, 248, 220, 48, 208,
|
||||
15, 244, 2, 221, 64, 23, 48, 86, 137, 211, 228, 146, 158, 178, 43,
|
||||
80, 7, 172, 3, 218, 129, 179, 43, 241, 9, 140, 0, 155, 129, 87,
|
||||
128, 193, 146, 15, 47, 248, 178, 100, 0, 237, 64, 39, 112, 14, 96,
|
||||
174, 20, 217, 153, 228, 162, 0, 50, 25, 2, 58, 128, 45, 83, 1,
|
||||
228, 53, 121, 10, 170, 129, 183, 128, 213, 126, 47, 215, 49, 224, 39,
|
||||
224, 19, 160, 7, 56, 2, 204, 0, 154, 128, 27, 128, 229, 110, 215,
|
||||
120, 64, 181, 132, 149, 184, 30, 120, 4, 248, 183, 24, 40, 185, 248,
|
||||
243, 147, 86, 160, 154, 144, 195, 91, 252, 43, 5, 140, 2, 31, 2,
|
||||
27, 129, 195, 83, 125, 141, 75, 19, 240, 2, 129, 47, 179, 61, 144,
|
||||
4, 216, 7, 172, 44, 14, 34, 105, 232, 62, 41, 128, 109, 192, 189,
|
||||
14, 126, 2, 24, 0, 30, 3, 246, 150, 1, 46, 150, 54, 15, 184,
|
||||
1, 200, 251, 216, 123, 192, 253, 19, 2, 152, 183, 119, 66, 0, 237,
|
||||
192, 27, 110, 159, 0, 250, 128, 187, 128, 67, 167, 8, 158, 201, 98,
|
||||
224, 3, 160, 209, 131, 72, 128, 53, 68, 156, 200, 153, 192, 181, 206,
|
||||
68, 167, 219, 50, 49, 96, 226, 78, 19, 135, 162, 103, 138, 117, 169,
|
||||
137, 46, 19, 163, 38, 82, 19, 63, 152, 120, 220, 196, 12, 159, 63,
|
||||
104, 98, 149, 137, 99, 238, 211, 28, 163, 46, 243, 145, 147, 192, 117,
|
||||
157, 68, 173, 219, 195, 18, 143, 74, 28, 137, 230, 139, 181, 77, 162,
|
||||
71, 98, 165, 68, 141, 68, 78, 98, 145, 196, 107, 18, 59, 252, 30,
|
||||
137, 3, 18, 207, 72, 140, 249, 125, 173, 99, 33, 65, 114, 209, 110,
|
||||
131, 192, 218, 65, 2, 105, 4, 108, 245, 165, 74, 125, 165, 206, 5,
|
||||
214, 2, 151, 3, 7, 128, 119, 128, 95, 253, 189, 169, 228, 105, 224,
|
||||
85, 183, 171, 128, 29, 192, 29, 4, 82, 142, 18, 246, 151, 177, 164,
|
||||
126, 151, 1, 220, 3, 188, 79, 32, 222, 40, 97, 167, 235, 243, 151,
|
||||
207, 2, 190, 5, 154, 35, 231, 223, 0, 45, 101, 242, 127, 12, 152,
|
||||
75, 97, 191, 88, 12, 124, 237, 254, 18, 96, 21, 176, 35, 227, 192,
|
||||
50, 207, 15, 38, 126, 49, 113, 56, 202, 243, 221, 38, 154, 139, 114,
|
||||
223, 82, 130, 23, 153, 214, 155, 88, 20, 221, 255, 104, 226, 104, 116,
|
||||
223, 106, 42, 144, 112, 169, 137, 196, 131, 248, 56, 10, 166, 222, 196,
|
||||
141, 21, 128, 77, 165, 223, 155, 120, 42, 34, 246, 158, 200, 247, 18,
|
||||
19, 228, 21, 178, 60, 223, 151, 41, 1, 190, 112, 251, 60, 224, 171,
|
||||
104, 238, 116, 36, 33, 240, 224, 32, 240, 25, 176, 31, 120, 194, 199,
|
||||
27, 161, 112, 26, 102, 167, 154, 1, 127, 186, 253, 98, 9, 240, 81,
|
||||
2, 97, 43, 149, 231, 61, 128, 129, 104, 108, 78, 28, 64, 44, 25,
|
||||
105, 218, 74, 56, 156, 13, 252, 76, 248, 43, 42, 145, 140, 176, 249,
|
||||
226, 137, 188, 133, 20, 12, 123, 68, 9, 225, 171, 127, 7, 46, 40,
|
||||
227, 180, 82, 112, 128, 153, 126, 189, 148, 194, 105, 57, 12, 5, 18,
|
||||
246, 71, 68, 185, 214, 237, 191, 166, 65, 190, 98, 237, 243, 235, 205,
|
||||
209, 88, 127, 188, 19, 246, 250, 53, 39, 177, 194, 237, 157, 37, 118,
|
||||
193, 83, 213, 183, 37, 102, 73, 92, 39, 145, 196, 152, 57, 75, 193,
|
||||
82, 246, 249, 21, 75, 89, 104, 41, 205, 150, 178, 222, 82, 250, 163,
|
||||
241, 211, 213, 253, 150, 178, 201, 82, 174, 180, 148, 185, 150, 146, 248,
|
||||
120, 183, 165, 133, 20, 116, 153, 24, 113, 123, 150, 137, 103, 77, 28,
|
||||
55, 113, 141, 137, 173, 38, 134, 60, 61, 167, 178, 236, 3, 38, 54,
|
||||
152, 184, 213, 255, 253, 39, 221, 55, 142, 213, 101, 42, 252, 5, 99,
|
||||
132, 202, 101, 45, 97, 175, 94, 14, 172, 0, 118, 1, 15, 185, 78,
|
||||
71, 86, 19, 138, 217, 196, 117, 179, 99, 146, 204, 126, 125, 188, 30,
|
||||
168, 35, 236, 255, 181, 132, 3, 233, 15, 224, 54, 202, 87, 64, 229,
|
||||
164, 5, 216, 9, 92, 232, 224, 67, 192, 66, 188, 88, 205, 69, 185,
|
||||
26, 180, 148, 14, 207, 81, 206, 82, 230, 89, 202, 110, 75, 185, 108,
|
||||
26, 249, 191, 202, 82, 222, 181, 148, 243, 163, 220, 119, 56, 22, 150,
|
||||
78, 172, 7, 144, 216, 34, 177, 205, 237, 188, 196, 2, 137, 61, 18,
|
||||
15, 158, 6, 243, 31, 144, 216, 37, 209, 224, 190, 18, 137, 237, 142,
|
||||
49, 254, 92, 50, 115, 211, 164, 69, 233, 71, 64, 43, 140, 23, 165,
|
||||
127, 19, 26, 142, 231, 8, 117, 192, 84, 82, 69, 56, 118, 215, 3,
|
||||
55, 17, 54, 160, 172, 40, 253, 148, 80, 168, 78, 44, 74, 171, 59,
|
||||
39, 237, 11, 170, 129, 55, 129, 251, 40, 108, 205, 2, 254, 1, 126,
|
||||
243, 96, 122, 129, 163, 62, 223, 0, 92, 237, 160, 141, 17, 112, 38,
|
||||
219, 129, 135, 139, 193, 1, 146, 170, 151, 43, 106, 76, 106, 139, 198,
|
||||
229, 192, 73, 20, 96, 12, 152, 177, 253, 56, 101, 26, 147, 36, 191,
|
||||
177, 226, 214, 108, 13, 133, 214, 172, 220, 75, 35, 14, 90, 190, 53,
|
||||
203, 189, 84, 113, 119, 156, 53, 167, 173, 192, 21, 252, 95, 205, 105,
|
||||
178, 225, 204, 182, 231, 255, 1, 200, 91, 112, 221, 160, 249, 68, 42,
|
||||
0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130
|
||||
};
|
||||
116
backends/cloud/cloudicon_disabled_data.h
Normal file
116
backends/cloud/cloudicon_disabled_data.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// This is a PNG file dumped into array.
|
||||
// $ recode data..d1 <dists/cloudicon_disabled.png >cloudicon_disabled_data.h
|
||||
// The tool is from https://github.com/pinard/Recode
|
||||
|
||||
static const byte cloudicon_disabled_data[] = {
|
||||
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68,
|
||||
82, 0, 0, 0, 32, 0, 0, 0, 32, 8, 6, 0, 0, 0, 115,
|
||||
122, 122, 244, 0, 0, 0, 4, 115, 66, 73, 84, 8, 8, 8, 8,
|
||||
124, 8, 100, 136, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11,
|
||||
18, 0, 0, 11, 18, 1, 210, 221, 126, 252, 0, 0, 0, 22, 116,
|
||||
69, 88, 116, 67, 114, 101, 97, 116, 105, 111, 110, 32, 84, 105, 109,
|
||||
101, 0, 48, 54, 47, 48, 51, 47, 49, 54, 159, 192, 233, 192, 0,
|
||||
0, 0, 28, 116, 69, 88, 116, 83, 111, 102, 116, 119, 97, 114, 101,
|
||||
0, 65, 100, 111, 98, 101, 32, 70, 105, 114, 101, 119, 111, 114, 107,
|
||||
115, 32, 67, 83, 54, 232, 188, 178, 140, 0, 0, 4, 139, 73, 68,
|
||||
65, 84, 88, 133, 197, 215, 91, 168, 86, 69, 20, 7, 240, 223, 254,
|
||||
244, 120, 236, 120, 57, 26, 94, 10, 31, 34, 35, 11, 36, 203, 74,
|
||||
212, 160, 204, 212, 110, 166, 248, 208, 205, 172, 151, 144, 136, 158, 82,
|
||||
80, 168, 32, 233, 165, 160, 32, 130, 236, 165, 34, 204, 74, 18, 52,
|
||||
36, 169, 232, 34, 24, 221, 243, 218, 133, 74, 77, 195, 44, 200, 91,
|
||||
122, 188, 156, 172, 102, 159, 221, 195, 204, 246, 219, 126, 231, 226, 17,
|
||||
138, 22, 12, 123, 246, 236, 153, 245, 95, 107, 205, 127, 205, 172, 157,
|
||||
21, 254, 95, 233, 27, 122, 63, 183, 5, 179, 48, 13, 87, 98, 52,
|
||||
6, 167, 111, 71, 176, 11, 27, 177, 14, 107, 209, 222, 27, 165, 217,
|
||||
137, 211, 207, 25, 137, 197, 152, 143, 65, 189, 209, 137, 163, 120, 1,
|
||||
79, 98, 111, 143, 147, 143, 247, 172, 108, 62, 158, 194, 16, 20, 169,
|
||||
105, 232, 151, 82, 171, 24, 80, 74, 27, 22, 225, 197, 110, 13, 104,
|
||||
235, 122, 188, 31, 94, 194, 93, 21, 192, 14, 49, 172, 223, 226, 109,
|
||||
124, 130, 29, 232, 131, 49, 184, 22, 55, 166, 126, 75, 131, 65, 175,
|
||||
225, 94, 252, 213, 201, 128, 131, 93, 131, 191, 137, 27, 42, 192, 199,
|
||||
241, 6, 158, 192, 246, 238, 188, 73, 50, 6, 143, 98, 78, 50, 164,
|
||||
140, 200, 123, 34, 135, 78, 49, 34, 219, 215, 89, 193, 171, 152, 151,
|
||||
192, 3, 246, 224, 1, 188, 123, 26, 224, 170, 92, 134, 143, 49, 160,
|
||||
50, 86, 96, 5, 238, 174, 78, 108, 204, 130, 249, 98, 216, 75, 240,
|
||||
109, 184, 13, 63, 156, 1, 248, 68, 44, 21, 189, 47, 165, 67, 140,
|
||||
196, 60, 172, 87, 225, 68, 182, 167, 62, 105, 100, 2, 106, 77, 11,
|
||||
118, 139, 123, 186, 163, 59, 164, 53, 76, 16, 195, 125, 29, 250, 15,
|
||||
227, 167, 73, 12, 237, 27, 73, 91, 75, 142, 28, 21, 185, 51, 60,
|
||||
141, 181, 225, 98, 41, 59, 106, 33, 185, 26, 88, 28, 104, 77, 253,
|
||||
163, 129, 251, 3, 59, 42, 223, 79, 105, 171, 184, 53, 240, 73, 224,
|
||||
150, 64, 203, 32, 106, 99, 185, 160, 224, 236, 16, 245, 118, 4, 54,
|
||||
5, 166, 4, 22, 6, 218, 211, 218, 214, 132, 37, 32, 75, 238, 181,
|
||||
224, 55, 12, 76, 222, 191, 140, 251, 144, 39, 79, 135, 98, 1, 198,
|
||||
98, 11, 150, 227, 251, 50, 204, 67, 113, 121, 90, 156, 57, 185, 127,
|
||||
135, 154, 184, 9, 95, 160, 9, 43, 49, 59, 69, 225, 24, 206, 65,
|
||||
123, 150, 54, 247, 14, 188, 158, 214, 30, 23, 79, 186, 109, 9, 252,
|
||||
44, 124, 142, 113, 149, 232, 127, 134, 201, 196, 88, 79, 66, 115, 5,
|
||||
252, 24, 182, 114, 224, 32, 35, 230, 212, 207, 139, 75, 241, 169, 168,
|
||||
47, 195, 157, 88, 89, 110, 193, 180, 64, 145, 250, 223, 5, 182, 87,
|
||||
194, 125, 123, 96, 92, 195, 22, 76, 14, 201, 227, 177, 226, 65, 144,
|
||||
227, 111, 28, 198, 151, 113, 131, 135, 5, 46, 169, 172, 249, 38, 176,
|
||||
187, 242, 62, 35, 160, 150, 199, 197, 19, 114, 178, 156, 34, 231, 173,
|
||||
244, 180, 154, 115, 115, 166, 166, 57, 167, 180, 193, 34, 3, 7, 165,
|
||||
61, 11, 137, 105, 27, 112, 160, 62, 111, 235, 106, 22, 166, 126, 71,
|
||||
206, 59, 165, 238, 156, 43, 114, 100, 155, 98, 120, 218, 146, 206, 2,
|
||||
83, 241, 225, 26, 134, 165, 253, 27, 173, 65, 134, 138, 137, 222, 154,
|
||||
222, 139, 228, 249, 87, 233, 217, 133, 76, 159, 19, 47, 169, 89, 226,
|
||||
129, 214, 71, 188, 192, 134, 148, 231, 64, 121, 171, 21, 248, 85, 244,
|
||||
232, 177, 30, 192, 59, 90, 82, 6, 193, 9, 108, 234, 30, 28, 30,
|
||||
9, 209, 128, 74, 214, 71, 204, 190, 121, 231, 201, 5, 228, 220, 218,
|
||||
248, 97, 136, 200, 246, 102, 106, 29, 234, 73, 190, 5, 135, 186, 7,
|
||||
135, 201, 9, 167, 111, 227, 135, 50, 2, 71, 146, 69, 153, 232, 245,
|
||||
206, 192, 136, 234, 196, 86, 49, 13, 154, 162, 113, 39, 211, 101, 51,
|
||||
126, 239, 25, 28, 154, 19, 206, 133, 234, 119, 195, 17, 234, 36, 220,
|
||||
85, 33, 202, 213, 169, 191, 175, 36, 92, 139, 152, 106, 3, 212, 9,
|
||||
119, 76, 100, 251, 126, 157, 9, 218, 69, 219, 150, 158, 211, 42, 99,
|
||||
187, 114, 245, 147, 112, 83, 122, 214, 2, 51, 83, 127, 85, 16, 89,
|
||||
62, 94, 60, 61, 202, 20, 58, 36, 38, 244, 126, 93, 159, 146, 93,
|
||||
180, 101, 129, 254, 129, 107, 2, 89, 26, 219, 24, 42, 6, 188, 95,
|
||||
153, 124, 81, 202, 251, 37, 3, 249, 101, 188, 120, 114, 148, 223, 219,
|
||||
197, 212, 56, 208, 123, 240, 245, 129, 167, 3, 19, 3, 163, 42, 6,
|
||||
172, 11, 234, 36, 92, 43, 242, 105, 32, 250, 227, 161, 89, 44, 45,
|
||||
98, 196, 139, 14, 178, 146, 112, 27, 210, 179, 23, 178, 7, 203, 240,
|
||||
248, 156, 152, 251, 15, 38, 221, 146, 138, 181, 212, 73, 216, 46, 214,
|
||||
112, 11, 82, 180, 103, 138, 71, 237, 40, 145, 52, 29, 216, 221, 194,
|
||||
220, 41, 49, 0, 103, 36, 129, 185, 152, 158, 116, 101, 9, 171, 29,
|
||||
178, 85, 245, 121, 213, 235, 184, 90, 215, 229, 248, 89, 172, 112, 190,
|
||||
62, 83, 112, 209, 145, 85, 226, 229, 147, 105, 188, 142, 43, 172, 220,
|
||||
155, 179, 40, 29, 201, 85, 6, 239, 204, 153, 155, 243, 117, 47, 216,
|
||||
222, 216, 38, 229, 188, 146, 51, 188, 162, 119, 81, 194, 82, 205, 130,
|
||||
178, 189, 24, 120, 173, 97, 108, 80, 34, 102, 111, 73, 87, 182, 123,
|
||||
2, 107, 2, 231, 133, 184, 213, 89, 96, 69, 194, 56, 57, 47, 91,
|
||||
222, 57, 100, 253, 68, 130, 92, 175, 94, 148, 254, 129, 15, 240, 176,
|
||||
88, 7, 116, 39, 77, 226, 181, 187, 68, 172, 146, 154, 69, 78, 101,
|
||||
98, 77, 57, 91, 99, 81, 250, 82, 215, 138, 202, 178, 188, 44, 78,
|
||||
37, 67, 254, 196, 143, 201, 152, 141, 98, 217, 86, 224, 60, 92, 149,
|
||||
64, 207, 175, 0, 151, 178, 66, 119, 101, 249, 243, 61, 184, 163, 254,
|
||||
99, 210, 218, 48, 94, 94, 5, 101, 13, 162, 1, 176, 100, 251, 97,
|
||||
167, 249, 49, 233, 115, 179, 250, 111, 78, 23, 109, 115, 193, 178, 130,
|
||||
62, 5, 151, 20, 52, 23, 241, 76, 200, 10, 106, 149, 103, 173, 50,
|
||||
158, 21, 28, 45, 120, 174, 96, 110, 193, 71, 61, 232, 151, 61, 219,
|
||||
115, 4, 170, 82, 254, 156, 206, 16, 47, 197, 127, 231, 231, 244, 153,
|
||||
222, 27, 240, 159, 200, 63, 153, 185, 24, 191, 162, 246, 71, 153, 0,
|
||||
0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130
|
||||
};
|
||||
601
backends/cloud/cloudmanager.cpp
Normal file
601
backends/cloud/cloudmanager.cpp
Normal file
@@ -0,0 +1,601 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/cloudmanager.h"
|
||||
#include "backends/cloud/box/boxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/cloud/googledrive/googledrivestorage.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/str.h"
|
||||
#ifdef USE_SDL_NET
|
||||
#include "backends/networking/sdl_net/localwebserver.h"
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
DECLARE_SINGLETON(Cloud::CloudManager);
|
||||
|
||||
}
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
const char *const CloudManager::kStoragePrefix = "storage_";
|
||||
|
||||
CloudManager::CloudManager() : _currentStorageIndex(0), _activeStorage(nullptr) {}
|
||||
|
||||
CloudManager::~CloudManager() {
|
||||
g_system->getEventManager()->getEventDispatcher()->unregisterSource(this);
|
||||
|
||||
delete _activeStorage;
|
||||
freeStorages();
|
||||
}
|
||||
|
||||
Common::String CloudManager::getStorageConfigName(uint32 index) const {
|
||||
switch (index) {
|
||||
case kStorageNoneId: return "<none>";
|
||||
case kStorageDropboxId: return "Dropbox";
|
||||
case kStorageOneDriveId: return "OneDrive";
|
||||
case kStorageGoogleDriveId: return "GoogleDrive";
|
||||
case kStorageBoxId: return "Box";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assert(false); // Unhandled StorageID value
|
||||
return "";
|
||||
}
|
||||
|
||||
void CloudManager::loadStorage() {
|
||||
switch (_currentStorageIndex) {
|
||||
case kStorageDropboxId:
|
||||
_activeStorage = Dropbox::DropboxStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
_activeStorage = OneDrive::OneDriveStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
_activeStorage = GoogleDrive::GoogleDriveStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
_activeStorage = Box::BoxStorage::loadFromConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
|
||||
break;
|
||||
default:
|
||||
_activeStorage = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_activeStorage) {
|
||||
_currentStorageIndex = kStorageNoneId;
|
||||
}
|
||||
}
|
||||
|
||||
void CloudManager::init() {
|
||||
//init configs structs
|
||||
for (uint32 i = 0; i < kStorageTotal; ++i) {
|
||||
Common::String name = getStorageConfigName(i);
|
||||
StorageConfig config;
|
||||
config.name = name;
|
||||
config.username = "";
|
||||
config.lastSyncDate = "";
|
||||
config.usedBytes = 0;
|
||||
if (ConfMan.hasKey(kStoragePrefix + name + "_username", ConfMan.kCloudDomain))
|
||||
config.username = ConfMan.get(kStoragePrefix + name + "_username", ConfMan.kCloudDomain);
|
||||
if (ConfMan.hasKey(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain))
|
||||
config.lastSyncDate = ConfMan.get(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain);
|
||||
if (ConfMan.hasKey(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain))
|
||||
config.usedBytes = ConfMan.get(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain).asUint64();
|
||||
_storages.push_back(config);
|
||||
}
|
||||
|
||||
//load an active storage if there is any
|
||||
_currentStorageIndex = kStorageNoneId;
|
||||
if (ConfMan.hasKey("current_storage", ConfMan.kCloudDomain))
|
||||
_currentStorageIndex = ConfMan.getInt("current_storage", ConfMan.kCloudDomain);
|
||||
|
||||
loadStorage();
|
||||
|
||||
g_system->getEventManager()->getEventDispatcher()->registerSource(this, false);
|
||||
}
|
||||
|
||||
void CloudManager::save() {
|
||||
for (uint32 i = 0; i < _storages.size(); ++i) {
|
||||
if (i == kStorageNoneId)
|
||||
continue;
|
||||
Common::String name = getStorageConfigName(i);
|
||||
ConfMan.set(kStoragePrefix + name + "_username", _storages[i].username, ConfMan.kCloudDomain);
|
||||
ConfMan.set(kStoragePrefix + name + "_lastSync", _storages[i].lastSyncDate, ConfMan.kCloudDomain);
|
||||
ConfMan.set(kStoragePrefix + name + "_usedBytes", Common::String::format("%llu", (unsigned long long)_storages[i].usedBytes), ConfMan.kCloudDomain);
|
||||
}
|
||||
|
||||
ConfMan.set("current_storage", Common::String::format("%u", _currentStorageIndex), ConfMan.kCloudDomain);
|
||||
if (_activeStorage)
|
||||
_activeStorage->saveConfig(kStoragePrefix + getStorageConfigName(_currentStorageIndex) + "_");
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
void CloudManager::replaceStorage(Storage *storage, uint32 index) {
|
||||
freeStorages();
|
||||
if (!storage)
|
||||
error("CloudManager::replaceStorage: NULL storage passed");
|
||||
if (index >= kStorageTotal)
|
||||
error("CloudManager::replaceStorage: invalid index passed");
|
||||
if (_activeStorage != nullptr && _activeStorage->isWorking()) {
|
||||
warning("CloudManager::replaceStorage: replacing Storage while the other is working");
|
||||
if (_activeStorage->isDownloading())
|
||||
_activeStorage->cancelDownload();
|
||||
if (_activeStorage->isSyncing())
|
||||
_activeStorage->cancelSync();
|
||||
removeStorage(_activeStorage);
|
||||
} else {
|
||||
delete _activeStorage;
|
||||
}
|
||||
_activeStorage = storage;
|
||||
_currentStorageIndex = index;
|
||||
if (_storages[index].username == "") {
|
||||
// options' Cloud tab believes Storage is connected once it has non-empty username
|
||||
_storages[index].username = Common::convertFromU32String(_("<syncing...>"));
|
||||
_storages[index].lastSyncDate = Common::convertFromU32String(_("<right now>"));
|
||||
_storages[index].usedBytes = 0;
|
||||
}
|
||||
save();
|
||||
|
||||
//do what should be done on first Storage connect
|
||||
if (_activeStorage) {
|
||||
_activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
|
||||
}
|
||||
}
|
||||
|
||||
void CloudManager::removeStorage(Storage *storage) {
|
||||
// can't just delete it as it's mostly likely the one who calls the method
|
||||
// it would be freed on freeStorages() call (on next Storage connect or replace)
|
||||
_storagesToRemove.push_back(storage);
|
||||
}
|
||||
|
||||
void CloudManager::freeStorages() {
|
||||
for (uint32 i = 0; i < _storagesToRemove.size(); ++i)
|
||||
delete _storagesToRemove[i];
|
||||
_storagesToRemove.clear();
|
||||
}
|
||||
|
||||
void CloudManager::passNoStorageConnected(Networking::ErrorCallback errorCallback) const {
|
||||
if (errorCallback == nullptr)
|
||||
return;
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "No Storage connected!", -1));
|
||||
}
|
||||
|
||||
Storage *CloudManager::getCurrentStorage() const {
|
||||
return _activeStorage;
|
||||
}
|
||||
|
||||
uint32 CloudManager::getStorageIndex() const {
|
||||
return _currentStorageIndex;
|
||||
}
|
||||
|
||||
Common::StringArray CloudManager::listStorages() const {
|
||||
Common::StringArray result;
|
||||
for (uint32 i = 0; i < _storages.size(); ++i) {
|
||||
result.push_back(_storages[i].name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CloudManager::switchStorage(uint32 index) {
|
||||
if (index >= _storages.size()) {
|
||||
warning("CloudManager::switchStorage: invalid index passed");
|
||||
return false;
|
||||
}
|
||||
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage && storage->isWorking()) {
|
||||
warning("CloudManager::switchStorage: another storage is working now");
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentStorageIndex = index;
|
||||
loadStorage();
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String CloudManager::getStorageUsername(uint32 index) {
|
||||
if (index >= _storages.size())
|
||||
return "";
|
||||
return _storages[index].username;
|
||||
}
|
||||
|
||||
uint64 CloudManager::getStorageUsedSpace(uint32 index) {
|
||||
if (index >= _storages.size())
|
||||
return 0;
|
||||
return _storages[index].usedBytes;
|
||||
}
|
||||
|
||||
Common::String CloudManager::getStorageLastSync(uint32 index) {
|
||||
if (index >= _storages.size())
|
||||
return "";
|
||||
if (index == _currentStorageIndex && isSyncing())
|
||||
return "";
|
||||
return _storages[index].lastSyncDate;
|
||||
}
|
||||
|
||||
void CloudManager::setStorageUsername(uint32 index, const Common::String &name) {
|
||||
if (index >= _storages.size())
|
||||
return;
|
||||
_storages[index].username = name;
|
||||
save();
|
||||
}
|
||||
|
||||
void CloudManager::setStorageUsedSpace(uint32 index, uint64 used) {
|
||||
if (index >= _storages.size())
|
||||
return;
|
||||
_storages[index].usedBytes = used;
|
||||
save();
|
||||
}
|
||||
|
||||
void CloudManager::setStorageLastSync(uint32 index, const Common::String &date) {
|
||||
if (index >= _storages.size())
|
||||
return;
|
||||
_storages[index].lastSyncDate = date;
|
||||
save();
|
||||
}
|
||||
|
||||
void CloudManager::connectStorage(uint32 index, const Common::String &code, Networking::ErrorCallback cb) {
|
||||
freeStorages();
|
||||
|
||||
switch (index) {
|
||||
case kStorageDropboxId:
|
||||
new Dropbox::DropboxStorage(code, cb);
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
new OneDrive::OneDriveStorage(code, cb);
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
new GoogleDrive::GoogleDriveStorage(code, cb);
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
new Box::BoxStorage(code, cb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// in these constructors Storages request token using the passed code
|
||||
// when the token is received, they call replaceStorage()
|
||||
// or removeStorage(), if some error occurred
|
||||
// thus, no memory leak happens
|
||||
}
|
||||
|
||||
void CloudManager::connectStorage(uint32 index, Networking::JsonResponse codeFlowJson, Networking::ErrorCallback cb) {
|
||||
freeStorages();
|
||||
|
||||
switch (index) {
|
||||
case kStorageDropboxId:
|
||||
new Dropbox::DropboxStorage(codeFlowJson, cb);
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
new OneDrive::OneDriveStorage(codeFlowJson, cb);
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
new GoogleDrive::GoogleDriveStorage(codeFlowJson, cb);
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
new Box::BoxStorage(codeFlowJson, cb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// in these constructors Storages extract tokens from the passed JSON
|
||||
// they call replaceStorage(), if the tokens are found,
|
||||
// or removeStorage(), if some error occurred
|
||||
// thus, no memory leak happens
|
||||
}
|
||||
|
||||
bool CloudManager::connectStorage(Networking::JsonResponse codeFlowJson, Networking::ErrorCallback cb) {
|
||||
const Common::JSONValue *json = codeFlowJson.value;
|
||||
if (json == nullptr || !json->isObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (!result.contains("storage")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::JSONValue *storageValue = result.getVal("storage");
|
||||
if (!storageValue->isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 storageId = kStorageNoneId;
|
||||
Common::String storage = storageValue->asString();
|
||||
if (storage == "dropbox")
|
||||
storageId = kStorageDropboxId;
|
||||
else if (storage == "onedrive")
|
||||
storageId = kStorageOneDriveId;
|
||||
else if (storage == "gdrive")
|
||||
storageId = kStorageGoogleDriveId;
|
||||
else if (storage == "box")
|
||||
storageId = kStorageBoxId;
|
||||
|
||||
if (storageId == kStorageNoneId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
connectStorage(storageId, codeFlowJson, cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloudManager::disconnectStorage(uint32 index) {
|
||||
if (index >= kStorageTotal)
|
||||
error("CloudManager::disconnectStorage: invalid index passed");
|
||||
|
||||
Common::String name = getStorageConfigName(index);
|
||||
switch (index) {
|
||||
case kStorageDropboxId:
|
||||
Dropbox::DropboxStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
OneDrive::OneDriveStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
GoogleDrive::GoogleDriveStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
Box::BoxStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switchStorage(kStorageNoneId);
|
||||
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_username", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain);
|
||||
|
||||
StorageConfig config;
|
||||
config.name = name;
|
||||
config.username = "";
|
||||
config.lastSyncDate = "";
|
||||
config.usedBytes = 0;
|
||||
|
||||
_storages[index] = config;
|
||||
}
|
||||
|
||||
|
||||
Networking::Request *CloudManager::listDirectory(const Common::String &path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
return storage->listDirectory(path, callback, errorCallback, recursive);
|
||||
} else {
|
||||
passNoStorageConnected(errorCallback);
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Networking::Request *CloudManager::downloadFolder(const Common::String &remotePath, const Common::Path &localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
return storage->downloadFolder(remotePath, localPath, callback, errorCallback, recursive);
|
||||
} else {
|
||||
passNoStorageConnected(errorCallback);
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Networking::Request *CloudManager::info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
return storage->info(callback, errorCallback);
|
||||
} else {
|
||||
passNoStorageConnected(errorCallback);
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String CloudManager::savesDirectoryPath() {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->savesDirectoryPath();
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CloudManager::canSyncFilename(const Common::String &filename) const {
|
||||
if (filename == "" || filename[0] == '.')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloudManager::isStorageEnabled() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->isEnabled();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CloudManager::enableStorage() {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
storage->enable();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
setStorageLastSync(_currentStorageIndex, "???"); //TODO get the date
|
||||
return storage->syncSaves(callback, errorCallback);
|
||||
} else {
|
||||
passNoStorageConnected(errorCallback);
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CloudManager::isWorking() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->isWorking();
|
||||
return false;
|
||||
}
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
bool CloudManager::isSyncing() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->isSyncing();
|
||||
return false;
|
||||
}
|
||||
|
||||
double CloudManager::getSyncDownloadingProgress() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getSyncDownloadingProgress();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CloudManager::getSyncDownloadingInfo(Storage::SyncDownloadingInfo &info) const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
storage->getSyncDownloadingInfo(info);
|
||||
}
|
||||
|
||||
double CloudManager::getSyncProgress() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getSyncProgress();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Common::Array<Common::String> CloudManager::getSyncingFiles() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getSyncingFiles();
|
||||
return Common::Array<Common::String>();
|
||||
}
|
||||
|
||||
void CloudManager::cancelSync() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
storage->cancelSync();
|
||||
}
|
||||
|
||||
void CloudManager::showCloudDisabledIcon() {
|
||||
_icon.show(CloudIcon::kDisabled, 3000);
|
||||
}
|
||||
|
||||
///// DownloadFolderRequest-related /////
|
||||
|
||||
bool CloudManager::startDownload(const Common::String &remotePath, const Common::Path &localPath) const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->startDownload(remotePath, localPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CloudManager::cancelDownload() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
storage->cancelDownload();
|
||||
}
|
||||
|
||||
void CloudManager::setDownloadTarget(GUI::CommandReceiver *target) const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
storage->setDownloadTarget(target);
|
||||
}
|
||||
|
||||
bool CloudManager::isDownloading() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->isDownloading();
|
||||
return false;
|
||||
}
|
||||
|
||||
double CloudManager::getDownloadingProgress() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadingProgress();
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64 CloudManager::getDownloadBytesNumber() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadBytesNumber();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64 CloudManager::getDownloadTotalBytesNumber() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadTotalBytesNumber();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64 CloudManager::getDownloadSpeed() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadSpeed();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::String CloudManager::getDownloadRemoteDirectory() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadRemoteDirectory();
|
||||
return "";
|
||||
}
|
||||
|
||||
Common::Path CloudManager::getDownloadLocalDirectory() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->getDownloadLocalDirectory();
|
||||
return Common::Path();
|
||||
}
|
||||
|
||||
bool CloudManager::pollEvent(Common::Event &event) {
|
||||
if (_icon.needsUpdate()) {
|
||||
if (_icon.getShownType() != CloudIcon::kDisabled) {
|
||||
if (isWorking()) {
|
||||
_icon.show(CloudIcon::kSyncing);
|
||||
} else {
|
||||
_icon.show(CloudIcon::kNone);
|
||||
}
|
||||
}
|
||||
|
||||
_icon.update();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
329
backends/cloud/cloudmanager.h
Normal file
329
backends/cloud/cloudmanager.h
Normal file
@@ -0,0 +1,329 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CLOUD_CLOUDMANAGER_H
|
||||
#define CLOUD_CLOUDMANAGER_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/cloud/cloudicon.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/events.h"
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class CommandReceiver;
|
||||
|
||||
}
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
// The actual indexes in CloudManager's array
|
||||
enum StorageID {
|
||||
kStorageNoneId = 0,
|
||||
kStorageDropboxId = 1,
|
||||
kStorageOneDriveId = 2,
|
||||
kStorageGoogleDriveId = 3,
|
||||
kStorageBoxId = 4,
|
||||
|
||||
kStorageTotal
|
||||
};
|
||||
|
||||
class CloudManager : public Common::Singleton<CloudManager>, public Common::EventSource {
|
||||
static const char *const kStoragePrefix;
|
||||
|
||||
struct StorageConfig {
|
||||
Common::String name, username;
|
||||
uint64 usedBytes;
|
||||
Common::String lastSyncDate;
|
||||
};
|
||||
|
||||
Common::Array<StorageConfig> _storages;
|
||||
uint _currentStorageIndex;
|
||||
Storage *_activeStorage;
|
||||
Common::Array<Storage *> _storagesToRemove;
|
||||
|
||||
CloudIcon _icon;
|
||||
|
||||
void loadStorage();
|
||||
|
||||
Common::String getStorageConfigName(uint32 index) const;
|
||||
|
||||
/** Frees memory used by storages which failed to connect. */
|
||||
void freeStorages();
|
||||
|
||||
/** Calls the error callback with a special "no storage connected" message. */
|
||||
void passNoStorageConnected(Networking::ErrorCallback errorCallback) const;
|
||||
|
||||
/**
|
||||
* Common::EventSource interface
|
||||
*
|
||||
* The cloud manager registers itself as an event source even if does not
|
||||
* actually produce events as a mean to be polled periodically by the GUI
|
||||
* or engine code.
|
||||
*
|
||||
* The periodical polling is used to update the OSD icon indicating
|
||||
* background sync activity.
|
||||
*/
|
||||
bool pollEvent(Common::Event &event) override;
|
||||
|
||||
public:
|
||||
CloudManager();
|
||||
~CloudManager() override;
|
||||
|
||||
/**
|
||||
* Loads all information from configs and creates current Storage instance.
|
||||
*
|
||||
* @note It's called once on startup in scummvm_main().
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Saves all information into configuration file.
|
||||
*/
|
||||
void save();
|
||||
|
||||
/**
|
||||
* Replace active Storage.
|
||||
* @note this method automatically saves the changes with ConfMan.
|
||||
*
|
||||
* @param storage Cloud::Storage to replace active storage with.
|
||||
* @param index one of Cloud::StorageID enum values to indicate what storage type is replaced.
|
||||
*/
|
||||
void replaceStorage(Storage *storage, uint32 index);
|
||||
|
||||
/** Adds storage in the list of storages to remove later. */
|
||||
void removeStorage(Storage *storage);
|
||||
|
||||
/**
|
||||
* Returns active Storage, which could be used to interact
|
||||
* with cloud storage.
|
||||
*
|
||||
* @return active Cloud::Storage or null, if there is no active Storage.
|
||||
*/
|
||||
Cloud::Storage *getCurrentStorage() const;
|
||||
|
||||
/**
|
||||
* Return active Storage's index.
|
||||
*
|
||||
* @return active Storage's index.
|
||||
*/
|
||||
uint32 getStorageIndex() const;
|
||||
|
||||
/**
|
||||
* Return Storages names as list.
|
||||
*
|
||||
* @return a list of Storages names.
|
||||
*/
|
||||
Common::StringArray listStorages() const;
|
||||
|
||||
/**
|
||||
* Changes the storage to the one with given index.
|
||||
*
|
||||
* @param new Storage's index.
|
||||
*/
|
||||
bool switchStorage(uint32 index);
|
||||
|
||||
/**
|
||||
* Return username used by Storage.
|
||||
*
|
||||
* @param Storage's index.
|
||||
* @returns username or "" if index is invalid (no such Storage).
|
||||
*/
|
||||
Common::String getStorageUsername(uint32 index);
|
||||
|
||||
/**
|
||||
* Return space used by Storage.
|
||||
*
|
||||
* @param Storage's index.
|
||||
* @returns used space in bytes or 0 if index is invalid (no such Storage).
|
||||
*/
|
||||
uint64 getStorageUsedSpace(uint32 index);
|
||||
|
||||
/**
|
||||
* Return Storage's last sync date.
|
||||
*
|
||||
* @param Storage's index.
|
||||
* @returns last sync date or "" if index is invalid (no such Storage).
|
||||
It also returns "" if there never was any sync
|
||||
or if storage is syncing right now.
|
||||
*/
|
||||
Common::String getStorageLastSync(uint32 index);
|
||||
|
||||
/**
|
||||
* Set Storage's username.
|
||||
* Automatically saves changes to the config.
|
||||
*
|
||||
* @param index Storage's index.
|
||||
* @param name username to set
|
||||
*/
|
||||
void setStorageUsername(uint32 index, const Common::String &name);
|
||||
|
||||
/**
|
||||
* Set Storage's used space field.
|
||||
* Automatically saves changes to the config.
|
||||
*
|
||||
* @param index Storage's index.
|
||||
* @param used value to set
|
||||
*/
|
||||
void setStorageUsedSpace(uint32 index, uint64 used);
|
||||
|
||||
/**
|
||||
* Set Storage's last sync date.
|
||||
* Automatically saves changes to the config.
|
||||
*
|
||||
* @param index Storage's index.
|
||||
* @param date date to set
|
||||
*/
|
||||
void setStorageLastSync(uint32 index, const Common::String &date);
|
||||
|
||||
/**
|
||||
* Replace Storage which has given index with a
|
||||
* storage created with given code.
|
||||
*
|
||||
* @param index Storage's index
|
||||
* @param code OAuth2 code received from user
|
||||
* @param cb callback to notify of success or error
|
||||
*/
|
||||
void connectStorage(uint32 index, const Common::String &code, Networking::ErrorCallback cb = nullptr);
|
||||
|
||||
/**
|
||||
* Replace Storage which has given index with a
|
||||
* storage created with given JSON response.
|
||||
*
|
||||
* @param index Storage's index
|
||||
* @param codeFlowJson OAuth2 code flow JSON response (acquired from cloud.scummvm.org)
|
||||
* @param cb callback to notify of success or error
|
||||
*/
|
||||
void connectStorage(uint32 index, Networking::JsonResponse codeFlowJson, Networking::ErrorCallback cb = nullptr);
|
||||
|
||||
/**
|
||||
* From given JSON response, extract Storage index
|
||||
* and replace Storage that has this index with a
|
||||
* storage created with given JSON.
|
||||
*
|
||||
* @param codeFlowJson OAuth2 code flow JSON response (acquired from cloud.scummvm.org)
|
||||
* @param cb callback to notify of success or error
|
||||
* @returns whether Storage index was found and is correct
|
||||
*/
|
||||
bool connectStorage(Networking::JsonResponse codeFlowJson, Networking::ErrorCallback cb = nullptr);
|
||||
|
||||
/**
|
||||
* Remove Storage with a given index from config.
|
||||
*
|
||||
* @param index Storage's index
|
||||
*/
|
||||
void disconnectStorage(uint32 index);
|
||||
|
||||
/** Returns ListDirectoryResponse with list of files. */
|
||||
Networking::Request *listDirectory(const Common::String &path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
|
||||
|
||||
/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
|
||||
Networking::Request *downloadFolder(const Common::String &remotePath, const Common::Path &localPath, Storage::FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
|
||||
|
||||
/** Return the StorageInfo struct. */
|
||||
Networking::Request *info(Storage::StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath();
|
||||
|
||||
/** Returns whether given filename could be uploaded to or downloaded from storage. */
|
||||
bool canSyncFilename(const Common::String &filename) const;
|
||||
|
||||
/** Returns whether current Storage is manually enabled by user (or false, if there is no active Storage). */
|
||||
bool isStorageEnabled() const;
|
||||
|
||||
/** Sets Storage::_isEnabled to true and updates the config. */
|
||||
void enableStorage();
|
||||
|
||||
/**
|
||||
* Starts saves syncing process in currently active storage if there is any.
|
||||
*/
|
||||
SavesSyncRequest *syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr);
|
||||
|
||||
/** Returns whether there are any requests running. */
|
||||
bool isWorking() const;
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
/** Returns whether there is a SavesSyncRequest running. */
|
||||
bool isSyncing() const;
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current sync downloading progress (1 = complete). */
|
||||
double getSyncDownloadingProgress() const;
|
||||
|
||||
/** Fills a struct with numbers about current sync downloading progress. */
|
||||
void getSyncDownloadingInfo(Storage::SyncDownloadingInfo &info) const;
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
|
||||
double getSyncProgress() const;
|
||||
|
||||
/** Returns an array of saves names which are not yet synced (thus cannot be used). */
|
||||
Common::Array<Common::String> getSyncingFiles() const;
|
||||
|
||||
/** Cancels running sync. */
|
||||
void cancelSync() const;
|
||||
|
||||
/** Shows a "cloud disabled" icon for three seconds. */
|
||||
void showCloudDisabledIcon();
|
||||
|
||||
///// DownloadFolderRequest-related /////
|
||||
|
||||
/** Starts a folder download. */
|
||||
bool startDownload(const Common::String &remotePath, const Common::Path &localPath) const;
|
||||
|
||||
/** Cancels running download. */
|
||||
void cancelDownload() const;
|
||||
|
||||
/** Sets FolderDownloadRequest's target to given CommandReceiver. */
|
||||
void setDownloadTarget(GUI::CommandReceiver *target) const;
|
||||
|
||||
/** Returns whether there is a FolderDownloadRequest running. */
|
||||
bool isDownloading() const;
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
|
||||
double getDownloadingProgress() const;
|
||||
|
||||
/** Returns a number of bytes that is downloaded in current download progress. */
|
||||
uint64 getDownloadBytesNumber() const;
|
||||
|
||||
/** Returns a total number of bytes to be downloaded in current download progress. */
|
||||
uint64 getDownloadTotalBytesNumber() const;
|
||||
|
||||
/** Returns download speed of current download progress. */
|
||||
uint64 getDownloadSpeed() const;
|
||||
|
||||
/** Returns remote directory path. */
|
||||
Common::String getDownloadRemoteDirectory() const;
|
||||
|
||||
/** Returns local directory path. */
|
||||
Common::Path getDownloadLocalDirectory() const;
|
||||
};
|
||||
|
||||
/** Shortcut for accessing the connection manager. */
|
||||
#define CloudMan Cloud::CloudManager::instance()
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
137
backends/cloud/downloadrequest.cpp
Normal file
137
backends/cloud/downloadrequest.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/downloadrequest.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, const Common::String &remoteFileId, Common::DumpFile *dumpFile):
|
||||
Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileId(remoteFileId), _storage(storage),
|
||||
_remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false), _buffer(new byte[DOWNLOAD_REQUEST_BUFFER_SIZE]) {
|
||||
start();
|
||||
}
|
||||
|
||||
DownloadRequest::~DownloadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
delete _localFile;
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
void DownloadRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_remoteFileStream = nullptr;
|
||||
//TODO: add some way to reopen DumpFile, so DownloadRequest could be restarted
|
||||
_ignoreCallback = false;
|
||||
|
||||
_workingRequest = _storage->streamFileById(
|
||||
_remoteFileId,
|
||||
new Common::Callback<DownloadRequest, const Networking::NetworkReadStreamResponse &>(this, &DownloadRequest::streamCallback),
|
||||
new Common::Callback<DownloadRequest, const Networking::ErrorResponse &>(this, &DownloadRequest::streamErrorCallback)
|
||||
);
|
||||
}
|
||||
|
||||
void DownloadRequest::streamCallback(const Networking::NetworkReadStreamResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
_remoteFileStream = response.value;
|
||||
}
|
||||
|
||||
void DownloadRequest::streamErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void DownloadRequest::handle() {
|
||||
if (!_localFile) {
|
||||
warning("DownloadRequest: no file to write");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: no file to write into", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_localFile->isOpen()) {
|
||||
warning("DownloadRequest: failed to open file to write");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: failed to open file to write", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_remoteFileStream) {
|
||||
//waiting for callback
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 readBytes = _remoteFileStream->read(_buffer, DOWNLOAD_REQUEST_BUFFER_SIZE);
|
||||
|
||||
if (readBytes != 0)
|
||||
if (_localFile->write(_buffer, readBytes) != readBytes) {
|
||||
warning("DownloadRequest: unable to write all received bytes into output file");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: failed to write all bytes into a file", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_remoteFileStream->eos()) {
|
||||
if (_remoteFileStream->httpResponseCode() != 200) {
|
||||
warning("DownloadRequest: HTTP response code is not 200 OK (it's %ld)", _remoteFileStream->httpResponseCode());
|
||||
//TODO: do something about it actually
|
||||
// the problem is file's already downloaded, stream is over
|
||||
// so we can't return error message anymore
|
||||
}
|
||||
|
||||
finishDownload(_remoteFileStream->httpResponseCode() == 200);
|
||||
|
||||
_localFile->close(); //yes, I know it's closed automatically in ~DumpFile()
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadRequest::restart() {
|
||||
warning("DownloadRequest: can't restart as there are no means to reopen DumpFile");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::restart: can't restart as there are no means to reopen DumpFile", -1));
|
||||
//start();
|
||||
}
|
||||
|
||||
void DownloadRequest::finishDownload(bool success) {
|
||||
Request::finishSuccess();
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
void DownloadRequest::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
if (_localFile)
|
||||
_localFile->close();
|
||||
Request::finishError(error);
|
||||
}
|
||||
|
||||
double DownloadRequest::getProgress() const {
|
||||
if (_remoteFileStream)
|
||||
return _remoteFileStream->getProgress();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
63
backends/cloud/downloadrequest.h
Normal file
63
backends/cloud/downloadrequest.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DOWNLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_DOWNLOADREQUEST_H
|
||||
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
#define DOWNLOAD_REQUEST_BUFFER_SIZE 1 * 1024 * 1024
|
||||
|
||||
class DownloadRequest: public Networking::Request {
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Common::DumpFile *_localFile;
|
||||
Common::String _remoteFileId;
|
||||
Storage *_storage;
|
||||
Networking::NetworkReadStream *_remoteFileStream;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
byte *_buffer;
|
||||
|
||||
void start();
|
||||
void streamCallback(const Networking::NetworkReadStreamResponse &response);
|
||||
void streamErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishDownload(bool success);
|
||||
void finishError(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED) override;
|
||||
|
||||
public:
|
||||
DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, const Common::String &remoteFileId, Common::DumpFile *dumpFile);
|
||||
~DownloadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getProgress() const;
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
138
backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
Normal file
138
backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxtokenrefresher.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_API_CREATE_FOLDER "https://api.dropboxapi.com/2/files/create_folder"
|
||||
|
||||
DropboxCreateDirectoryRequest::DropboxCreateDirectoryRequest(DropboxStorage *storage, const Common::String &path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
DropboxCreateDirectoryRequest::~DropboxCreateDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
}
|
||||
|
||||
void DropboxCreateDirectoryRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_ignoreCallback = false;
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxCreateDirectoryRequest::responseCallback);
|
||||
Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxCreateDirectoryRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER);
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("path", new Common::JSONValue(_path));
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void DropboxCreateDirectoryRequest::responseCallback(const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
if (response.request) _date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "DropboxCreateDirectoryRequest::responseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject info = json->asObject();
|
||||
if (info.contains("id")) {
|
||||
finishCreation(true);
|
||||
} else {
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(info, "error_summary", "DropboxCreateDirectoryRequest")) {
|
||||
Common::String summary = info.getVal("error_summary")->asString();
|
||||
if (summary.contains("path") && summary.contains("conflict") && summary.contains("folder")) {
|
||||
// existing directory - not an error for CreateDirectoryRequest
|
||||
finishCreation(false);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
}
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void DropboxCreateDirectoryRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void DropboxCreateDirectoryRequest::handle() {}
|
||||
|
||||
void DropboxCreateDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String DropboxCreateDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void DropboxCreateDirectoryRequest::finishCreation(bool success) {
|
||||
Request::finishSuccess();
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
58
backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
Normal file
58
backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXCREATEDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_DROPBOXCREATEDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage;
|
||||
|
||||
class DropboxCreateDirectoryRequest: public Networking::Request {
|
||||
DropboxStorage *_storage;
|
||||
Common::String _path;
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void responseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishCreation(bool success);
|
||||
public:
|
||||
DropboxCreateDirectoryRequest(DropboxStorage *storage, const Common::String &path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
|
||||
~DropboxCreateDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
193
backends/cloud/dropbox/dropboxinforequest.cpp
Normal file
193
backends/cloud/dropbox/dropboxinforequest.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxinforequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxtokenrefresher.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_API_GET_CURRENT_ACCOUNT "https://api.dropboxapi.com/2/users/get_current_account"
|
||||
#define DROPBOX_API_GET_SPACE_USAGE "https://api.dropboxapi.com/2/users/get_space_usage"
|
||||
|
||||
DropboxInfoRequest::DropboxInfoRequest(DropboxStorage *storage, Storage::StorageInfoCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _infoCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
DropboxInfoRequest::~DropboxInfoRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _infoCallback;
|
||||
}
|
||||
|
||||
void DropboxInfoRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_ignoreCallback = false;
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, const Networking::JsonResponse &>(this, &DropboxInfoRequest::userResponseCallback);
|
||||
Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, const Networking::ErrorResponse &>(this, &DropboxInfoRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
request->addPostField("null"); //use POST
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void DropboxInfoRequest::userResponseCallback(const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Networking::ErrorResponse error(this, "DropboxInfoRequest::userResponseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
//Dropbox documentation states there are no errors for this API method
|
||||
Common::JSONObject info = json->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsAttribute(info, "name", "DropboxInfoRequest") &&
|
||||
Networking::HttpJsonRequest::jsonIsObject(info.getVal("name"), "DropboxInfoRequest")) {
|
||||
Common::JSONObject nameInfo = info.getVal("name")->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(nameInfo, "display_name", "DropboxInfoRequest")) {
|
||||
_name = nameInfo.getVal("display_name")->asString();
|
||||
}
|
||||
}
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(info, "account_id", "DropboxInfoRequest")) {
|
||||
_uid = info.getVal("account_id")->asString();
|
||||
}
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(info, "email", "DropboxInfoRequest")) {
|
||||
_email = info.getVal("email")->asString();
|
||||
}
|
||||
CloudMan.setStorageUsername(kStorageDropboxId, _email);
|
||||
delete json;
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, const Networking::JsonResponse &>(this, &DropboxInfoRequest::quotaResponseCallback);
|
||||
Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, const Networking::ErrorResponse &>(this, &DropboxInfoRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE);
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
request->addPostField("null"); //use POST
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void DropboxInfoRequest::quotaResponseCallback(const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Networking::ErrorResponse error(this, "DropboxInfoRequest::quotaResponseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
//Dropbox documentation states there are no errors for this API method
|
||||
Common::JSONObject info = json->asObject();
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(info, "used", "DropboxInfoRequest")) {
|
||||
error.response = "Passed JSON misses 'used' attribute!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 used = info.getVal("used")->asIntegerNumber(), allocated = 0;
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsAttribute(info, "allocation", "DropboxInfoRequest") &&
|
||||
Networking::HttpJsonRequest::jsonIsObject(info.getVal("allocation"), "DropboxInfoRequest")) {
|
||||
Common::JSONObject allocation = info.getVal("allocation")->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(allocation, "allocated", "DropboxInfoRequest")) {
|
||||
error.response = "Passed JSON misses 'allocation/allocated' attribute!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
allocated = allocation.getVal("allocated")->asIntegerNumber();
|
||||
}
|
||||
|
||||
finishInfo(StorageInfo(_uid, _name, _email, used, allocated));
|
||||
delete json;
|
||||
}
|
||||
|
||||
void DropboxInfoRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void DropboxInfoRequest::handle() {}
|
||||
|
||||
void DropboxInfoRequest::restart() { start(); }
|
||||
|
||||
void DropboxInfoRequest::finishInfo(const StorageInfo &info) {
|
||||
Request::finishSuccess();
|
||||
if (_infoCallback)
|
||||
(*_infoCallback)(Storage::StorageInfoResponse(this, info));
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
57
backends/cloud/dropbox/dropboxinforequest.h
Normal file
57
backends/cloud/dropbox/dropboxinforequest.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXINFOREQUEST_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_DROPBOXINFOREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage;
|
||||
|
||||
class DropboxInfoRequest: public Networking::Request {
|
||||
DropboxStorage *_storage;
|
||||
Common::String _uid, _name, _email;
|
||||
Storage::StorageInfoCallback _infoCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
|
||||
void start();
|
||||
void userResponseCallback(const Networking::JsonResponse &response);
|
||||
void quotaResponseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishInfo(const StorageInfo &info);
|
||||
public:
|
||||
DropboxInfoRequest(DropboxStorage *storage, Storage::StorageInfoCallback cb, Networking::ErrorCallback ecb);
|
||||
~DropboxInfoRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
224
backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
Normal file
224
backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxtokenrefresher.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_API_LIST_FOLDER "https://api.dropboxapi.com/2/files/list_folder"
|
||||
#define DROPBOX_API_LIST_FOLDER_CONTINUE "https://api.dropboxapi.com/2/files/list_folder/continue"
|
||||
|
||||
DropboxListDirectoryRequest::DropboxListDirectoryRequest(DropboxStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
|
||||
Networking::Request(nullptr, ecb), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb),
|
||||
_storage(storage), _workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
DropboxListDirectoryRequest::~DropboxListDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _listDirectoryCallback;
|
||||
}
|
||||
|
||||
void DropboxListDirectoryRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_files.clear();
|
||||
_ignoreCallback = false;
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxListDirectoryRequest::responseCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxListDirectoryRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER);
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("path", new Common::JSONValue(_requestedPath));
|
||||
jsonRequestParameters.setVal("recursive", new Common::JSONValue(_requestedRecursive));
|
||||
jsonRequestParameters.setVal("include_media_info", new Common::JSONValue(false));
|
||||
jsonRequestParameters.setVal("include_deleted", new Common::JSONValue(false));
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void DropboxListDirectoryRequest::responseCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
|
||||
if (_ignoreCallback) {
|
||||
delete response.value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "DropboxListDirectoryRequest::responseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject responseObject = json->asObject();
|
||||
|
||||
if (responseObject.contains("error") || responseObject.contains("error_summary")) {
|
||||
if (responseObject.contains("error_summary") && responseObject.getVal("error_summary")->isString()) {
|
||||
warning("Dropbox returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
|
||||
}
|
||||
error.failed = true;
|
||||
error.response = json->stringify();
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
|
||||
if (responseObject.contains("entries")) {
|
||||
if (!responseObject.getVal("entries")->isArray()) {
|
||||
error.response = Common::String::format(
|
||||
"\"entries\" found, but that's not an array!\n%s",
|
||||
responseObject.getVal("entries")->stringify(true).c_str()
|
||||
);
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONArray items = responseObject.getVal("entries")->asArray();
|
||||
for (uint32 i = 0; i < items.size(); ++i) {
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "DropboxListDirectoryRequest"))
|
||||
continue;
|
||||
|
||||
Common::JSONObject item = items[i]->asObject();
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "path_lower", "DropboxListDirectoryRequest"))
|
||||
continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, ".tag", "DropboxListDirectoryRequest"))
|
||||
continue;
|
||||
|
||||
Common::String path = item.getVal("path_lower")->asString();
|
||||
bool isDirectory = (item.getVal(".tag")->asString() == "folder");
|
||||
uint32 size = 0, timestamp = 0;
|
||||
if (!isDirectory) {
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "server_modified", "DropboxListDirectoryRequest"))
|
||||
continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(item, "size", "DropboxListDirectoryRequest"))
|
||||
continue;
|
||||
|
||||
size = item.getVal("size")->asIntegerNumber();
|
||||
timestamp = ISO8601::convertToTimestamp(item.getVal("server_modified")->asString());
|
||||
}
|
||||
_files.push_back(StorageFile(path, size, timestamp, isDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMore = false;
|
||||
if (responseObject.contains("has_more")) {
|
||||
if (!responseObject.getVal("has_more")->isBool()) {
|
||||
warning("DropboxListDirectoryRequest: \"has_more\" is not a boolean");
|
||||
debug(9, "%s", responseObject.getVal("has_more")->stringify(true).c_str());
|
||||
error.response = "\"has_more\" is not a boolean!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
hasMore = responseObject.getVal("has_more")->asBool();
|
||||
}
|
||||
|
||||
if (hasMore) {
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(responseObject, "cursor", "DropboxListDirectoryRequest")) {
|
||||
error.response = "\"has_more\" found, but \"cursor\" is not (or it's not a string)!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxListDirectoryRequest::responseCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxListDirectoryRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER_CONTINUE);
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("cursor", new Common::JSONValue(responseObject.getVal("cursor")->asString()));
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
} else {
|
||||
finishListing(_files);
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void DropboxListDirectoryRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void DropboxListDirectoryRequest::handle() {}
|
||||
|
||||
void DropboxListDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String DropboxListDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void DropboxListDirectoryRequest::finishListing(const Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_listDirectoryCallback)
|
||||
(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
62
backends/cloud/dropbox/dropboxlistdirectoryrequest.h
Normal file
62
backends/cloud/dropbox/dropboxlistdirectoryrequest.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage;
|
||||
|
||||
class DropboxListDirectoryRequest: public Networking::Request {
|
||||
Common::String _requestedPath;
|
||||
bool _requestedRecursive;
|
||||
|
||||
Storage::ListDirectoryCallback _listDirectoryCallback;
|
||||
DropboxStorage *_storage;
|
||||
Common::Array<StorageFile> _files;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void responseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishListing(const Common::Array<StorageFile> &files);
|
||||
public:
|
||||
DropboxListDirectoryRequest(DropboxStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
|
||||
~DropboxListDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
131
backends/cloud/dropbox/dropboxstorage.cpp
Normal file
131
backends/cloud/dropbox/dropboxstorage.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxinforequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxuploadrequest.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_API_FILES_DOWNLOAD "https://content.dropboxapi.com/2/files/download"
|
||||
|
||||
DropboxStorage::DropboxStorage(const Common::String &accessToken, const Common::String &refreshToken, bool enabled):
|
||||
BaseStorage(accessToken, refreshToken, enabled) {}
|
||||
|
||||
DropboxStorage::DropboxStorage(const Common::String &code, Networking::ErrorCallback cb): BaseStorage() {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
DropboxStorage::DropboxStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb) : BaseStorage() {
|
||||
codeFlowComplete(cb, codeFlowJson);
|
||||
}
|
||||
|
||||
DropboxStorage::~DropboxStorage() {}
|
||||
|
||||
Common::String DropboxStorage::cloudProvider() { return "dropbox"; }
|
||||
|
||||
uint32 DropboxStorage::storageIndex() { return kStorageDropboxId; }
|
||||
|
||||
bool DropboxStorage::needsRefreshToken() { return true; }
|
||||
|
||||
bool DropboxStorage::canReuseRefreshToken() { return true; }
|
||||
|
||||
void DropboxStorage::saveConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String DropboxStorage::name() const {
|
||||
return "Dropbox";
|
||||
}
|
||||
|
||||
Networking::Request *DropboxStorage::listDirectory(const Common::String &path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
return addRequest(new DropboxListDirectoryRequest(this, path, outerCallback, errorCallback, recursive));
|
||||
}
|
||||
|
||||
Networking::Request *DropboxStorage::upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
return addRequest(new DropboxUploadRequest(this, path, contents, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *DropboxStorage::streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("path", new Common::JSONValue(path));
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
|
||||
Networking::HttpRequest *request = new Networking::HttpRequest(nullptr, nullptr, DROPBOX_API_FILES_DOWNLOAD); //TODO: is it OK to pass no callbacks?
|
||||
request->addHeader("Authorization: Bearer " + _token);
|
||||
request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
|
||||
request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
|
||||
|
||||
Networking::NetworkReadStreamResponse response = request->execute();
|
||||
if (callback)
|
||||
(*callback)(response);
|
||||
return request; // no leak here, response.request == request
|
||||
}
|
||||
|
||||
Networking::Request *DropboxStorage::createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new DropboxCreateDirectoryRequest(this, path, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *DropboxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new DropboxInfoRequest(this, callback, errorCallback));
|
||||
}
|
||||
|
||||
Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; }
|
||||
|
||||
DropboxStorage *DropboxStorage::loadFromConfig(const Common::String &keyPrefix) {
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("DropboxStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
|
||||
warning("DropboxStorage: no refresh_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new DropboxStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void DropboxStorage::removeFromConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
116
backends/cloud/dropbox/dropboxstorage.h
Normal file
116
backends/cloud/dropbox/dropboxstorage.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_STORAGE_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_STORAGE_H
|
||||
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "common/callback.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage: public Cloud::BaseStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
DropboxStorage(const Common::String &token, const Common::String &refreshToken, bool enabled);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "dropbox"
|
||||
*/
|
||||
Common::String cloudProvider() override;
|
||||
|
||||
/**
|
||||
* @return kStorageDropboxId
|
||||
*/
|
||||
uint32 storageIndex() override;
|
||||
|
||||
bool needsRefreshToken() override;
|
||||
|
||||
bool canReuseRefreshToken() override;
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
DropboxStorage(const Common::String &code, Networking::ErrorCallback cb);
|
||||
|
||||
/** This constructor extracts tokens from JSON acquired via OAuth code flow. */
|
||||
DropboxStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb);
|
||||
|
||||
~DropboxStorage() override;
|
||||
|
||||
/**
|
||||
* Storage methods, which are used by CloudManager to save
|
||||
* storage in configuration file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save storage data using ConfMan.
|
||||
* @param keyPrefix all saved keys must start with this prefix.
|
||||
* @note every Storage must write keyPrefix + "type" key
|
||||
* with common value (e.g. "Dropbox").
|
||||
*/
|
||||
void saveConfig(const Common::String &keyPrefix) override;
|
||||
|
||||
/**
|
||||
* Return unique storage name.
|
||||
* @returns some unique storage name (for example, "Dropbox (user@example.com)")
|
||||
*/
|
||||
Common::String name() const override;
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
||||
/** Returns ListDirectoryStatus struct with list of files. */
|
||||
Networking::Request *listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) override;
|
||||
|
||||
/** Returns UploadStatus struct with info about uploaded file. */
|
||||
Networking::Request *upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
Networking::Request *streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
Networking::Request *createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns the StorageInfo struct. */
|
||||
Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath() override;
|
||||
|
||||
/**
|
||||
* Load token and user id from configs and return DropboxStorage for those.
|
||||
* @return pointer to the newly created DropboxStorage or 0 if some problem occurred.
|
||||
*/
|
||||
static DropboxStorage *loadFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
/**
|
||||
* Remove all DropboxStorage-related data from config.
|
||||
*/
|
||||
static void removeFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
107
backends/cloud/dropbox/dropboxtokenrefresher.cpp
Normal file
107
backends/cloud/dropbox/dropboxtokenrefresher.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxtokenrefresher.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
DropboxTokenRefresher::DropboxTokenRefresher(DropboxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
|
||||
HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
|
||||
|
||||
DropboxTokenRefresher::~DropboxTokenRefresher() {}
|
||||
|
||||
void DropboxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
|
||||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("DropboxTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DropboxTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//update headers: first change header with token, then pass those to request
|
||||
for (uint32 i = 0; i < _headersList.size(); ++i) {
|
||||
if (_headersList[i].contains("Authorization")) {
|
||||
_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
|
||||
}
|
||||
}
|
||||
|
||||
//successfully received refreshed token, can restart the original request now
|
||||
retry(0);
|
||||
}
|
||||
|
||||
void DropboxTokenRefresher::finishJson(const Common::JSONValue *json) {
|
||||
if (!json) {
|
||||
//that's probably not an error (200 OK)
|
||||
HttpJsonRequest::finishJson(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonIsObject(json, "DropboxTokenRefresher")) {
|
||||
Common::JSONObject result = json->asObject();
|
||||
|
||||
if (result.contains("error") || result.contains("error_summary")) {
|
||||
long httpCode = -1;
|
||||
if (_stream) {
|
||||
httpCode = _stream->httpResponseCode();
|
||||
debug(9, "DropboxTokenRefresher: code %ld", httpCode);
|
||||
}
|
||||
|
||||
bool irrecoverable = true;
|
||||
if (jsonContainsString(result, "error_summary", "DropboxTokenRefresher")) {
|
||||
if (result.getVal("error_summary")->asString().contains("expired_access_token")) {
|
||||
irrecoverable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (irrecoverable) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpCode));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<DropboxTokenRefresher, const Storage::BoolResponse &>(this, &DropboxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//notify user of success
|
||||
HttpJsonRequest::finishJson(json);
|
||||
}
|
||||
|
||||
void DropboxTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
if (error.httpResponseCode == 401) {
|
||||
pause();
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<DropboxTokenRefresher, const Storage::BoolResponse &>(this, &DropboxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
|
||||
Request::finishError(error);
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
48
backends/cloud/dropbox/dropboxtokenrefresher.h
Normal file
48
backends/cloud/dropbox/dropboxtokenrefresher.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXTOKENREFRESHER_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_DROPBOXTOKENREFRESHER_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage;
|
||||
|
||||
class DropboxTokenRefresher: public Networking::HttpJsonRequest {
|
||||
DropboxStorage *_parentStorage;
|
||||
|
||||
void tokenRefreshed(const Storage::BoolResponse &response);
|
||||
|
||||
void finishJson(const Common::JSONValue *json) override;
|
||||
void finishError(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED) override;
|
||||
public:
|
||||
DropboxTokenRefresher(DropboxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
|
||||
~DropboxTokenRefresher() override;
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
205
backends/cloud/dropbox/dropboxuploadrequest.cpp
Normal file
205
backends/cloud/dropbox/dropboxuploadrequest.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/dropbox/dropboxuploadrequest.h"
|
||||
#include "backends/cloud/dropbox/dropboxstorage.h"
|
||||
#include "backends/cloud/dropbox/dropboxtokenrefresher.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_API_FILES_UPLOAD "https://content.dropboxapi.com/2/files/upload"
|
||||
#define DROPBOX_API_FILES_UPLOAD_SESSION "https://content.dropboxapi.com/2/files/upload_session/"
|
||||
|
||||
DropboxUploadRequest::DropboxUploadRequest(DropboxStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
DropboxUploadRequest::~DropboxUploadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _contentsStream;
|
||||
delete _uploadCallback;
|
||||
}
|
||||
|
||||
void DropboxUploadRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
if (!_contentsStream) {
|
||||
warning("DropboxUploadRequest: cannot start because stream is invalid");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DropboxUploadRequest::start: cannot start because stream is invalid", -1));
|
||||
return;
|
||||
}
|
||||
if (!_contentsStream->seek(0)) {
|
||||
warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DropboxUploadRequest::start: cannot restart because stream couldn't seek(0)", -1));
|
||||
return;
|
||||
}
|
||||
_ignoreCallback = false;
|
||||
|
||||
uploadNextPart();
|
||||
}
|
||||
|
||||
void DropboxUploadRequest::uploadNextPart() {
|
||||
const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
|
||||
|
||||
Common::String url = DROPBOX_API_FILES_UPLOAD_SESSION;
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
|
||||
if (_contentsStream->pos() == 0 || _sessionId == "") {
|
||||
if ((uint32)_contentsStream->size() <= UPLOAD_PER_ONE_REQUEST) {
|
||||
url = DROPBOX_API_FILES_UPLOAD;
|
||||
jsonRequestParameters.setVal("path", new Common::JSONValue(_savePath));
|
||||
jsonRequestParameters.setVal("mode", new Common::JSONValue("overwrite"));
|
||||
jsonRequestParameters.setVal("autorename", new Common::JSONValue(false));
|
||||
jsonRequestParameters.setVal("mute", new Common::JSONValue(false));
|
||||
} else {
|
||||
url += "start";
|
||||
jsonRequestParameters.setVal("close", new Common::JSONValue(false));
|
||||
}
|
||||
} else {
|
||||
if ((uint32)(_contentsStream->size() - _contentsStream->pos()) <= UPLOAD_PER_ONE_REQUEST) {
|
||||
url += "finish";
|
||||
Common::JSONObject jsonCursor, jsonCommit;
|
||||
jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
|
||||
jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
|
||||
jsonCommit.setVal("path", new Common::JSONValue(_savePath));
|
||||
jsonCommit.setVal("mode", new Common::JSONValue("overwrite"));
|
||||
jsonCommit.setVal("autorename", new Common::JSONValue(false));
|
||||
jsonCommit.setVal("mute", new Common::JSONValue(false));
|
||||
jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
|
||||
jsonRequestParameters.setVal("commit", new Common::JSONValue(jsonCommit));
|
||||
} else {
|
||||
url += "append_v2";
|
||||
Common::JSONObject jsonCursor;
|
||||
jsonCursor.setVal("session_id", new Common::JSONValue(_sessionId));
|
||||
jsonCursor.setVal("offset", new Common::JSONValue((long long int)_contentsStream->pos()));
|
||||
jsonRequestParameters.setVal("cursor", new Common::JSONValue(jsonCursor));
|
||||
jsonRequestParameters.setVal("close", new Common::JSONValue(false));
|
||||
}
|
||||
}
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
Networking::JsonCallback callback = new Common::Callback<DropboxUploadRequest, const Networking::JsonResponse &>(this, &DropboxUploadRequest::partUploadedCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<DropboxUploadRequest, const Networking::ErrorResponse &>(this, &DropboxUploadRequest::partUploadedErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/octet-stream");
|
||||
request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
|
||||
|
||||
byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
|
||||
uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
|
||||
request->setBuffer(buffer, size);
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void DropboxUploadRequest::partUploadedCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "", -1);
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
bool needsFinishRequest = false;
|
||||
|
||||
if (json->isObject()) {
|
||||
Common::JSONObject object = json->asObject();
|
||||
|
||||
//debug(9, "%s", json->stringify(true).c_str());
|
||||
|
||||
if (object.contains("error") || object.contains("error_summary")) {
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "error_summary", "DropboxUploadRequest")) {
|
||||
warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
|
||||
}
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "path_lower", "DropboxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(object, "server_modified", "DropboxUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsIntegerNumber(object, "size", "DropboxUploadRequest")) {
|
||||
//finished
|
||||
Common::String path = object.getVal("path_lower")->asString();
|
||||
uint32 size = object.getVal("size")->asIntegerNumber();
|
||||
uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString());
|
||||
finishUpload(StorageFile(path, size, timestamp, false));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sessionId == "") {
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "session_id", "DropboxUploadRequest"))
|
||||
_sessionId = object.getVal("session_id")->asString();
|
||||
needsFinishRequest = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) {
|
||||
warning("DropboxUploadRequest: no file info to return");
|
||||
finishUpload(StorageFile(_savePath, 0, 0, false));
|
||||
} else {
|
||||
uploadNextPart();
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void DropboxUploadRequest::partUploadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void DropboxUploadRequest::handle() {}
|
||||
|
||||
void DropboxUploadRequest::restart() { start(); }
|
||||
|
||||
void DropboxUploadRequest::finishUpload(const StorageFile &file) {
|
||||
Request::finishSuccess();
|
||||
if (_uploadCallback)
|
||||
(*_uploadCallback)(Storage::UploadResponse(this, file));
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
61
backends/cloud/dropbox/dropboxuploadrequest.h
Normal file
61
backends/cloud/dropbox/dropboxuploadrequest.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_DROPBOX_DROPBOXUPLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_DROPBOXUPLOADREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage;
|
||||
|
||||
class DropboxUploadRequest: public Networking::Request {
|
||||
DropboxStorage *_storage;
|
||||
Common::String _savePath;
|
||||
Common::SeekableReadStream *_contentsStream;
|
||||
Storage::UploadCallback _uploadCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _sessionId;
|
||||
|
||||
void start();
|
||||
void uploadNextPart();
|
||||
void partUploadedCallback(const Networking::JsonResponse &response);
|
||||
void partUploadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishUpload(const StorageFile &status);
|
||||
|
||||
public:
|
||||
DropboxUploadRequest(DropboxStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
|
||||
~DropboxUploadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
198
backends/cloud/folderdownloadrequest.cpp
Normal file
198
backends/cloud/folderdownloadrequest.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/folderdownloadrequest.h"
|
||||
#include "backends/cloud/downloadrequest.h"
|
||||
#include "backends/cloud/id/iddownloadrequest.h"
|
||||
#include "common/debug.h"
|
||||
#include "gui/downloaddialog.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "cloudmanager.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, const Common::String &remoteDirectoryPath, const Common::Path &localDirectoryPath, bool recursive):
|
||||
Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _fileArrayCallback(callback),
|
||||
_remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive),
|
||||
_workingRequest(nullptr), _ignoreCallback(false), _totalFiles(0) {
|
||||
start();
|
||||
}
|
||||
|
||||
FolderDownloadRequest::~FolderDownloadRequest() {
|
||||
sendCommand(GUI::kDownloadEndedCmd, 0);
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _fileArrayCallback;
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_currentFile = StorageFile();
|
||||
_pendingFiles.clear();
|
||||
_failedFiles.clear();
|
||||
_ignoreCallback = false;
|
||||
_totalFiles = 0;
|
||||
_downloadedBytes = _totalBytes = _wasDownloadedBytes = _currentDownloadSpeed = 0;
|
||||
|
||||
//list directory first
|
||||
_workingRequest = _storage->listDirectory(
|
||||
_remoteDirectoryPath,
|
||||
new Common::Callback<FolderDownloadRequest, const Storage::ListDirectoryResponse &>(this, &FolderDownloadRequest::directoryListedCallback),
|
||||
new Common::Callback<FolderDownloadRequest, const Networking::ErrorResponse &>(this, &FolderDownloadRequest::directoryListedErrorCallback),
|
||||
_recursive
|
||||
);
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::directoryListedCallback(const Storage::ListDirectoryResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
_pendingFiles = response.value;
|
||||
|
||||
// remove all directories
|
||||
// non-empty directories would be created by DumpFile, and empty ones are just ignored
|
||||
// also skip all hidden files (with names starting with '.') or with other names that are forbidden to sync in CloudManager
|
||||
for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end();)
|
||||
if (i->isDirectory() || !CloudMan.canSyncFilename(i->name()))
|
||||
_pendingFiles.erase(i);
|
||||
else {
|
||||
_totalBytes += i->size();
|
||||
++i;
|
||||
}
|
||||
|
||||
_totalFiles = _pendingFiles.size();
|
||||
downloadNextFile();
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::directoryListedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::fileDownloadedCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (!response.value) _failedFiles.push_back(_currentFile);
|
||||
_downloadedBytes += _currentFile.size();
|
||||
downloadNextFile();
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::fileDownloadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
fileDownloadedCallback(Storage::BoolResponse(error.request, false));
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::downloadNextFile() {
|
||||
do {
|
||||
if (_pendingFiles.empty()) {
|
||||
sendCommand(GUI::kDownloadEndedCmd, 0);
|
||||
finishDownload(_failedFiles);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentFile = _pendingFiles.back();
|
||||
_pendingFiles.pop_back();
|
||||
} while (_currentFile.isDirectory()); // directories are actually removed earlier, in the directoryListedCallback()
|
||||
|
||||
sendCommand(GUI::kDownloadProgressCmd, (int)(getProgress() * 100));
|
||||
|
||||
Common::String remotePath = _currentFile.path();
|
||||
Common::String localPathStr = remotePath;
|
||||
if (!_remoteDirectoryPath.empty()) {
|
||||
if (remotePath.hasPrefix(_remoteDirectoryPath)) {
|
||||
localPathStr.erase(0, _remoteDirectoryPath.size());
|
||||
if (_remoteDirectoryPath.lastChar() != '/' && _remoteDirectoryPath.lastChar() != '\\')
|
||||
localPathStr.erase(0, 1);
|
||||
} else {
|
||||
warning("FolderDownloadRequest: Can't process the following paths:");
|
||||
warning("remote directory: %s", _remoteDirectoryPath.c_str());
|
||||
warning("remote file under that directory: %s", remotePath.c_str());
|
||||
}
|
||||
}
|
||||
Common::Path localPath(localPathStr);
|
||||
if (!_localDirectoryPath.empty()) {
|
||||
localPath = _localDirectoryPath.join(localPath);
|
||||
}
|
||||
debug(9, "FolderDownloadRequest: %s -> %s", remotePath.c_str(), localPath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
_workingRequest = _storage->downloadById(
|
||||
_currentFile.id(), localPath,
|
||||
new Common::Callback<FolderDownloadRequest, const Storage::BoolResponse &>(this, &FolderDownloadRequest::fileDownloadedCallback),
|
||||
new Common::Callback<FolderDownloadRequest, const Networking::ErrorResponse &>(this, &FolderDownloadRequest::fileDownloadedErrorCallback)
|
||||
);
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::handle() {
|
||||
uint32 microsecondsPassed = Networking::ConnectionManager::getCloudRequestsPeriodInMicroseconds();
|
||||
uint64 currentDownloadedBytes = getDownloadedBytes();
|
||||
uint64 downloadedThisPeriod = currentDownloadedBytes - _wasDownloadedBytes;
|
||||
_currentDownloadSpeed = downloadedThisPeriod * (1000000L / microsecondsPassed);
|
||||
_wasDownloadedBytes = currentDownloadedBytes;
|
||||
}
|
||||
|
||||
void FolderDownloadRequest::restart() { start(); }
|
||||
|
||||
void FolderDownloadRequest::finishDownload(Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_fileArrayCallback)
|
||||
(*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
|
||||
}
|
||||
|
||||
double FolderDownloadRequest::getProgress() const {
|
||||
if (_totalFiles == 0 || _totalBytes == 0)
|
||||
return 0;
|
||||
return (double)getDownloadedBytes() / (double)getTotalBytesToDownload();
|
||||
}
|
||||
|
||||
uint64 FolderDownloadRequest::getDownloadedBytes() const {
|
||||
if (_totalFiles == 0)
|
||||
return 0;
|
||||
|
||||
double currentFileProgress = 0;
|
||||
DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
|
||||
if (downloadRequest != nullptr) {
|
||||
currentFileProgress = downloadRequest->getProgress();
|
||||
} else {
|
||||
Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest);
|
||||
if (idDownloadRequest != nullptr)
|
||||
currentFileProgress = idDownloadRequest->getProgress();
|
||||
}
|
||||
|
||||
return _downloadedBytes + (uint64)(currentFileProgress * _currentFile.size());
|
||||
}
|
||||
|
||||
uint64 FolderDownloadRequest::getTotalBytesToDownload() const {
|
||||
return _totalBytes;
|
||||
}
|
||||
|
||||
uint64 FolderDownloadRequest::getDownloadSpeed() const {
|
||||
return _currentDownloadSpeed;
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
79
backends/cloud/folderdownloadrequest.h
Normal file
79
backends/cloud/folderdownloadrequest.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
|
||||
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "gui/object.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
class FolderDownloadRequest: public Networking::Request, public GUI::CommandSender {
|
||||
Storage *_storage;
|
||||
Storage::FileArrayCallback _fileArrayCallback;
|
||||
Common::String _remoteDirectoryPath;
|
||||
Common::Path _localDirectoryPath;
|
||||
bool _recursive;
|
||||
Common::Array<StorageFile> _pendingFiles, _failedFiles;
|
||||
StorageFile _currentFile;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
uint32 _totalFiles;
|
||||
uint64 _downloadedBytes, _totalBytes, _wasDownloadedBytes, _currentDownloadSpeed;
|
||||
|
||||
void start();
|
||||
void directoryListedCallback(const Storage::ListDirectoryResponse &response);
|
||||
void directoryListedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void fileDownloadedCallback(const Storage::BoolResponse &response);
|
||||
void fileDownloadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void downloadNextFile();
|
||||
void finishDownload(Common::Array<StorageFile> &files);
|
||||
public:
|
||||
FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, const Common::String &remoteDirectoryPath, const Common::Path &localDirectoryPath, bool recursive);
|
||||
~FolderDownloadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getProgress() const;
|
||||
|
||||
/** Returns a number of downloaded bytes. */
|
||||
uint64 getDownloadedBytes() const;
|
||||
|
||||
/** Returns a total number of bytes to download. */
|
||||
uint64 getTotalBytesToDownload() const;
|
||||
|
||||
/** Returns average download speed for the last second. */
|
||||
uint64 getDownloadSpeed() const;
|
||||
|
||||
/** Returns remote directory path. */
|
||||
Common::String getRemotePath() const { return _remoteDirectoryPath; }
|
||||
|
||||
/** Returns local directory path. */
|
||||
Common::Path getLocalPath() const { return _localDirectoryPath; }
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h"
|
||||
#include "backends/cloud/googledrive/googledrivestorage.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "googledrivetokenrefresher.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/drive/v3/files?spaces=drive&fields=files%28id,mimeType,modifiedTime,name,size%29,nextPageToken&orderBy=folder,name"
|
||||
//files(id,mimeType,modifiedTime,name,size),nextPageToken
|
||||
|
||||
GoogleDriveListDirectoryByIdRequest::GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, const Common::String &id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _requestedId(id), _storage(storage), _listDirectoryCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
GoogleDriveListDirectoryByIdRequest::~GoogleDriveListDirectoryByIdRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _listDirectoryCallback;
|
||||
}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_files.clear();
|
||||
_ignoreCallback = false;
|
||||
|
||||
makeRequest("");
|
||||
}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::makeRequest(const Common::String &pageToken) {
|
||||
Common::String url = GOOGLEDRIVE_API_FILES;
|
||||
if (pageToken != "")
|
||||
url += "&pageToken=" + pageToken;
|
||||
url += "&q=%27" + _requestedId + "%27+in+parents";
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, const Networking::JsonResponse &>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, const Networking::ErrorResponse &>(this, &GoogleDriveListDirectoryByIdRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::responseCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete response.value;
|
||||
return;
|
||||
}
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "GoogleDriveListDirectoryByIdRequest::responseCallback");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json) {
|
||||
Common::JSONObject responseObject = json->asObject();
|
||||
|
||||
///debug("%s", json->stringify(true).c_str());
|
||||
|
||||
if (responseObject.contains("error") || responseObject.contains("error_summary")) {
|
||||
warning("GoogleDrive returned error: %s", responseObject.getVal("error_summary")->asString().c_str());
|
||||
error.failed = true;
|
||||
error.response = json->stringify();
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
|
||||
|
||||
if (responseObject.contains("files") && responseObject.getVal("files")->isArray()) {
|
||||
Common::JSONArray items = responseObject.getVal("files")->asArray();
|
||||
for (uint32 i = 0; i < items.size(); ++i) {
|
||||
Common::JSONObject item = items[i]->asObject();
|
||||
Common::String id = item.getVal("id")->asString();
|
||||
Common::String name = item.getVal("name")->asString();
|
||||
bool isDirectory = (item.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
|
||||
uint32 size = 0, timestamp = 0;
|
||||
if (item.contains("size") && item.getVal("size")->isString())
|
||||
size = item.getVal("size")->asString().asUint64();
|
||||
if (item.contains("modifiedTime") && item.getVal("modifiedTime")->isString())
|
||||
timestamp = ISO8601::convertToTimestamp(item.getVal("modifiedTime")->asString());
|
||||
|
||||
//as we list directory by id, we can't determine full path for the file, so we leave it empty
|
||||
_files.push_back(StorageFile(id, "", name, size, timestamp, isDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMore = (responseObject.contains("nextPageToken"));
|
||||
|
||||
if (hasMore) {
|
||||
Common::String token = responseObject.getVal("nextPageToken")->asString();
|
||||
makeRequest(token);
|
||||
} else {
|
||||
finishListing(_files);
|
||||
}
|
||||
} else {
|
||||
warning("null, not json");
|
||||
error.failed = true;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::handle() {}
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::restart() { start(); }
|
||||
|
||||
Common::String GoogleDriveListDirectoryByIdRequest::date() const { return _date; }
|
||||
|
||||
void GoogleDriveListDirectoryByIdRequest::finishListing(Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_listDirectoryCallback)
|
||||
(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
|
||||
}
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYBYIDREQUEST_H
|
||||
#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYBYIDREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
class GoogleDriveStorage;
|
||||
|
||||
class GoogleDriveListDirectoryByIdRequest: public Networking::Request {
|
||||
Common::String _requestedId;
|
||||
GoogleDriveStorage *_storage;
|
||||
|
||||
Storage::ListDirectoryCallback _listDirectoryCallback;
|
||||
Common::Array<StorageFile> _files;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void makeRequest(const Common::String &pageToken);
|
||||
void responseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishListing(Common::Array<StorageFile> &files);
|
||||
public:
|
||||
GoogleDriveListDirectoryByIdRequest(GoogleDriveStorage *storage, const Common::String &id, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb);
|
||||
~GoogleDriveListDirectoryByIdRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
249
backends/cloud/googledrive/googledrivestorage.cpp
Normal file
249
backends/cloud/googledrive/googledrivestorage.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/googledrive/googledrivestorage.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/cloud/googledrive/googledrivetokenrefresher.h"
|
||||
#include "backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h"
|
||||
#include "backends/cloud/googledrive/googledriveuploadrequest.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
#define GOOGLEDRIVE_API_FILES_ALT_MEDIA "https://www.googleapis.com/drive/v3/files/%s?alt=media"
|
||||
#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/drive/v3/files"
|
||||
#define GOOGLEDRIVE_API_ABOUT "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user"
|
||||
|
||||
GoogleDriveStorage::GoogleDriveStorage(const Common::String &token, const Common::String &refreshToken, bool enabled):
|
||||
IdStorage(token, refreshToken, enabled) {}
|
||||
|
||||
GoogleDriveStorage::GoogleDriveStorage(const Common::String &code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
GoogleDriveStorage::GoogleDriveStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb) {
|
||||
codeFlowComplete(cb, codeFlowJson);
|
||||
}
|
||||
|
||||
GoogleDriveStorage::~GoogleDriveStorage() {}
|
||||
|
||||
Common::String GoogleDriveStorage::cloudProvider() { return "gdrive"; }
|
||||
|
||||
uint32 GoogleDriveStorage::storageIndex() { return kStorageGoogleDriveId; }
|
||||
|
||||
bool GoogleDriveStorage::needsRefreshToken() { return true; }
|
||||
|
||||
bool GoogleDriveStorage::canReuseRefreshToken() { return true; }
|
||||
|
||||
void GoogleDriveStorage::saveConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String GoogleDriveStorage::name() const {
|
||||
return "Google Drive";
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("GoogleDriveStorage::infoInnerCallback: NULL passed instead of JSON");
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(json, "GoogleDriveStorage::infoInnerCallback")) {
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject jsonInfo = json->asObject();
|
||||
|
||||
Common::String uid, displayName, email;
|
||||
uint64 quotaUsed = 0, quotaAllocated = 0;
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") &&
|
||||
Networking::HttpJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
|
||||
//"me":true, "kind":"drive#user","photoLink": "",
|
||||
//"displayName":"Alexander Tkachev","emailAddress":"alexander@tkachov.ru","permissionId":""
|
||||
Common::JSONObject user = jsonInfo.getVal("user")->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
|
||||
uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
|
||||
displayName = user.getVal("displayName")->asString();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
|
||||
email = user.getVal("emailAddress")->asString();
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
|
||||
Networking::HttpJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
|
||||
//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
|
||||
Common::JSONObject storageQuota = jsonInfo.getVal("storageQuota")->asObject();
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
|
||||
Common::String usage = storageQuota.getVal("usage")->asString();
|
||||
quotaUsed = usage.asUint64();
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(storageQuota, "limit", "GoogleDriveStorage::infoInnerCallback")) {
|
||||
Common::String limit = storageQuota.getVal("limit")->asString();
|
||||
quotaAllocated = limit.asUint64();
|
||||
}
|
||||
}
|
||||
|
||||
CloudMan.setStorageUsername(kStorageGoogleDriveId, email);
|
||||
|
||||
if (outerCallback) {
|
||||
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("GoogleDriveStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (outerCallback) {
|
||||
if (Networking::HttpJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
|
||||
Common::JSONObject jsonInfo = json->asObject();
|
||||
(*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
|
||||
} else {
|
||||
(*outerCallback)(BoolResponse(nullptr, false));
|
||||
}
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
Networking::Request *GoogleDriveStorage::listDirectoryById(const Common::String &id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
if (!callback)
|
||||
callback = new Common::Callback<GoogleDriveStorage, const FileArrayResponse &>(this, &GoogleDriveStorage::printFiles);
|
||||
return addRequest(new GoogleDriveListDirectoryByIdRequest(this, id, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *GoogleDriveStorage::upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
return addRequest(new GoogleDriveUploadRequest(this, path, contents, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *GoogleDriveStorage::streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (callback) {
|
||||
Common::String url = Common::String::format(GOOGLEDRIVE_API_FILES_ALT_MEDIA, Common::percentEncodeString(id).c_str());
|
||||
Common::String header = "Authorization: Bearer " + _token;
|
||||
Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
|
||||
headersList->push_back(header);
|
||||
Networking::NetworkReadStream *stream = Networking::NetworkReadStream::make(url.c_str(), headersList, "");
|
||||
(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
|
||||
}
|
||||
delete callback;
|
||||
delete errorCallback;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::printInfo(const StorageInfoResponse &response) {
|
||||
debug(9, "\nGoogleDriveStorage: user info:");
|
||||
debug(9, "\tname: %s", response.value.name().c_str());
|
||||
debug(9, "\temail: %s", response.value.email().c_str());
|
||||
debug(9, "\tdisk usage: %llu/%llu",
|
||||
(unsigned long long)response.value.used(),
|
||||
(unsigned long long)response.value.available());
|
||||
}
|
||||
|
||||
Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(const Common::String &parentId, const Common::String &directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
|
||||
Common::String url = GOOGLEDRIVE_API_FILES;
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, const BoolResponse &, const Networking::JsonResponse &>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
|
||||
Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONArray parentsArray;
|
||||
parentsArray.push_back(new Common::JSONValue(parentId));
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder"));
|
||||
jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
|
||||
jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!callback)
|
||||
callback = new Common::Callback<GoogleDriveStorage, const StorageInfoResponse &>(this, &GoogleDriveStorage::printInfo);
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &GoogleDriveStorage::infoInnerCallback, callback);
|
||||
Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, GOOGLEDRIVE_API_ABOUT);
|
||||
request->addHeader("Authorization: Bearer " + _token);
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; }
|
||||
|
||||
GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(const Common::String &keyPrefix) {
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("GoogleDriveStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
|
||||
warning("GoogleDriveStorage: no refresh_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new GoogleDriveStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::removeFromConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String GoogleDriveStorage::getRootDirectoryId() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
125
backends/cloud/googledrive/googledrivestorage.h
Normal file
125
backends/cloud/googledrive/googledrivestorage.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
|
||||
#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
|
||||
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
class GoogleDriveStorage: public Id::IdStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
GoogleDriveStorage(const Common::String &token, const Common::String &refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &json);
|
||||
|
||||
/** Returns bool based on JSON response from cloud. */
|
||||
void createDirectoryInnerCallback(BoolCallback outerCallback, const Networking::JsonResponse &json);
|
||||
|
||||
void printInfo(const StorageInfoResponse &response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "gdrive"
|
||||
*/
|
||||
Common::String cloudProvider() override;
|
||||
|
||||
/**
|
||||
* @return kStorageGoogleDriveId
|
||||
*/
|
||||
uint32 storageIndex() override;
|
||||
|
||||
bool needsRefreshToken() override;
|
||||
|
||||
bool canReuseRefreshToken() override;
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
GoogleDriveStorage(const Common::String &code, Networking::ErrorCallback cb);
|
||||
|
||||
/** This constructor extracts tokens from JSON acquired via OAuth code flow. */
|
||||
GoogleDriveStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb);
|
||||
|
||||
~GoogleDriveStorage() override;
|
||||
|
||||
/**
|
||||
* Storage methods, which are used by CloudManager to save
|
||||
* storage in configuration file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save storage data using ConfMan.
|
||||
* @param keyPrefix all saved keys must start with this prefix.
|
||||
* @note every Storage must write keyPrefix + "type" key
|
||||
* with common value (e.g. "Dropbox").
|
||||
*/
|
||||
void saveConfig(const Common::String &keyPrefix) override;
|
||||
|
||||
/**
|
||||
* Return unique storage name.
|
||||
* @returns some unique storage name (for example, "Dropbox (user@example.com)")
|
||||
*/
|
||||
Common::String name() const override;
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
||||
/** Returns Array<StorageFile> - the list of files. */
|
||||
Networking::Request *listDirectoryById(const Common::String &id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns UploadStatus struct with info about uploaded file. */
|
||||
Networking::Request *upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
Networking::Request *streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
Networking::Request *createDirectoryWithParentId(const Common::String &parentId, const Common::String &directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns the StorageInfo struct. */
|
||||
Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath() override;
|
||||
|
||||
/**
|
||||
* Load token and user id from configs and return GoogleDriveStorage for those.
|
||||
* @return pointer to the newly created GoogleDriveStorage or 0 if some problem occurred.
|
||||
*/
|
||||
static GoogleDriveStorage *loadFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
/**
|
||||
* Remove all GoogleDriveStorage-related data from config.
|
||||
*/
|
||||
static void removeFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
Common::String getRootDirectoryId() override;
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
108
backends/cloud/googledrive/googledrivetokenrefresher.cpp
Normal file
108
backends/cloud/googledrive/googledrivetokenrefresher.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/googledrive/googledrivetokenrefresher.h"
|
||||
#include "backends/cloud/googledrive/googledrivestorage.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
GoogleDriveTokenRefresher::GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
|
||||
HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
|
||||
|
||||
GoogleDriveTokenRefresher::~GoogleDriveTokenRefresher() {}
|
||||
|
||||
void GoogleDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
|
||||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("GoogleDriveTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//update headers: first change header with token, then pass those to request
|
||||
for (uint32 i = 0; i < _headersList.size(); ++i) {
|
||||
if (_headersList[i].contains("Authorization")) {
|
||||
_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
|
||||
}
|
||||
}
|
||||
|
||||
//successfully received refreshed token, can restart the original request now
|
||||
retry(0);
|
||||
}
|
||||
|
||||
void GoogleDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
|
||||
if (!json) {
|
||||
//that's probably not an error (200 OK)
|
||||
HttpJsonRequest::finishJson(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonIsObject(json, "GoogleDriveTokenRefresher")) {
|
||||
Common::JSONObject result = json->asObject();
|
||||
long httpResponseCode = -1;
|
||||
if (result.contains("error") && jsonIsObject(result.getVal("error"), "GoogleDriveTokenRefresher")) {
|
||||
//new token needed => request token & then retry original request
|
||||
if (_stream) {
|
||||
httpResponseCode = _stream->httpResponseCode();
|
||||
debug(9, "GoogleDriveTokenRefresher: code = %ld", httpResponseCode);
|
||||
}
|
||||
|
||||
Common::JSONObject error = result.getVal("error")->asObject();
|
||||
bool irrecoverable = true;
|
||||
|
||||
uint32 code = 0xFFFFFFFF; // Invalid
|
||||
Common::String message;
|
||||
if (jsonContainsIntegerNumber(error, "code", "GoogleDriveTokenRefresher")) {
|
||||
code = error.getVal("code")->asIntegerNumber();
|
||||
debug(9, "GoogleDriveTokenRefresher: code = %u", code);
|
||||
}
|
||||
|
||||
if (jsonContainsString(error, "message", "GoogleDriveTokenRefresher")) {
|
||||
message = error.getVal("message")->asString();
|
||||
debug(9, "GoogleDriveTokenRefresher: message = %s", message.c_str());
|
||||
}
|
||||
|
||||
if (code == 401 || message == "Invalid Credentials")
|
||||
irrecoverable = false;
|
||||
|
||||
if (irrecoverable) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<GoogleDriveTokenRefresher, const Storage::BoolResponse &>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//notify user of success
|
||||
HttpJsonRequest::finishJson(json);
|
||||
}
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
47
backends/cloud/googledrive/googledrivetokenrefresher.h
Normal file
47
backends/cloud/googledrive/googledrivetokenrefresher.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVETOKENREFRESHER_H
|
||||
#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVETOKENREFRESHER_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
class GoogleDriveStorage;
|
||||
|
||||
class GoogleDriveTokenRefresher: public Networking::HttpJsonRequest {
|
||||
GoogleDriveStorage *_parentStorage;
|
||||
|
||||
void tokenRefreshed(const Storage::BoolResponse &response);
|
||||
|
||||
void finishJson(const Common::JSONValue *json) override;
|
||||
public:
|
||||
GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
|
||||
~GoogleDriveTokenRefresher() override;
|
||||
};
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
344
backends/cloud/googledrive/googledriveuploadrequest.cpp
Normal file
344
backends/cloud/googledrive/googledriveuploadrequest.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/googledrive/googledriveuploadrequest.h"
|
||||
#include "backends/cloud/googledrive/googledrivestorage.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "googledrivetokenrefresher.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/upload/drive/v3/files"
|
||||
|
||||
GoogleDriveUploadRequest::GoogleDriveUploadRequest(GoogleDriveStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
GoogleDriveUploadRequest::~GoogleDriveUploadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _contentsStream;
|
||||
delete _uploadCallback;
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
|
||||
warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveUploadRequest::start: couldn't restart because failed to seek(0)", -1));
|
||||
return;
|
||||
}
|
||||
_resolvedId = ""; //used to update file contents
|
||||
_parentId = ""; //used to create file within parent directory
|
||||
_serverReceivedBytes = 0;
|
||||
_ignoreCallback = false;
|
||||
|
||||
resolveId();
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::resolveId() {
|
||||
//check whether such file already exists
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveUploadRequest, const Storage::UploadResponse &>(this, &GoogleDriveUploadRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::idResolveFailedCallback);
|
||||
_workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
_resolvedId = response.value.id();
|
||||
startUpload();
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::idResolveFailedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//not resolved => error or no such file
|
||||
if (error.response.contains("no such file found in its parent directory")) {
|
||||
//parent's id after the '\n'
|
||||
Common::String parentId = error.response;
|
||||
for (uint32 i = 0; i < parentId.size(); ++i)
|
||||
if (parentId[i] == '\n') {
|
||||
parentId.erase(0, i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
_parentId = parentId;
|
||||
startUpload();
|
||||
return;
|
||||
}
|
||||
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::startUpload() {
|
||||
Common::String name = _savePath;
|
||||
for (uint32 i = name.size(); i > 0; --i) {
|
||||
if (name[i - 1] == '/' || name[i - 1] == '\\') {
|
||||
name.erase(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String url = GOOGLEDRIVE_API_FILES;
|
||||
if (_resolvedId != "")
|
||||
url += "/" + Common::percentEncodeString(_resolvedId);
|
||||
url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
|
||||
Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, const Networking::JsonResponse &>(this, &GoogleDriveUploadRequest::startUploadCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
if (_resolvedId != "")
|
||||
request->usePatch();
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
if (_resolvedId != "") {
|
||||
jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
|
||||
} else {
|
||||
Common::JSONArray parentsArray;
|
||||
parentsArray.push_back(new Common::JSONValue(_parentId));
|
||||
jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
|
||||
}
|
||||
jsonRequestParameters.setVal("name", new Common::JSONValue(name));
|
||||
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::startUploadCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "GoogleDriveUploadRequest::startUploadCallback", -1);
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq) {
|
||||
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
|
||||
if (stream) {
|
||||
long code = stream->httpResponseCode();
|
||||
if (code == 200) {
|
||||
Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
|
||||
if (headers.contains("location")) {
|
||||
_uploadUrl = headers["location"];
|
||||
uploadNextPart();
|
||||
return;
|
||||
} else {
|
||||
error.response += ": response must provide Location header, but it's not there";
|
||||
}
|
||||
} else {
|
||||
error.response += ": response is not 200 OK";
|
||||
}
|
||||
|
||||
error.httpResponseCode = code;
|
||||
} else {
|
||||
error.response += ": missing response stream [improbable]";
|
||||
}
|
||||
} else {
|
||||
error.response += ": missing request object [improbable]";
|
||||
}
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
delete json;
|
||||
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::startUploadErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::uploadNextPart() {
|
||||
const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
|
||||
Common::String url = _uploadUrl;
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, const Networking::JsonResponse &>(this, &GoogleDriveUploadRequest::partUploadedCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::partUploadedErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->usePut();
|
||||
|
||||
uint32 oldPos = _contentsStream->pos();
|
||||
if (oldPos != _serverReceivedBytes) {
|
||||
if (!_contentsStream->seek(_serverReceivedBytes)) {
|
||||
warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%llu)", (unsigned long long)_serverReceivedBytes);
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveUploadRequest::uploadNextPart: seek() didn't work", -1));
|
||||
return;
|
||||
}
|
||||
oldPos = _serverReceivedBytes;
|
||||
}
|
||||
|
||||
byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
|
||||
uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
|
||||
if (size != 0)
|
||||
request->setBuffer(buffer, size);
|
||||
|
||||
if (_uploadUrl != "") {
|
||||
if (_contentsStream->pos() == 0)
|
||||
request->addHeader(Common::String::format("Content-Length: 0"));
|
||||
else
|
||||
request->addHeader(Common::String::format("Content-Range: bytes %u-%lu/%lu", oldPos, long(_contentsStream->pos() - 1), long(_contentsStream->size())));
|
||||
}
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
|
||||
//308 Resume Incomplete, with Range: X-Y header
|
||||
if (!stream)
|
||||
return false;
|
||||
if (stream->httpResponseCode() != 308)
|
||||
return false; //seriously
|
||||
|
||||
Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
|
||||
if (headers.contains("range")) {
|
||||
Common::String range = headers["range"];
|
||||
for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
|
||||
const char *needle = (rangeTry == 0 ? "0-" : "bytes=0-"); //if it lost the first part, I refuse to talk with it
|
||||
uint32 needleLength = (rangeTry == 0 ? 2 : 8);
|
||||
|
||||
if (range.hasPrefix(needle)) {
|
||||
range.erase(0, needleLength);
|
||||
_serverReceivedBytes = range.asUint64() + 1;
|
||||
uploadNextPart();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::partUploadedCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "", -1);
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq) {
|
||||
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
|
||||
if (stream) {
|
||||
long code = stream->httpResponseCode();
|
||||
error.httpResponseCode = code;
|
||||
if (code == 308 && handleHttp308(stream)) {
|
||||
delete response.value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (json->isObject()) {
|
||||
Common::JSONObject object = json->asObject();
|
||||
|
||||
if (object.contains("error")) {
|
||||
warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
|
||||
//finished
|
||||
Common::String id = object.getVal("id")->asString();
|
||||
Common::String name = object.getVal("name")->asString();
|
||||
bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
|
||||
uint32 size = 0, timestamp = 0;
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
|
||||
size = object.getVal("size")->asString().asUint64();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
|
||||
timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
|
||||
|
||||
finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
|
||||
warning("GoogleDriveUploadRequest: no file info to return");
|
||||
finishUpload(StorageFile(_savePath, 0, 0, false));
|
||||
} else {
|
||||
uploadNextPart();
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::partUploadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::HttpJsonRequest *rq = (Networking::HttpJsonRequest *)error.request;
|
||||
if (rq) {
|
||||
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
|
||||
if (stream) {
|
||||
long code = stream->httpResponseCode();
|
||||
if (code == 308 && handleHttp308(stream)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void GoogleDriveUploadRequest::handle() {}
|
||||
|
||||
void GoogleDriveUploadRequest::restart() { start(); }
|
||||
|
||||
void GoogleDriveUploadRequest::finishUpload(const StorageFile &file) {
|
||||
Request::finishSuccess();
|
||||
if (_uploadCallback)
|
||||
(*_uploadCallback)(Storage::UploadResponse(this, file));
|
||||
}
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
69
backends/cloud/googledrive/googledriveuploadrequest.h
Normal file
69
backends/cloud/googledrive/googledriveuploadrequest.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEUPLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEUPLOADREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
class GoogleDriveStorage;
|
||||
|
||||
class GoogleDriveUploadRequest: public Networking::Request {
|
||||
GoogleDriveStorage *_storage;
|
||||
Common::String _savePath;
|
||||
Common::SeekableReadStream *_contentsStream;
|
||||
Storage::UploadCallback _uploadCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _resolvedId, _parentId;
|
||||
Common::String _uploadUrl;
|
||||
uint64 _serverReceivedBytes;
|
||||
|
||||
void start();
|
||||
void resolveId();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveFailedCallback(const Networking::ErrorResponse &error);
|
||||
void startUpload();
|
||||
void startUploadCallback(const Networking::JsonResponse &response);
|
||||
void startUploadErrorCallback(const Networking::ErrorResponse &error);
|
||||
void uploadNextPart();
|
||||
void partUploadedCallback(const Networking::JsonResponse &response);
|
||||
void partUploadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
bool handleHttp308(const Networking::NetworkReadStream *stream);
|
||||
void finishUpload(const StorageFile &status);
|
||||
|
||||
public:
|
||||
GoogleDriveUploadRequest(GoogleDriveStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
|
||||
~GoogleDriveUploadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace GoogleDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
162
backends/cloud/id/idcreatedirectoryrequest.cpp
Normal file
162
backends/cloud/id/idcreatedirectoryrequest.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/idcreatedirectoryrequest.h"
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdCreateDirectoryRequest::IdCreateDirectoryRequest(IdStorage *storage, const Common::String &parentPath, const Common::String &directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb),
|
||||
_requestedParentPath(parentPath), _requestedDirectoryName(directoryName), _storage(storage), _boolCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
IdCreateDirectoryRequest::~IdCreateDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_ignoreCallback = false;
|
||||
|
||||
//the only exception when we create parent folder - is when it's ScummVM/ base folder
|
||||
Common::String prefix = _requestedParentPath;
|
||||
if (prefix.size() > 7)
|
||||
prefix.erase(7);
|
||||
if (prefix.equalsIgnoreCase("ScummVM")) {
|
||||
Storage::BoolCallback callback = new Common::Callback<IdCreateDirectoryRequest, const Storage::BoolResponse &>(this, &IdCreateDirectoryRequest::createdBaseDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<IdCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback);
|
||||
_workingRequest = _storage->createDirectory("ScummVM", callback, failureCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
resolveId();
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::createdBaseDirectoryCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
resolveId();
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::createdBaseDirectoryErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::resolveId() {
|
||||
//check whether such folder already exists
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<IdCreateDirectoryRequest, const Storage::UploadResponse &>(this, &IdCreateDirectoryRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &IdCreateDirectoryRequest::idResolveFailedCallback);
|
||||
Common::String path = _requestedParentPath;
|
||||
if (_requestedParentPath != "")
|
||||
path += "/";
|
||||
path += _requestedDirectoryName;
|
||||
_workingRequest = _storage->resolveFileId(path, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
//resolved => folder already exists
|
||||
finishCreation(false);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::idResolveFailedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
|
||||
//not resolved => folder not exists
|
||||
if (error.response.contains("no such file found in its parent directory")) {
|
||||
//parent's id after the '\n'
|
||||
Common::String parentId = error.response;
|
||||
for (uint32 i = 0; i < parentId.size(); ++i)
|
||||
if (parentId[i] == '\n') {
|
||||
parentId.erase(0, i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
Storage::BoolCallback callback = new Common::Callback<IdCreateDirectoryRequest, const Storage::BoolResponse &>(this, &IdCreateDirectoryRequest::createdDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<IdCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &IdCreateDirectoryRequest::createdDirectoryErrorCallback);
|
||||
_workingRequest = _storage->createDirectoryWithParentId(parentId, _requestedDirectoryName, callback, failureCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::createdDirectoryCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
finishCreation(response.value);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::createdDirectoryErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdCreateDirectoryRequest::handle() {}
|
||||
|
||||
void IdCreateDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String IdCreateDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void IdCreateDirectoryRequest::finishCreation(bool success) {
|
||||
Request::finishSuccess();
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
64
backends/cloud/id/idcreatedirectoryrequest.h
Normal file
64
backends/cloud/id/idcreatedirectoryrequest.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDCREATEDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_ID_IDCREATEDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage;
|
||||
|
||||
class IdCreateDirectoryRequest: public Networking::Request {
|
||||
Common::String _requestedParentPath;
|
||||
Common::String _requestedDirectoryName;
|
||||
IdStorage *_storage;
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void createdBaseDirectoryCallback(const Storage::BoolResponse &response);
|
||||
void createdBaseDirectoryErrorCallback(const Networking::ErrorResponse &error);
|
||||
void resolveId();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveFailedCallback(const Networking::ErrorResponse &error);
|
||||
void createdDirectoryCallback(const Storage::BoolResponse &response);
|
||||
void createdDirectoryErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishCreation(bool success);
|
||||
public:
|
||||
IdCreateDirectoryRequest(IdStorage *storage, const Common::String &parentPath, const Common::String &directoryName, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
|
||||
~IdCreateDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
107
backends/cloud/id/iddownloadrequest.cpp
Normal file
107
backends/cloud/id/iddownloadrequest.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/iddownloadrequest.h"
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
#include "backends/cloud/downloadrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdDownloadRequest::IdDownloadRequest(IdStorage *storage, const Common::String &remotePath, const Common::Path &localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _requestedFile(remotePath), _requestedLocalFile(localPath), _storage(storage), _boolCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
IdDownloadRequest::~IdDownloadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
}
|
||||
|
||||
void IdDownloadRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_ignoreCallback = false;
|
||||
|
||||
//find file's id
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<IdDownloadRequest, const Storage::UploadResponse &>(this, &IdDownloadRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, const Networking::ErrorResponse &>(this, &IdDownloadRequest::idResolveFailedCallback);
|
||||
_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdDownloadRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Storage::BoolCallback innerCallback = new Common::Callback<IdDownloadRequest, const Storage::BoolResponse &>(this, &IdDownloadRequest::downloadCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdDownloadRequest, const Networking::ErrorResponse &>(this, &IdDownloadRequest::downloadErrorCallback);
|
||||
_workingRequest = _storage->downloadById(response.value.id(), _requestedLocalFile, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdDownloadRequest::idResolveFailedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdDownloadRequest::downloadCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishDownload(response.value);
|
||||
}
|
||||
|
||||
void IdDownloadRequest::downloadErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdDownloadRequest::handle() {}
|
||||
|
||||
void IdDownloadRequest::restart() { start(); }
|
||||
|
||||
void IdDownloadRequest::finishDownload(bool success) {
|
||||
Request::finishSuccess();
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
double IdDownloadRequest::getProgress() const {
|
||||
DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest);
|
||||
if (downloadRequest == nullptr)
|
||||
return 0; // resolving id still
|
||||
|
||||
// id resolve is 10 % and download is the other 90 %
|
||||
return 0.1 + 0.9 * downloadRequest->getProgress(); // downloading
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
62
backends/cloud/id/iddownloadrequest.h
Normal file
62
backends/cloud/id/iddownloadrequest.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDDOWNLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_ID_IDDOWNLOADREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage;
|
||||
|
||||
class IdDownloadRequest: public Networking::Request {
|
||||
Common::String _requestedFile;
|
||||
Common::Path _requestedLocalFile;
|
||||
IdStorage *_storage;
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
|
||||
void start();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveFailedCallback(const Networking::ErrorResponse &error);
|
||||
void downloadCallback(const Storage::BoolResponse &response);
|
||||
void downloadErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishDownload(bool success);
|
||||
public:
|
||||
IdDownloadRequest(IdStorage *storage, const Common::String &remotePath, const Common::Path &localPath, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
|
||||
~IdDownloadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getProgress() const;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
140
backends/cloud/id/idlistdirectoryrequest.cpp
Normal file
140
backends/cloud/id/idlistdirectoryrequest.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/idlistdirectoryrequest.h"
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdListDirectoryRequest::IdListDirectoryRequest(IdStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
|
||||
Networking::Request(nullptr, ecb),
|
||||
_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
IdListDirectoryRequest::~IdListDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _listDirectoryCallback;
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_files.clear();
|
||||
_directoriesQueue.clear();
|
||||
_currentDirectory = StorageFile();
|
||||
_ignoreCallback = false;
|
||||
|
||||
//find out that directory's id
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<IdListDirectoryRequest, const Storage::UploadResponse &>(this, &IdListDirectoryRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdListDirectoryRequest, const Networking::ErrorResponse &>(this, &IdListDirectoryRequest::idResolveErrorCallback);
|
||||
_workingRequest = _storage->resolveFileId(_requestedPath, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
StorageFile directory = response.value;
|
||||
directory.setPath(_requestedPath);
|
||||
_directoriesQueue.push_back(directory);
|
||||
listNextDirectory();
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::idResolveErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::listNextDirectory() {
|
||||
if (_directoriesQueue.empty()) {
|
||||
finishListing(_files);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentDirectory = _directoriesQueue.back();
|
||||
_directoriesQueue.pop_back();
|
||||
|
||||
Storage::FileArrayCallback callback = new Common::Callback<IdListDirectoryRequest, const Storage::FileArrayResponse &>(this, &IdListDirectoryRequest::listedDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<IdListDirectoryRequest, const Networking::ErrorResponse &>(this, &IdListDirectoryRequest::listedDirectoryErrorCallback);
|
||||
_workingRequest = _storage->listDirectoryById(_currentDirectory.id(), callback, failureCallback);
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::listedDirectoryCallback(const Storage::FileArrayResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
for (uint32 i = 0; i < response.value.size(); ++i) {
|
||||
StorageFile file = response.value[i];
|
||||
Common::String path = _currentDirectory.path();
|
||||
if (path.size() && path.lastChar() != '/' && path.lastChar() != '\\')
|
||||
path += '/';
|
||||
path += file.name();
|
||||
file.setPath(path);
|
||||
_files.push_back(file);
|
||||
if (_requestedRecursive && file.isDirectory()) {
|
||||
_directoriesQueue.push_back(file);
|
||||
}
|
||||
}
|
||||
|
||||
listNextDirectory();
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::listedDirectoryErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdListDirectoryRequest::handle() {}
|
||||
|
||||
void IdListDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String IdListDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void IdListDirectoryRequest::finishListing(const Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_listDirectoryCallback)
|
||||
(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
65
backends/cloud/id/idlistdirectoryrequest.h
Normal file
65
backends/cloud/id/idlistdirectoryrequest.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDLISTDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_ID_IDLISTDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage;
|
||||
|
||||
class IdListDirectoryRequest: public Networking::Request {
|
||||
Common::String _requestedPath;
|
||||
bool _requestedRecursive;
|
||||
IdStorage *_storage;
|
||||
Storage::ListDirectoryCallback _listDirectoryCallback;
|
||||
Common::Array<StorageFile> _files;
|
||||
Common::Array<StorageFile> _directoriesQueue;
|
||||
StorageFile _currentDirectory;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveErrorCallback(const Networking::ErrorResponse &error);
|
||||
void listNextDirectory();
|
||||
void listedDirectoryCallback(const Storage::FileArrayResponse &response);
|
||||
void listedDirectoryErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishListing(const Common::Array<StorageFile> &files);
|
||||
public:
|
||||
IdListDirectoryRequest(IdStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
|
||||
~IdListDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
135
backends/cloud/id/idresolveidrequest.cpp
Normal file
135
backends/cloud/id/idresolveidrequest.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/idresolveidrequest.h"
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdResolveIdRequest::IdResolveIdRequest(IdStorage *storage, const Common::String &path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive):
|
||||
Networking::Request(nullptr, ecb),
|
||||
_requestedPath(path), _storage(storage), _uploadCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
IdResolveIdRequest::~IdResolveIdRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _uploadCallback;
|
||||
}
|
||||
|
||||
void IdResolveIdRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_currentDirectory = "";
|
||||
_currentDirectoryId = _storage->getRootDirectoryId();
|
||||
_ignoreCallback = false;
|
||||
|
||||
listNextDirectory(StorageFile(_currentDirectoryId, 0, 0, true));
|
||||
}
|
||||
|
||||
void IdResolveIdRequest::listNextDirectory(const StorageFile &fileToReturn) {
|
||||
if (_currentDirectory.equalsIgnoreCase(_requestedPath)) {
|
||||
finishFile(fileToReturn);
|
||||
return;
|
||||
}
|
||||
|
||||
Storage::FileArrayCallback callback = new Common::Callback<IdResolveIdRequest, const Storage::FileArrayResponse &>(this, &IdResolveIdRequest::listedDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<IdResolveIdRequest, const Networking::ErrorResponse &>(this, &IdResolveIdRequest::listedDirectoryErrorCallback);
|
||||
_workingRequest = _storage->listDirectoryById(_currentDirectoryId, callback, failureCallback);
|
||||
}
|
||||
|
||||
void IdResolveIdRequest::listedDirectoryCallback(const Storage::FileArrayResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Common::String currentLevelName = _requestedPath;
|
||||
///debug(9, "'%s'", currentLevelName.c_str());
|
||||
if (_currentDirectory.size())
|
||||
currentLevelName.erase(0, _currentDirectory.size());
|
||||
if (currentLevelName.size() && (currentLevelName[0] == '/' || currentLevelName[0] == '\\'))
|
||||
currentLevelName.erase(0, 1);
|
||||
///debug(9, "'%s'", currentLevelName.c_str());
|
||||
for (uint32 i = 0; i < currentLevelName.size(); ++i) {
|
||||
if (currentLevelName[i] == '/' || currentLevelName[i] == '\\') {
|
||||
currentLevelName.erase(i);
|
||||
///debug(9, "'%s'", currentLevelName.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String path = _currentDirectory;
|
||||
if (path != "")
|
||||
path += "/";
|
||||
path += currentLevelName;
|
||||
bool lastLevel = (path.equalsIgnoreCase(_requestedPath));
|
||||
|
||||
///debug(9, "IdResolveIdRequest: searching for '%s' in '%s'", currentLevelName.c_str(), _currentDirectory.c_str());
|
||||
|
||||
const Common::Array<StorageFile> &files = response.value;
|
||||
bool found = false;
|
||||
for (uint32 i = 0; i < files.size(); ++i) {
|
||||
if ((files[i].isDirectory() || lastLevel) && files[i].name().equalsIgnoreCase(currentLevelName)) {
|
||||
if (_currentDirectory != "")
|
||||
_currentDirectory += "/";
|
||||
_currentDirectory += files[i].name();
|
||||
_currentDirectoryId = files[i].id();
|
||||
///debug(9, "IdResolveIdRequest: found it! new directory and its id: '%s', '%s'", _currentDirectory.c_str(), _currentDirectoryId.c_str());
|
||||
listNextDirectory(files[i]);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
if (lastLevel)
|
||||
finishError(Networking::ErrorResponse(this, false, true, Common::String("no such file found in its parent directory\n") + _currentDirectoryId, 404));
|
||||
else
|
||||
finishError(Networking::ErrorResponse(this, false, true, "subdirectory not found", 400));
|
||||
}
|
||||
}
|
||||
|
||||
void IdResolveIdRequest::listedDirectoryErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdResolveIdRequest::handle() {}
|
||||
|
||||
void IdResolveIdRequest::restart() { start(); }
|
||||
|
||||
void IdResolveIdRequest::finishFile(const StorageFile &file) {
|
||||
Request::finishSuccess();
|
||||
if (_uploadCallback)
|
||||
(*_uploadCallback)(Storage::UploadResponse(this, file));
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
59
backends/cloud/id/idresolveidrequest.h
Normal file
59
backends/cloud/id/idresolveidrequest.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDRESOLVEIDREQUEST_H
|
||||
#define BACKENDS_CLOUD_ID_IDRESOLVEIDREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage;
|
||||
|
||||
class IdResolveIdRequest: public Networking::Request {
|
||||
Common::String _requestedPath;
|
||||
IdStorage *_storage;
|
||||
Storage::UploadCallback _uploadCallback;
|
||||
Common::String _currentDirectory;
|
||||
Common::String _currentDirectoryId;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
|
||||
void start();
|
||||
void listNextDirectory(const StorageFile &fileToReturn);
|
||||
void listedDirectoryCallback(const Storage::FileArrayResponse &response);
|
||||
void listedDirectoryErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishFile(const StorageFile &file);
|
||||
public:
|
||||
IdResolveIdRequest(IdStorage *storage, const Common::String &path, Storage::UploadCallback cb, Networking::ErrorCallback ecb, bool recursive = false); //TODO: why upload?
|
||||
~IdResolveIdRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
112
backends/cloud/id/idstorage.cpp
Normal file
112
backends/cloud/id/idstorage.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/idstorage.h"
|
||||
#include "backends/cloud/id/idcreatedirectoryrequest.h"
|
||||
#include "backends/cloud/id/iddownloadrequest.h"
|
||||
#include "backends/cloud/id/idlistdirectoryrequest.h"
|
||||
#include "backends/cloud/id/idresolveidrequest.h"
|
||||
#include "backends/cloud/id/idstreamfilerequest.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdStorage::IdStorage() {}
|
||||
|
||||
IdStorage::IdStorage(const Common::String &token, const Common::String &refreshToken, bool enabled):
|
||||
BaseStorage(token, refreshToken, enabled) {}
|
||||
|
||||
IdStorage::~IdStorage() {}
|
||||
|
||||
void IdStorage::printFiles(const FileArrayResponse &response) {
|
||||
debug(9, "IdStorage: files:");
|
||||
const Common::Array<StorageFile> &files = response.value;
|
||||
for (uint32 i = 0; i < files.size(); ++i) {
|
||||
debug(9, "\t%s%s", files[i].name().c_str(), files[i].isDirectory() ? " (directory)" : "");
|
||||
debug(9, "\t%s", files[i].path().c_str());
|
||||
debug(9, "\t%s", files[i].id().c_str());
|
||||
debug(9, " ");
|
||||
}
|
||||
}
|
||||
|
||||
void IdStorage::printBool(const BoolResponse &response) {
|
||||
debug(9, "IdStorage: bool: %s", response.value ? "true" : "false");
|
||||
}
|
||||
|
||||
void IdStorage::printFile(const UploadResponse &response) {
|
||||
debug(9, "\nIdStorage: uploaded file info:");
|
||||
debug(9, "\tid: %s", response.value.path().c_str());
|
||||
debug(9, "\tname: %s", response.value.name().c_str());
|
||||
debug(9, "\tsize: %u", response.value.size());
|
||||
debug(9, "\ttimestamp: %u", response.value.timestamp());
|
||||
}
|
||||
|
||||
Storage::ListDirectoryCallback IdStorage::getPrintFilesCallback() {
|
||||
return new Common::Callback<IdStorage, const FileArrayResponse &>(this, &IdStorage::printFiles);
|
||||
}
|
||||
|
||||
Networking::Request *IdStorage::resolveFileId(const Common::String &path, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
if (!callback)
|
||||
callback = new Common::Callback<IdStorage, const UploadResponse &>(this, &IdStorage::printFile);
|
||||
return addRequest(new IdResolveIdRequest(this, path, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *IdStorage::listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
if (!callback)
|
||||
callback = new Common::Callback<IdStorage, const FileArrayResponse &>(this, &IdStorage::printFiles);
|
||||
return addRequest(new IdListDirectoryRequest(this, path, callback, errorCallback, recursive));
|
||||
}
|
||||
|
||||
Networking::Request *IdStorage::createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
if (!callback)
|
||||
callback = new Common::Callback<IdStorage, const BoolResponse &>(this, &IdStorage::printBool);
|
||||
|
||||
//find out the parent path and directory name
|
||||
Common::String parentPath = "", directoryName = path;
|
||||
for (uint32 i = path.size(); i > 0; --i) {
|
||||
if (path[i - 1] == '/' || path[i - 1] == '\\') {
|
||||
parentPath = path;
|
||||
parentPath.erase(i - 1);
|
||||
directoryName.erase(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return addRequest(new IdCreateDirectoryRequest(this, parentPath, directoryName, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *IdStorage::streamFile(const Common::String &path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
|
||||
return addRequest(new IdStreamFileRequest(this, path, outerCallback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *IdStorage::download(const Common::String &remotePath, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
return addRequest(new IdDownloadRequest(this, remotePath, localPath, callback, errorCallback));
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
84
backends/cloud/id/idstorage.h
Normal file
84
backends/cloud/id/idstorage.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDSTORAGE_H
|
||||
#define BACKENDS_CLOUD_ID_IDSTORAGE_H
|
||||
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
/*
|
||||
* Id::IdStorage is a special base class, which is created
|
||||
* to simplify adding new storages which use ids instead of
|
||||
* paths in their API.
|
||||
*
|
||||
* Some Requests are already implemented, and Storage based
|
||||
* on IdStorage needs to override/implement a few basic things.
|
||||
*
|
||||
* For example, ListDirectoryRequest and ResolveIdRequests are
|
||||
* based on listDirectoryById() and getRootDirectoryId() methods.
|
||||
* Implementing these you'll get id resolving and directory
|
||||
* listing by path.
|
||||
*/
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage: public Cloud::BaseStorage {
|
||||
protected:
|
||||
void printFiles(const FileArrayResponse &response);
|
||||
void printBool(const BoolResponse &response);
|
||||
void printFile(const UploadResponse &response);
|
||||
|
||||
ListDirectoryCallback getPrintFilesCallback();
|
||||
|
||||
public:
|
||||
IdStorage();
|
||||
IdStorage(const Common::String &token, const Common::String &refreshToken, bool enabled);
|
||||
~IdStorage() override;
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
||||
/** Returns StorageFile with the resolved file's id. */
|
||||
virtual Networking::Request *resolveFileId(const Common::String &path, UploadCallback callback, Networking::ErrorCallback errorCallback);
|
||||
|
||||
/** Returns ListDirectoryStatus struct with list of files. */
|
||||
Networking::Request *listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) override;
|
||||
virtual Networking::Request *listDirectoryById(const Common::String &id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
Networking::Request *createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
virtual Networking::Request *createDirectoryWithParentId(const Common::String &parentId, const Common::String &name, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
Networking::Request *streamFile(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
virtual Networking::Request *streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override = 0;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
Networking::Request *download(const Common::String &remotePath, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
virtual Common::String getRootDirectoryId() = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
97
backends/cloud/id/idstreamfilerequest.cpp
Normal file
97
backends/cloud/id/idstreamfilerequest.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/id/idstreamfilerequest.h"
|
||||
#include "backends/cloud/id/idstorage.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdStreamFileRequest::IdStreamFileRequest(IdStorage *storage, const Common::String &path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _requestedFile(path), _storage(storage), _streamCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
IdStreamFileRequest::~IdStreamFileRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _streamCallback;
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_ignoreCallback = false;
|
||||
|
||||
//find file's id
|
||||
Storage::UploadCallback innerCallback = new Common::Callback<IdStreamFileRequest, const Storage::UploadResponse &>(this, &IdStreamFileRequest::idResolvedCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, const Networking::ErrorResponse &>(this, &IdStreamFileRequest::idResolveFailedCallback);
|
||||
_workingRequest = _storage->resolveFileId(_requestedFile, innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::idResolvedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::NetworkReadStreamCallback innerCallback = new Common::Callback<IdStreamFileRequest, const Networking::NetworkReadStreamResponse &>(this, &IdStreamFileRequest::streamFileCallback);
|
||||
Networking::ErrorCallback innerErrorCallback = new Common::Callback<IdStreamFileRequest, const Networking::ErrorResponse &>(this, &IdStreamFileRequest::streamFileErrorCallback);
|
||||
_workingRequest = _storage->streamFileById(response.value.id(), innerCallback, innerErrorCallback);
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::idResolveFailedCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::streamFileCallback(const Networking::NetworkReadStreamResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishStream(response.value);
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::streamFileErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void IdStreamFileRequest::handle() {}
|
||||
|
||||
void IdStreamFileRequest::restart() { start(); }
|
||||
|
||||
void IdStreamFileRequest::finishStream(Networking::NetworkReadStream *stream) {
|
||||
Request::finishSuccess();
|
||||
if (_streamCallback)
|
||||
(*_streamCallback)(Networking::NetworkReadStreamResponse(this, stream));
|
||||
}
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
58
backends/cloud/id/idstreamfilerequest.h
Normal file
58
backends/cloud/id/idstreamfilerequest.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ID_IDSTREAMFILEREQUEST_H
|
||||
#define BACKENDS_CLOUD_ID_IDSTREAMFILEREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage;
|
||||
|
||||
class IdStreamFileRequest: public Networking::Request {
|
||||
Common::String _requestedFile;
|
||||
IdStorage *_storage;
|
||||
Networking::NetworkReadStreamCallback _streamCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
|
||||
void start();
|
||||
void idResolvedCallback(const Storage::UploadResponse &response);
|
||||
void idResolveFailedCallback(const Networking::ErrorResponse &error);
|
||||
void streamFileCallback(const Networking::NetworkReadStreamResponse &response);
|
||||
void streamFileErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishStream(Networking::NetworkReadStream *stream);
|
||||
public:
|
||||
IdStreamFileRequest(IdStorage *storage, const Common::String &path, Networking::NetworkReadStreamCallback cb, Networking::ErrorCallback ecb);
|
||||
~IdStreamFileRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace Id
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
99
backends/cloud/iso8601.cpp
Normal file
99
backends/cloud/iso8601.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/iso8601.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace {
|
||||
|
||||
Common::String getSubstring(const Common::String &s, uint32 beginning, uint32 ending) {
|
||||
//beginning inclusive, ending exclusive
|
||||
Common::String result = s;
|
||||
result.erase(ending);
|
||||
result.erase(0, beginning);
|
||||
return result;
|
||||
}
|
||||
|
||||
int find(const char *cstr, uint32 startPosition, char needle) {
|
||||
const char *res = strchr(cstr + startPosition, needle);
|
||||
if (res == nullptr)
|
||||
return -1;
|
||||
return res - cstr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Cloud {
|
||||
namespace ISO8601 {
|
||||
|
||||
uint32 convertToTimestamp(const Common::String &iso8601Date) {
|
||||
//2015-05-12T15:50:38Z
|
||||
const char *cstr = iso8601Date.c_str();
|
||||
int firstHyphen = find(cstr, 0, '-');
|
||||
int secondHyphen = find(cstr, firstHyphen + 1, '-');
|
||||
int tSeparator = find(cstr, secondHyphen + 1, 'T');
|
||||
int firstColon = find(cstr, tSeparator + 1, ':');
|
||||
int secondColon = find(cstr, firstColon + 1, ':');
|
||||
int zSeparator = find(cstr, secondColon + 1, 'Z');
|
||||
if (zSeparator == -1)
|
||||
zSeparator = find(cstr, secondColon + 1, '-'); // Box's RFC 3339
|
||||
//now note '+1' which means if there ever was '-1' result of find(), we still did a valid find() from 0th char
|
||||
|
||||
Common::String year = getSubstring(iso8601Date, 0, firstHyphen);
|
||||
Common::String month = getSubstring(iso8601Date, firstHyphen + 1, secondHyphen);
|
||||
Common::String day = getSubstring(iso8601Date, secondHyphen + 1, tSeparator);
|
||||
Common::String hour = getSubstring(iso8601Date, tSeparator + 1, firstColon);
|
||||
Common::String minute = getSubstring(iso8601Date, firstColon + 1, secondColon);
|
||||
Common::String second = getSubstring(iso8601Date, secondColon + 1, zSeparator);
|
||||
//now note only 'ending' argument was not '+1' (which means I could've make that function such that -1 means 'until the end')
|
||||
|
||||
int Y = atoi(year.c_str());
|
||||
int M = atoi(month.c_str());
|
||||
int D = atoi(day.c_str());
|
||||
int h = atoi(hour.c_str());
|
||||
int m = atoi(minute.c_str());
|
||||
int s = atoi(second.c_str());
|
||||
|
||||
//ok, now I compose a timestamp based on my basic perception of time/date
|
||||
//yeah, I know about leap years and leap seconds and all, but still we don't care there
|
||||
|
||||
uint32 days = D - 1;
|
||||
for (int i = 1970; i < Y; ++i)
|
||||
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
|
||||
days += 366;
|
||||
else
|
||||
days += 365;
|
||||
|
||||
int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
for (int i = 1; i < M; ++i) {
|
||||
days += mdays[i - 1];
|
||||
if (i == 2)
|
||||
if ((Y % 4 == 0 && Y % 100 != 0) || (Y % 400 == 0))
|
||||
days += 1;
|
||||
}
|
||||
|
||||
uint32 hours = days * 24 + h;
|
||||
uint32 minutes = hours * 60 + m;
|
||||
return minutes * 60 + s;
|
||||
}
|
||||
|
||||
} // End of namespace ISO8601
|
||||
} // End of namespace Cloud
|
||||
36
backends/cloud/iso8601.h
Normal file
36
backends/cloud/iso8601.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ISO8601_H
|
||||
#define BACKENDS_CLOUD_ISO8601_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace ISO8601 {
|
||||
|
||||
/** Returns timestamp corresponding to given ISO 8601 date */
|
||||
uint32 convertToTimestamp(const Common::String &iso8601Date);
|
||||
|
||||
} // End of namespace ISO8601
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
149
backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
Normal file
149
backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
|
||||
#include "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://graph.microsoft.com/v1.0/drive/special/approot"
|
||||
|
||||
OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, const Common::String &path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
OneDriveCreateDirectoryRequest::~OneDriveCreateDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
}
|
||||
|
||||
void OneDriveCreateDirectoryRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_ignoreCallback = false;
|
||||
|
||||
Common::String name = _path, parent = _path;
|
||||
if (name.size() != 0) {
|
||||
uint32 i = name.size() - 1;
|
||||
while (true) {
|
||||
parent.deleteLastChar();
|
||||
if (name[i] == '/' || name[i] == '\\') {
|
||||
name.erase(0, i + 1);
|
||||
break;
|
||||
}
|
||||
if (i == 0)
|
||||
break;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String url = ONEDRIVE_API_SPECIAL_APPROOT;
|
||||
if (parent != "")
|
||||
url += ":/" + Common::percentEncodeString(parent) + ":";
|
||||
url += "/children";
|
||||
Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::JsonResponse &>(this, &OneDriveCreateDirectoryRequest::responseCallback);
|
||||
Networking::ErrorCallback errorResponseCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &OneDriveCreateDirectoryRequest::errorCallback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Content-Type: application/json");
|
||||
|
||||
Common::JSONObject jsonRequestParameters;
|
||||
jsonRequestParameters.setVal("name", new Common::JSONValue(name));
|
||||
jsonRequestParameters.setVal("folder", new Common::JSONValue(Common::JSONObject()));
|
||||
Common::JSONValue value(jsonRequestParameters);
|
||||
request->addPostField(Common::JSON::stringify(&value));
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void OneDriveCreateDirectoryRequest::responseCallback(const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback) {
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "OneDriveCreateDirectoryRequest::responseCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject info = json->asObject();
|
||||
if (info.contains("id")) {
|
||||
finishCreation(true);
|
||||
} else {
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void OneDriveCreateDirectoryRequest::errorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void OneDriveCreateDirectoryRequest::handle() {}
|
||||
|
||||
void OneDriveCreateDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String OneDriveCreateDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void OneDriveCreateDirectoryRequest::finishCreation(bool success) {
|
||||
Request::finishSuccess();
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
58
backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
Normal file
58
backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
class OneDriveStorage;
|
||||
|
||||
class OneDriveCreateDirectoryRequest: public Networking::Request {
|
||||
OneDriveStorage *_storage;
|
||||
Common::String _path;
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void responseCallback(const Networking::JsonResponse &response);
|
||||
void errorCallback(const Networking::ErrorResponse &error);
|
||||
void finishCreation(bool success);
|
||||
public:
|
||||
OneDriveCreateDirectoryRequest(OneDriveStorage *storage, const Common::String &path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
|
||||
~OneDriveCreateDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
194
backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
Normal file
194
backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
|
||||
#include "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/children"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN_ROOT_ITSELF "https://graph.microsoft.com/v1.0/drive/special/approot/children"
|
||||
|
||||
OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
|
||||
Networking::Request(nullptr, ecb),
|
||||
_requestedPath(path), _requestedRecursive(recursive), _storage(storage), _listDirectoryCallback(cb),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
OneDriveListDirectoryRequest::~OneDriveListDirectoryRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _listDirectoryCallback;
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_files.clear();
|
||||
_directoriesQueue.clear();
|
||||
_currentDirectory = "";
|
||||
_ignoreCallback = false;
|
||||
|
||||
_directoriesQueue.push_back(_requestedPath);
|
||||
listNextDirectory();
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::listNextDirectory() {
|
||||
if (_directoriesQueue.empty()) {
|
||||
finishListing(_files);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentDirectory = _directoriesQueue.back();
|
||||
_directoriesQueue.pop_back();
|
||||
|
||||
if (_currentDirectory != "" && _currentDirectory.lastChar() != '/' && _currentDirectory.lastChar() != '\\')
|
||||
_currentDirectory += '/';
|
||||
|
||||
Common::String dir = _currentDirectory;
|
||||
dir.deleteLastChar();
|
||||
Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, Common::percentEncodeString(dir).c_str());
|
||||
if (dir == "") url = Common::String(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN_ROOT_ITSELF);
|
||||
makeRequest(url);
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::makeRequest(const Common::String &url) {
|
||||
Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, const Networking::JsonResponse &>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, const Networking::ErrorResponse &>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: bearer " + _storage->accessToken());
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::listedDirectoryCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
const Common::JSONValue *json = response.value;
|
||||
|
||||
if (_ignoreCallback) {
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this, "OneDriveListDirectoryRequest::listedDirectoryCallback: unknown error");
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json->isObject()) {
|
||||
error.response = "Passed JSON is not an object!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject object = json->asObject();
|
||||
|
||||
//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
|
||||
if (!Networking::HttpJsonRequest::jsonContainsArray(object, "value", "OneDriveListDirectoryRequest")) {
|
||||
error.response = "\"value\" not found or that's not an array!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONArray items = object.getVal("value")->asArray();
|
||||
for (uint32 i = 0; i < items.size(); ++i) {
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;
|
||||
|
||||
Common::JSONObject item = items[i]->asObject();
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(item, "lastModifiedDateTime", "OneDriveListDirectoryRequest")) continue;
|
||||
|
||||
Common::String path = _currentDirectory + item.getVal("name")->asString();
|
||||
bool isDirectory = item.contains("folder");
|
||||
uint32 size = item.getVal("size")->asIntegerNumber();
|
||||
uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());
|
||||
|
||||
StorageFile file(path, size, timestamp, isDirectory);
|
||||
_files.push_back(file);
|
||||
if (_requestedRecursive && file.isDirectory()) {
|
||||
_directoriesQueue.push_back(file.path());
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMore = object.contains("@odata.nextLink");
|
||||
if (hasMore) {
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(object, "@odata.nextLink", "OneDriveListDirectoryRequest")) {
|
||||
error.response = "\"@odata.nextLink\" is not a string!";
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
makeRequest(object.getVal("@odata.nextLink")->asString());
|
||||
} else {
|
||||
listNextDirectory();
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::listedDirectoryErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
if (error.request)
|
||||
_date = error.request->date();
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void OneDriveListDirectoryRequest::handle() {}
|
||||
|
||||
void OneDriveListDirectoryRequest::restart() { start(); }
|
||||
|
||||
Common::String OneDriveListDirectoryRequest::date() const { return _date; }
|
||||
|
||||
void OneDriveListDirectoryRequest::finishListing(const Common::Array<StorageFile> &files) {
|
||||
Request::finishSuccess();
|
||||
if (_listDirectoryCallback)
|
||||
(*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
65
backends/cloud/onedrive/onedrivelistdirectoryrequest.h
Normal file
65
backends/cloud/onedrive/onedrivelistdirectoryrequest.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
class OneDriveStorage;
|
||||
|
||||
class OneDriveListDirectoryRequest: public Networking::Request {
|
||||
Common::String _requestedPath;
|
||||
bool _requestedRecursive;
|
||||
OneDriveStorage *_storage;
|
||||
Storage::ListDirectoryCallback _listDirectoryCallback;
|
||||
Common::Array<StorageFile> _files;
|
||||
Common::Array<Common::String> _directoriesQueue;
|
||||
Common::String _currentDirectory;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _date;
|
||||
|
||||
void start();
|
||||
void listNextDirectory();
|
||||
void listedDirectoryCallback(const Networking::JsonResponse &response);
|
||||
void listedDirectoryErrorCallback(const Networking::ErrorResponse &error);
|
||||
void makeRequest(const Common::String &url);
|
||||
void finishListing(const Common::Array<StorageFile> &files);
|
||||
public:
|
||||
OneDriveListDirectoryRequest(OneDriveStorage *storage, const Common::String &path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
|
||||
~OneDriveListDirectoryRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
Common::String date() const override;
|
||||
};
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
220
backends/cloud/onedrive/onedrivestorage.cpp
Normal file
220
backends/cloud/onedrive/onedrivestorage.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
|
||||
#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
|
||||
#include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
|
||||
#include "backends/cloud/onedrive/onedriveuploadrequest.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://graph.microsoft.com/v1.0/drive/special/approot:/"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://graph.microsoft.com/v1.0/drive/special/approot"
|
||||
|
||||
OneDriveStorage::OneDriveStorage(const Common::String &token, const Common::String &refreshToken, bool enabled):
|
||||
BaseStorage(token, refreshToken, enabled) {}
|
||||
|
||||
OneDriveStorage::OneDriveStorage(const Common::String &code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
OneDriveStorage::OneDriveStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb) {
|
||||
codeFlowComplete(cb, codeFlowJson);
|
||||
}
|
||||
|
||||
OneDriveStorage::~OneDriveStorage() {}
|
||||
|
||||
Common::String OneDriveStorage::cloudProvider() { return "onedrive"; }
|
||||
|
||||
uint32 OneDriveStorage::storageIndex() { return kStorageOneDriveId; }
|
||||
|
||||
bool OneDriveStorage::needsRefreshToken() { return true; }
|
||||
|
||||
bool OneDriveStorage::canReuseRefreshToken() { return false; }
|
||||
|
||||
void OneDriveStorage::saveConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String OneDriveStorage::name() const {
|
||||
return "OneDrive";
|
||||
}
|
||||
|
||||
void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("OneDriveStorage::infoInnerCallback: NULL passed instead of JSON");
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject jsonInfo = json->asObject();
|
||||
|
||||
Common::String uid, displayName, email;
|
||||
uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
|
||||
Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
|
||||
Common::JSONObject user = createdBy.getVal("user")->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
|
||||
uid = user.getVal("id")->asString();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
|
||||
displayName = user.getVal("displayName")->asString();
|
||||
}
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
|
||||
quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
|
||||
}
|
||||
|
||||
Common::String username = email;
|
||||
if (username == "")
|
||||
username = displayName;
|
||||
if (username == "")
|
||||
username = uid;
|
||||
CloudMan.setStorageUsername(kStorageOneDriveId, username);
|
||||
|
||||
if (outerCallback) {
|
||||
(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, const Networking::JsonResponse &response) {
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("OneDriveStorage::fileInfoCallback: NULL passed instead of JSON");
|
||||
if (outerCallback)
|
||||
(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::HttpJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
|
||||
if (outerCallback)
|
||||
(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject result = response.value->asObject();
|
||||
if (!Networking::HttpJsonRequest::jsonContainsString(result, "@microsoft.graph.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
|
||||
warning("OneDriveStorage: downloadUrl not found in passed JSON");
|
||||
debug(9, "%s", response.value->stringify().c_str());
|
||||
if (outerCallback)
|
||||
(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *url = result.getVal("@microsoft.graph.downloadUrl")->asString().c_str();
|
||||
if (outerCallback)
|
||||
(*outerCallback)(Networking::NetworkReadStreamResponse(
|
||||
response.request,
|
||||
Networking::NetworkReadStream::make(url, nullptr, "")
|
||||
));
|
||||
|
||||
delete json;
|
||||
delete outerCallback;
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
debug(9, "OneDrive: `ls \"%s\"`", path.c_str());
|
||||
return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `upload \"%s\"`", path.c_str());
|
||||
return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `download \"%s\"`", path.c_str());
|
||||
Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + Common::percentEncodeString(path);
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, const Networking::NetworkReadStreamResponse &, const Networking::JsonResponse &>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
|
||||
request->addHeader("Authorization: bearer " + _token);
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `mkdir \"%s\"`", path.c_str());
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `info`");
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &OneDriveStorage::infoInnerCallback, callback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
|
||||
request->addHeader("Authorization: bearer " + _token);
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
|
||||
|
||||
OneDriveStorage *OneDriveStorage::loadFromConfig(const Common::String &keyPrefix) {
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("OneDriveStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
|
||||
warning("OneDriveStorage: no refresh_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new OneDriveStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void OneDriveStorage::removeFromConfig(const Common::String &keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
120
backends/cloud/onedrive/onedrivestorage.h
Normal file
120
backends/cloud/onedrive/onedrivestorage.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
|
||||
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
class OneDriveStorage: public Cloud::BaseStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
OneDriveStorage(const Common::String &token, const Common::String &refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, const Networking::JsonResponse &json);
|
||||
|
||||
void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, const Networking::JsonResponse &response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "onedrive"
|
||||
*/
|
||||
Common::String cloudProvider() override;
|
||||
|
||||
/**
|
||||
* @return kStorageOneDriveId
|
||||
*/
|
||||
uint32 storageIndex() override;
|
||||
|
||||
bool needsRefreshToken() override;
|
||||
|
||||
bool canReuseRefreshToken() override;
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
OneDriveStorage(const Common::String &code, Networking::ErrorCallback cb);
|
||||
|
||||
/** This constructor extracts tokens from JSON acquired via OAuth code flow. */
|
||||
OneDriveStorage(const Networking::JsonResponse &codeFlowJson, Networking::ErrorCallback cb);
|
||||
|
||||
~OneDriveStorage() override;
|
||||
|
||||
/**
|
||||
* Storage methods, which are used by CloudManager to save
|
||||
* storage in configuration file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save storage data using ConfMan.
|
||||
* @param keyPrefix all saved keys must start with this prefix.
|
||||
* @note every Storage must write keyPrefix + "type" key
|
||||
* with common value (e.g. "Dropbox").
|
||||
*/
|
||||
void saveConfig(const Common::String &keyPrefix) override;
|
||||
|
||||
/**
|
||||
* Return unique storage name.
|
||||
* @returns some unique storage name (for example, "Dropbox (user@example.com)")
|
||||
*/
|
||||
Common::String name() const override;
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
||||
/** Returns ListDirectoryStatus struct with list of files. */
|
||||
Networking::Request *listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) override;
|
||||
|
||||
/** Returns UploadStatus struct with info about uploaded file. */
|
||||
Networking::Request *upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
Networking::Request *streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
Networking::Request *createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns the StorageInfo struct. */
|
||||
Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) override;
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath() override;
|
||||
|
||||
/**
|
||||
* Load token and user id from configs and return OneDriveStorage for those.
|
||||
* @return pointer to the newly created OneDriveStorage or 0 if some problem occurred.
|
||||
*/
|
||||
static OneDriveStorage *loadFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
/**
|
||||
* Remove all OneDriveStorage-related data from config.
|
||||
*/
|
||||
static void removeFromConfig(const Common::String &keyPrefix);
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
142
backends/cloud/onedrive/onedrivetokenrefresher.cpp
Normal file
142
backends/cloud/onedrive/onedrivetokenrefresher.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/onedrive/onedrivetokenrefresher.h"
|
||||
#include "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/formats/json.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
|
||||
HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
|
||||
|
||||
OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
|
||||
|
||||
void OneDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
|
||||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("OneDriveTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//update headers: first change header with token, then pass those to request
|
||||
for (uint32 i = 0; i < _headersList.size(); ++i) {
|
||||
if (_headersList[i].contains("Authorization")) {
|
||||
_headersList[i] = "Authorization: bearer " + _parentStorage->accessToken();
|
||||
}
|
||||
}
|
||||
|
||||
//successfully received refreshed token, can restart the original request now
|
||||
retry(0);
|
||||
}
|
||||
|
||||
void OneDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
|
||||
if (!json) {
|
||||
//that's probably not an error (200 OK)
|
||||
HttpJsonRequest::finishJson(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonIsObject(json, "OneDriveTokenRefresher")) {
|
||||
Common::JSONObject result = json->asObject();
|
||||
long httpResponseCode = -1;
|
||||
if (result.contains("error") && jsonIsObject(result.getVal("error"), "OneDriveTokenRefresher")) {
|
||||
//new token needed => request token & then retry original request
|
||||
if (_stream) {
|
||||
httpResponseCode = _stream->httpResponseCode();
|
||||
debug(9, "OneDriveTokenRefresher: code = %ld", httpResponseCode);
|
||||
}
|
||||
|
||||
Common::JSONObject error = result.getVal("error")->asObject();
|
||||
bool irrecoverable = true;
|
||||
|
||||
Common::String code, message;
|
||||
if (jsonContainsString(error, "code", "OneDriveTokenRefresher")) {
|
||||
code = error.getVal("code")->asString();
|
||||
debug(9, "OneDriveTokenRefresher: code = %s", code.c_str());
|
||||
}
|
||||
|
||||
if (jsonContainsString(error, "message", "OneDriveTokenRefresher")) {
|
||||
message = error.getVal("message")->asString();
|
||||
debug(9, "OneDriveTokenRefresher: message = %s", message.c_str());
|
||||
}
|
||||
|
||||
//determine whether token refreshing would help in this situation
|
||||
if (code == "itemNotFound") {
|
||||
if (message.contains("application ID"))
|
||||
irrecoverable = false;
|
||||
}
|
||||
|
||||
if (code == "unauthenticated" || code == "InvalidAuthenticationToken")
|
||||
irrecoverable = false;
|
||||
|
||||
if (irrecoverable) {
|
||||
Common::String errorContents = json->stringify(true);
|
||||
finishErrorIrrecoverable(Networking::ErrorResponse(this, false, true, errorContents, httpResponseCode));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<OneDriveTokenRefresher, const Storage::BoolResponse &>(this, &OneDriveTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//notify user of success
|
||||
HttpJsonRequest::finishJson(json);
|
||||
}
|
||||
|
||||
void OneDriveTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
if (error.failed) {
|
||||
Common::JSONValue *value = Common::JSON::parse(error.response);
|
||||
|
||||
//somehow OneDrive returns JSON with '.' in unexpected places, try fixing it
|
||||
if (!value) {
|
||||
Common::String fixedResponse = error.response;
|
||||
for (uint32 i = 0; i < fixedResponse.size(); ++i) {
|
||||
if (fixedResponse[i] == '.')
|
||||
fixedResponse.replace(i, 1, " ");
|
||||
}
|
||||
value = Common::JSON::parse(fixedResponse);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
finishJson(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Request::finishError(error, state); //call closest base class's method
|
||||
}
|
||||
|
||||
void OneDriveTokenRefresher::finishErrorIrrecoverable(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
// don't try to fix JSON as this is irrecoverable version
|
||||
Request::finishError(error, state); // call closest base class's method
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
50
backends/cloud/onedrive/onedrivetokenrefresher.h
Normal file
50
backends/cloud/onedrive/onedrivetokenrefresher.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
class OneDriveStorage;
|
||||
|
||||
class OneDriveTokenRefresher: public Networking::HttpJsonRequest {
|
||||
OneDriveStorage *_parentStorage;
|
||||
|
||||
void tokenRefreshed(const Storage::BoolResponse &response);
|
||||
|
||||
void finishJson(const Common::JSONValue *json) override;
|
||||
void finishError(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED) override;
|
||||
void finishErrorIrrecoverable(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED);
|
||||
|
||||
public:
|
||||
OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
|
||||
~OneDriveTokenRefresher() override;
|
||||
};
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
192
backends/cloud/onedrive/onedriveuploadrequest.cpp
Normal file
192
backends/cloud/onedrive/onedriveuploadrequest.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/onedrive/onedriveuploadrequest.h"
|
||||
#include "backends/cloud/onedrive/onedrivestorage.h"
|
||||
#include "backends/cloud/iso8601.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/networkreadstream.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "onedrivetokenrefresher.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/upload.createSession"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CONTENT "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/content"
|
||||
|
||||
OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
|
||||
_workingRequest(nullptr), _ignoreCallback(false) {
|
||||
start();
|
||||
}
|
||||
|
||||
OneDriveUploadRequest::~OneDriveUploadRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _contentsStream;
|
||||
delete _uploadCallback;
|
||||
}
|
||||
|
||||
void OneDriveUploadRequest::start() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
if (_contentsStream == nullptr) {
|
||||
warning("OneDriveUploadRequest: cannot restart because no stream given");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveUploadRequest::start: can't restart, because no stream given", -1));
|
||||
return;
|
||||
}
|
||||
if (!_contentsStream->seek(0)) {
|
||||
warning("OneDriveUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveUploadRequest::start: can't restart, because seek(0) didn't work", -1));
|
||||
return;
|
||||
}
|
||||
_ignoreCallback = false;
|
||||
|
||||
uploadNextPart();
|
||||
}
|
||||
|
||||
void OneDriveUploadRequest::uploadNextPart() {
|
||||
const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
|
||||
|
||||
if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
|
||||
Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD, Common::percentEncodeString(_savePath).c_str()); //folder must exist
|
||||
Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, const Networking::JsonResponse &>(this, &OneDriveUploadRequest::partUploadedCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, const Networking::ErrorResponse &>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->setBuffer(new byte[1], 0); //use POST
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String url;
|
||||
if (_uploadUrl == "") {
|
||||
url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CONTENT, Common::percentEncodeString(_savePath).c_str());
|
||||
} else {
|
||||
url = _uploadUrl;
|
||||
}
|
||||
|
||||
Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, const Networking::JsonResponse &>(this, &OneDriveUploadRequest::partUploadedCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, const Networking::ErrorResponse &>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
|
||||
Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->usePut();
|
||||
|
||||
uint32 oldPos = _contentsStream->pos();
|
||||
|
||||
byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
|
||||
uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
|
||||
request->setBuffer(buffer, size);
|
||||
|
||||
if (_uploadUrl != "") {
|
||||
request->addHeader(Common::String::format("Content-Range: bytes %u-%lu/%lu", oldPos,
|
||||
static_cast<unsigned long>(_contentsStream->pos() - 1),
|
||||
static_cast<unsigned long>(_contentsStream->size())));
|
||||
} else if (_contentsStream->size() == 0) {
|
||||
warning("\"Sorry, OneDrive can't upload empty files\"");
|
||||
finishUpload(StorageFile(_savePath, 0, 0, false));
|
||||
delete request;
|
||||
return;
|
||||
}
|
||||
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
void OneDriveUploadRequest::partUploadedCallback(const Networking::JsonResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "", -1);
|
||||
const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
||||
const Common::JSONValue *json = response.value;
|
||||
if (json == nullptr) {
|
||||
error.response = "Failed to parse JSON, null passed!";
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (json->isObject()) {
|
||||
Common::JSONObject object = json->asObject();
|
||||
|
||||
if (object.contains("error")) {
|
||||
warning("OneDriveUploadRequest: error: %s", json->stringify(true).c_str());
|
||||
error.response = json->stringify(true);
|
||||
finishError(error);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
|
||||
Networking::HttpJsonRequest::jsonContainsString(object, "lastModifiedDateTime", "OneDriveUploadRequest")) {
|
||||
//finished
|
||||
Common::String path = _savePath;
|
||||
uint32 size = object.getVal("size")->asIntegerNumber();
|
||||
uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
|
||||
finishUpload(StorageFile(path, size, timestamp, false));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_uploadUrl == "") {
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(object, "uploadUrl", "OneDriveUploadRequest"))
|
||||
_uploadUrl = object.getVal("uploadUrl")->asString();
|
||||
}
|
||||
}
|
||||
|
||||
if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
|
||||
warning("OneDriveUploadRequest: no file info to return");
|
||||
finishUpload(StorageFile(_savePath, 0, 0, false));
|
||||
} else {
|
||||
uploadNextPart();
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void OneDriveUploadRequest::partUploadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void OneDriveUploadRequest::handle() {}
|
||||
|
||||
void OneDriveUploadRequest::restart() { start(); }
|
||||
|
||||
void OneDriveUploadRequest::finishUpload(const StorageFile &file) {
|
||||
Request::finishSuccess();
|
||||
if (_uploadCallback)
|
||||
(*_uploadCallback)(Storage::UploadResponse(this, file));
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
60
backends/cloud/onedrive/onedriveuploadrequest.h
Normal file
60
backends/cloud/onedrive/onedriveuploadrequest.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "common/callback.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
class OneDriveStorage;
|
||||
|
||||
class OneDriveUploadRequest: public Networking::Request {
|
||||
OneDriveStorage *_storage;
|
||||
Common::String _savePath;
|
||||
Common::SeekableReadStream *_contentsStream;
|
||||
Storage::UploadCallback _uploadCallback;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
Common::String _uploadUrl;
|
||||
|
||||
void start();
|
||||
void uploadNextPart();
|
||||
void partUploadedCallback(const Networking::JsonResponse &response);
|
||||
void partUploadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void finishUpload(const StorageFile &status);
|
||||
|
||||
public:
|
||||
OneDriveUploadRequest(OneDriveStorage *storage, const Common::String &path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
|
||||
~OneDriveUploadRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
};
|
||||
|
||||
} // End of namespace OneDrive
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
489
backends/cloud/savessyncrequest.cpp
Normal file
489
backends/cloud/savessyncrequest.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/savessyncrequest.h"
|
||||
#include "backends/cloud/cloudmanager.h"
|
||||
#include "backends/cloud/downloadrequest.h"
|
||||
#include "backends/cloud/id/iddownloadrequest.h"
|
||||
#include "backends/networking/http/httpjsonrequest.h"
|
||||
#include "backends/saves/default/default-saves.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/formats/json.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "gui/saveload-dialog.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
|
||||
Request(nullptr, ecb), _storage(storage), _boolCallback(callback),
|
||||
_workingRequest(nullptr), _ignoreCallback(false), _bytesToDownload(0), _bytesDownloaded(0) {
|
||||
start();
|
||||
}
|
||||
|
||||
SavesSyncRequest::~SavesSyncRequest() {
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
delete _boolCallback;
|
||||
}
|
||||
|
||||
void SavesSyncRequest::start() {
|
||||
//cleanup
|
||||
_ignoreCallback = true;
|
||||
if (_workingRequest)
|
||||
_workingRequest->finish();
|
||||
_currentDownloadingFile = StorageFile();
|
||||
_currentUploadingFile = "";
|
||||
_filesToDownload.clear();
|
||||
_filesToUpload.clear();
|
||||
_localFilesTimestamps.clear();
|
||||
_totalFilesToHandle = 0;
|
||||
_ignoreCallback = false;
|
||||
|
||||
//load timestamps
|
||||
_localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
|
||||
|
||||
//list saves directory
|
||||
Common::String dir = _storage->savesDirectoryPath();
|
||||
if (dir.lastChar() == '/')
|
||||
dir.deleteLastChar();
|
||||
_workingRequest = _storage->listDirectory(
|
||||
dir,
|
||||
new Common::Callback<SavesSyncRequest, const Storage::ListDirectoryResponse &>(this, &SavesSyncRequest::directoryListedCallback),
|
||||
new Common::Callback<SavesSyncRequest, const Networking::ErrorResponse &>(this, &SavesSyncRequest::directoryListedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this, "SavesSyncRequest::start: Storage couldn't create Request to list directory"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryListedCallback(const Storage::ListDirectoryResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
if (response.request) _date = response.request->date();
|
||||
if (_date.empty()) {
|
||||
// This is from SaveLoadChooser::createDefaultSaveDescription
|
||||
TimeDate curTime;
|
||||
g_system->getTimeAndDate(curTime);
|
||||
curTime.tm_year += 1900; // fixup year
|
||||
curTime.tm_mon++; // fixup month
|
||||
_date = Common::String::format("%04d-%02d-%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
|
||||
debug(9, "SavesSyncRequest: using local time as fallback: %s", _date.c_str());
|
||||
}
|
||||
|
||||
Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
|
||||
for (auto ×tamp : _localFilesTimestamps) {
|
||||
localFileNotAvailableInCloud[timestamp._key] = true;
|
||||
}
|
||||
|
||||
// Determine which files to download and which files to upload
|
||||
const Common::Array<StorageFile> &remoteFiles = response.value;
|
||||
uint64 totalSize = 0;
|
||||
debug(9, "SavesSyncRequest decisions:");
|
||||
for (uint32 i = 0; i < remoteFiles.size(); ++i) {
|
||||
const StorageFile &file = remoteFiles[i];
|
||||
if (file.isDirectory())
|
||||
continue;
|
||||
totalSize += file.size();
|
||||
if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME || !CloudMan.canSyncFilename(file.name()))
|
||||
continue;
|
||||
|
||||
Common::String name = file.name();
|
||||
if (!_localFilesTimestamps.contains(name)) {
|
||||
_filesToDownload.push_back(file);
|
||||
debug(9, "- downloading file %s, because it is not present on local", name.c_str());
|
||||
} else {
|
||||
localFileNotAvailableInCloud[name] = false;
|
||||
|
||||
if (_localFilesTimestamps[name] == file.timestamp())
|
||||
continue;
|
||||
|
||||
// We actually can have some files not only with timestamp < remote
|
||||
// but also with timestamp > remote (when we have been using ANOTHER CLOUD and then switched back)
|
||||
if (_localFilesTimestamps[name] > file.timestamp() || _localFilesTimestamps[name] == DefaultSaveFileManager::INVALID_TIMESTAMP)
|
||||
_filesToUpload.push_back(file.name());
|
||||
else
|
||||
_filesToDownload.push_back(file);
|
||||
|
||||
if (_localFilesTimestamps[name] == DefaultSaveFileManager::INVALID_TIMESTAMP)
|
||||
debug(9, "- uploading file %s, because it is has invalid timestamp", name.c_str());
|
||||
else if (_localFilesTimestamps[name] > file.timestamp())
|
||||
debug(9, "- uploading file %s, because it is %d seconds newer than remote\n\tlocal = %d; \tremote = %d", name.c_str(), _localFilesTimestamps[name] - file.timestamp(), _localFilesTimestamps[name], file.timestamp());
|
||||
else
|
||||
debug(9, "- downloading file %s, because it is %d seconds older than remote\n\tlocal = %d; \tremote = %d", name.c_str(), file.timestamp() - _localFilesTimestamps[name], _localFilesTimestamps[name], file.timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
|
||||
|
||||
// Upload files which are unavailable in cloud
|
||||
for (auto &localFile : localFileNotAvailableInCloud) {
|
||||
if (localFile._key == DefaultSaveFileManager::TIMESTAMPS_FILENAME || !CloudMan.canSyncFilename(localFile._key))
|
||||
continue;
|
||||
if (localFile._value) {
|
||||
_filesToUpload.push_back(localFile._key);
|
||||
debug(9, "- uploading file %s, because it is not present on remote", localFile._key.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
_bytesToDownload = 0;
|
||||
_bytesDownloaded = 0;
|
||||
debug(9, "\nSavesSyncRequest: ");
|
||||
if (_filesToDownload.size() > 0) {
|
||||
debug(9, "download files:");
|
||||
for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
|
||||
debug(9, " %s", _filesToDownload[i].name().c_str());
|
||||
_bytesToDownload += _filesToDownload[i].size();
|
||||
}
|
||||
debug(9, "%s", "");
|
||||
} else {
|
||||
debug(9, "nothing to download");
|
||||
}
|
||||
debug(9, "SavesSyncRequest: ");
|
||||
if (_filesToUpload.size() > 0) {
|
||||
debug(9, "upload files:");
|
||||
for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
|
||||
debug(9, " %s", _filesToUpload[i].c_str());
|
||||
}
|
||||
} else {
|
||||
debug(9, "nothing to upload");
|
||||
}
|
||||
_totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
|
||||
|
||||
// Start downloading files
|
||||
if (!_filesToDownload.empty()) {
|
||||
downloadNextFile();
|
||||
} else {
|
||||
uploadNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryListedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
if (error.failed) debug(9, "%s", error.response.c_str());
|
||||
|
||||
bool irrecoverable = error.interrupted || error.failed;
|
||||
if (error.failed) {
|
||||
Common::JSONValue *value = Common::JSON::parse(error.response);
|
||||
|
||||
// Somehow OneDrive returns JSON with '.' in unexpected places, try fixing it
|
||||
if (!value) {
|
||||
Common::String fixedResponse = error.response;
|
||||
for (uint32 i = 0; i < fixedResponse.size(); ++i) {
|
||||
if (fixedResponse[i] == '.')
|
||||
fixedResponse.replace(i, 1, " ");
|
||||
}
|
||||
value = Common::JSON::parse(fixedResponse);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (value->isObject()) {
|
||||
Common::JSONObject object = value->asObject();
|
||||
|
||||
// Dropbox-related error:
|
||||
if (object.contains("error_summary") && object.getVal("error_summary")->isString()) {
|
||||
Common::String summary = object.getVal("error_summary")->asString();
|
||||
if (summary.contains("not_found")) {
|
||||
irrecoverable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// OneDrive-related error:
|
||||
if (object.contains("error") && object.getVal("error")->isObject()) {
|
||||
Common::JSONObject errorNode = object.getVal("error")->asObject();
|
||||
if (Networking::HttpJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
|
||||
Common::String code = errorNode.getVal("code")->asString();
|
||||
if (code == "itemNotFound") {
|
||||
irrecoverable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete value;
|
||||
}
|
||||
|
||||
// Google Drive, Box and OneDrive-related ScummVM-based error
|
||||
if (error.response.contains("subdirectory not found")) {
|
||||
irrecoverable = false; //base "/ScummVM/" folder not found
|
||||
} else if (error.response.contains("no such file found in its parent directory")) {
|
||||
irrecoverable = false; //"Saves" folder within "/ScummVM/" not found
|
||||
} else if (error.response.contains("itemNotFound") && error.response.contains("Item does not exist")) {
|
||||
irrecoverable = false; //"saves" folder within application folder is not found
|
||||
}
|
||||
}
|
||||
|
||||
if (irrecoverable) {
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// We're lucky - user just lacks his "/cloud/" folder - let's create one
|
||||
Common::String dir = _storage->savesDirectoryPath();
|
||||
if (dir.lastChar() == '/')
|
||||
dir.deleteLastChar();
|
||||
debug(9, "\nSavesSyncRequest: creating %s", dir.c_str());
|
||||
_workingRequest = _storage->createDirectory(
|
||||
dir,
|
||||
new Common::Callback<SavesSyncRequest, const Storage::BoolResponse &>(this, &SavesSyncRequest::directoryCreatedCallback),
|
||||
new Common::Callback<SavesSyncRequest, const Networking::ErrorResponse &>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest)
|
||||
finishError(Networking::ErrorResponse(this, "SavesSyncRequest::directoryListedErrorCallback: Storage couldn't create Request to create remote directory"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryCreatedCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//stop syncing if failed to create saves directory
|
||||
if (!response.value) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, "SavesSyncRequest::directoryCreatedCallback: failed to create remote directory", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//continue with empty files list
|
||||
Common::Array<StorageFile> files;
|
||||
directoryListedCallback(Storage::ListDirectoryResponse(response.request, files));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryCreatedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//stop syncing if failed to create saves directory
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void SavesSyncRequest::downloadNextFile() {
|
||||
if (_filesToDownload.empty()) {
|
||||
_currentDownloadingFile = StorageFile("", 0, 0, false); //so getFilesToDownload() would return an empty array
|
||||
uploadNextFile();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentDownloadingFile = _filesToDownload.back();
|
||||
_filesToDownload.pop_back();
|
||||
|
||||
debug(9, "\nSavesSyncRequest: downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
|
||||
_workingRequest = _storage->downloadById(
|
||||
_currentDownloadingFile.id(),
|
||||
DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
|
||||
new Common::Callback<SavesSyncRequest, const Storage::BoolResponse &>(this, &SavesSyncRequest::fileDownloadedCallback),
|
||||
new Common::Callback<SavesSyncRequest, const Networking::ErrorResponse &>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest)
|
||||
finishError(Networking::ErrorResponse(this, "SavesSyncRequest::downloadNextFile: Storage couldn't create Request to download a file"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileDownloadedCallback(const Storage::BoolResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//stop syncing if download failed
|
||||
if (!response.value) {
|
||||
//delete the incomplete file
|
||||
g_system->getSavefileManager()->removeSavefile(_currentDownloadingFile.name());
|
||||
finishError(Networking::ErrorResponse(this, false, true, "SavesSyncRequest::fileDownloadedCallback: failed to download a file", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
//update local timestamp for downloaded file
|
||||
_localFilesTimestamps[_currentDownloadingFile.name()] = _currentDownloadingFile.timestamp();
|
||||
DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
|
||||
_bytesDownloaded += _currentDownloadingFile.size();
|
||||
|
||||
//continue downloading files
|
||||
downloadNextFile();
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileDownloadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//stop syncing if download failed
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void SavesSyncRequest::uploadNextFile() {
|
||||
if (_filesToUpload.empty()) {
|
||||
finishSync(true);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentUploadingFile = _filesToUpload.back();
|
||||
_filesToUpload.pop_back();
|
||||
|
||||
debug(9, "\nSavesSyncRequest: uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
|
||||
if (_storage->uploadStreamSupported()) {
|
||||
_workingRequest = _storage->upload(
|
||||
_storage->savesDirectoryPath() + _currentUploadingFile,
|
||||
g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
|
||||
new Common::Callback<SavesSyncRequest, const Storage::UploadResponse &>(this, &SavesSyncRequest::fileUploadedCallback),
|
||||
new Common::Callback<SavesSyncRequest, const Networking::ErrorResponse &>(this, &SavesSyncRequest::fileUploadedErrorCallback)
|
||||
);
|
||||
} else {
|
||||
_workingRequest = _storage->upload(
|
||||
_storage->savesDirectoryPath() + _currentUploadingFile,
|
||||
DefaultSaveFileManager::concatWithSavesPath(_currentUploadingFile),
|
||||
new Common::Callback<SavesSyncRequest, const Storage::UploadResponse &>(this, &SavesSyncRequest::fileUploadedCallback),
|
||||
new Common::Callback<SavesSyncRequest, const Networking::ErrorResponse &>(this, &SavesSyncRequest::fileUploadedErrorCallback)
|
||||
);
|
||||
}
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this, "SavesSyncRequest::uploadNextFile: Storage couldn't create Request to upload a file"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileUploadedCallback(const Storage::UploadResponse &response) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//update local timestamp for the uploaded file
|
||||
_localFilesTimestamps[_currentUploadingFile] = response.value.timestamp();
|
||||
DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
|
||||
|
||||
//continue uploading files
|
||||
uploadNextFile();
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileUploadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_workingRequest = nullptr;
|
||||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
//stop syncing if upload failed
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
void SavesSyncRequest::handle() {}
|
||||
|
||||
void SavesSyncRequest::restart() { start(); }
|
||||
|
||||
double SavesSyncRequest::getDownloadingProgress() const {
|
||||
if (_totalFilesToHandle == 0) {
|
||||
if (_state == Networking::FINISHED)
|
||||
return 1; //nothing to upload and download => Request ends soon
|
||||
return 0; //directory not listed yet
|
||||
}
|
||||
|
||||
if (_totalFilesToHandle == _filesToUpload.size())
|
||||
return 1; //nothing to download => download complete
|
||||
|
||||
if (_bytesToDownload > 0) {
|
||||
// can calculate more precise progress
|
||||
return (double)(getDownloadedBytes()) / (double)(_bytesToDownload);
|
||||
}
|
||||
|
||||
uint32 totalFilesToDownload = _totalFilesToHandle - _filesToUpload.size();
|
||||
uint32 filesLeftToDownload = _filesToDownload.size() + (_currentDownloadingFile.name() != "" ? 1 : 0);
|
||||
if (filesLeftToDownload > totalFilesToDownload)
|
||||
filesLeftToDownload = totalFilesToDownload;
|
||||
return (double)(totalFilesToDownload - filesLeftToDownload) / (double)(totalFilesToDownload);
|
||||
}
|
||||
|
||||
void SavesSyncRequest::getDownloadingInfo(Storage::SyncDownloadingInfo &info) const {
|
||||
info.bytesDownloaded = getDownloadedBytes();
|
||||
info.bytesToDownload = getBytesToDownload();
|
||||
|
||||
uint32 totalFilesToDownload = _totalFilesToHandle - _filesToUpload.size();
|
||||
uint32 filesLeftToDownload = _filesToDownload.size() + (_currentDownloadingFile.name() != "" ? 1 : 0);
|
||||
if (filesLeftToDownload > totalFilesToDownload)
|
||||
filesLeftToDownload = totalFilesToDownload;
|
||||
info.filesDownloaded = totalFilesToDownload - filesLeftToDownload;
|
||||
info.filesToDownload = totalFilesToDownload;
|
||||
|
||||
info.inProgress = (totalFilesToDownload > 0 && filesLeftToDownload > 0);
|
||||
}
|
||||
|
||||
double SavesSyncRequest::getProgress() const {
|
||||
if (_totalFilesToHandle == 0) {
|
||||
if (_state == Networking::FINISHED)
|
||||
return 1; //nothing to upload and download => Request ends soon
|
||||
return 0; //directory not listed yet
|
||||
}
|
||||
|
||||
return (double)(_totalFilesToHandle - _filesToDownload.size() - _filesToUpload.size()) / (double)(_totalFilesToHandle);
|
||||
}
|
||||
|
||||
Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
|
||||
Common::Array<Common::String> result;
|
||||
for (uint32 i = 0; i < _filesToDownload.size(); ++i)
|
||||
result.push_back(_filesToDownload[i].name());
|
||||
if (_currentDownloadingFile.name() != "")
|
||||
result.push_back(_currentDownloadingFile.name());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 SavesSyncRequest::getDownloadedBytes() const {
|
||||
double currentFileProgress = 0;
|
||||
if (const DownloadRequest *downloadRequest = dynamic_cast<DownloadRequest *>(_workingRequest))
|
||||
currentFileProgress = downloadRequest->getProgress();
|
||||
else if (const Id::IdDownloadRequest *idDownloadRequest = dynamic_cast<Id::IdDownloadRequest *>(_workingRequest))
|
||||
currentFileProgress = idDownloadRequest->getProgress();
|
||||
|
||||
return _bytesDownloaded + currentFileProgress * _currentDownloadingFile.size();
|
||||
}
|
||||
|
||||
uint32 SavesSyncRequest::getBytesToDownload() const {
|
||||
return _bytesToDownload;
|
||||
}
|
||||
|
||||
void SavesSyncRequest::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
|
||||
debug(9, "SavesSync::finishError");
|
||||
//if we were downloading a file - remember the name
|
||||
//and make the Request close() it, so we can delete it
|
||||
Common::String name = _currentDownloadingFile.name();
|
||||
if (_workingRequest) {
|
||||
_ignoreCallback = true;
|
||||
_workingRequest->finish();
|
||||
_workingRequest = nullptr;
|
||||
_ignoreCallback = false;
|
||||
}
|
||||
//unlock all the files by making getFilesToDownload() return empty array
|
||||
_currentDownloadingFile = StorageFile();
|
||||
_filesToDownload.clear();
|
||||
//delete the incomplete file
|
||||
if (name != "")
|
||||
g_system->getSavefileManager()->removeSavefile(name);
|
||||
Request::finishError(error);
|
||||
}
|
||||
|
||||
void SavesSyncRequest::finishSync(bool success) {
|
||||
Request::finishSuccess();
|
||||
|
||||
//update last successful sync date
|
||||
debug(9, "SavesSyncRequest: last successful sync date: %s", _date.c_str());
|
||||
CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
|
||||
|
||||
if (_boolCallback)
|
||||
(*_boolCallback)(Storage::BoolResponse(this, success));
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
85
backends/cloud/savessyncrequest.h
Normal file
85
backends/cloud/savessyncrequest.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_SAVESSYNCREQUEST_H
|
||||
#define BACKENDS_CLOUD_SAVESSYNCREQUEST_H
|
||||
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
class SavesSyncRequest: public Networking::Request {
|
||||
Storage *_storage;
|
||||
Storage::BoolCallback _boolCallback;
|
||||
Common::HashMap<Common::String, uint32> _localFilesTimestamps;
|
||||
Common::Array<StorageFile> _filesToDownload;
|
||||
Common::Array<Common::String> _filesToUpload;
|
||||
StorageFile _currentDownloadingFile;
|
||||
Common::String _currentUploadingFile;
|
||||
Request *_workingRequest;
|
||||
bool _ignoreCallback;
|
||||
uint32 _totalFilesToHandle;
|
||||
Common::String _date;
|
||||
uint32 _bytesToDownload, _bytesDownloaded;
|
||||
|
||||
void start();
|
||||
void directoryListedCallback(const Storage::ListDirectoryResponse &response);
|
||||
void directoryListedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void directoryCreatedCallback(const Storage::BoolResponse &response);
|
||||
void directoryCreatedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void fileDownloadedCallback(const Storage::BoolResponse &response);
|
||||
void fileDownloadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void fileUploadedCallback(const Storage::UploadResponse &response);
|
||||
void fileUploadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
void downloadNextFile();
|
||||
void uploadNextFile();
|
||||
void finishError(const Networking::ErrorResponse &error, Networking::RequestState state = Networking::FINISHED) override;
|
||||
void finishSync(bool success);
|
||||
|
||||
uint32 getDownloadedBytes() const;
|
||||
uint32 getBytesToDownload() const;
|
||||
|
||||
public:
|
||||
SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb);
|
||||
~SavesSyncRequest() override;
|
||||
|
||||
void handle() override;
|
||||
void restart() override;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getDownloadingProgress() const;
|
||||
|
||||
/** Fills a struct with numbers about current sync downloading progress. */
|
||||
void getDownloadingInfo(Storage::SyncDownloadingInfo &info) const;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getProgress() const;
|
||||
|
||||
/** Returns an array of saves names which are not downloaded yet. */
|
||||
Common::Array<Common::String> getFilesToDownload();
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
367
backends/cloud/storage.cpp
Normal file
367
backends/cloud/storage.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/storage.h"
|
||||
#include "backends/cloud/downloadrequest.h"
|
||||
#include "backends/cloud/folderdownloadrequest.h"
|
||||
#include "backends/cloud/savessyncrequest.h"
|
||||
#include "backends/networking/http/connectionmanager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/osd_message_queue.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
Storage::Storage():
|
||||
_runningRequestsCount(0), _savesSyncRequest(nullptr), _syncRestartRequestsed(false),
|
||||
_downloadFolderRequest(nullptr), _isEnabled(false) {}
|
||||
|
||||
Storage::~Storage() {}
|
||||
|
||||
bool Storage::isEnabled() const {
|
||||
return _isEnabled;
|
||||
}
|
||||
|
||||
void Storage::enable() {
|
||||
_isEnabled = true;
|
||||
}
|
||||
|
||||
Networking::ErrorCallback Storage::getErrorPrintingCallback() {
|
||||
return new Common::Callback<Storage, const Networking::ErrorResponse &>(this, &Storage::printErrorResponse);
|
||||
}
|
||||
|
||||
void Storage::printErrorResponse(const Networking::ErrorResponse &error) {
|
||||
debug(9, "Storage: error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
}
|
||||
|
||||
Networking::Request *Storage::addRequest(Networking::Request *request) {
|
||||
_runningRequestsMutex.lock();
|
||||
++_runningRequestsCount;
|
||||
if (_runningRequestsCount == 1) {
|
||||
g_system->taskStarted(OSystem::kCloudDownload);
|
||||
debug(9, "Storage is working now");
|
||||
}
|
||||
_runningRequestsMutex.unlock();
|
||||
return ConnMan.addRequest(request, new Common::Callback<Storage, Networking::Request *>(this, &Storage::requestFinishedCallback));
|
||||
}
|
||||
|
||||
void Storage::requestFinishedCallback(Networking::Request *invalidRequestPointer) {
|
||||
bool restartSync = false;
|
||||
|
||||
_runningRequestsMutex.lock();
|
||||
if (invalidRequestPointer == _savesSyncRequest)
|
||||
_savesSyncRequest = nullptr;
|
||||
--_runningRequestsCount;
|
||||
if (_syncRestartRequestsed)
|
||||
restartSync = true;
|
||||
if (_runningRequestsCount == 0) {
|
||||
g_system->taskFinished(OSystem::kCloudDownload);
|
||||
debug(9, "Storage is not working now");
|
||||
}
|
||||
_runningRequestsMutex.unlock();
|
||||
|
||||
if (restartSync)
|
||||
syncSaves(nullptr, nullptr);
|
||||
}
|
||||
|
||||
Networking::Request *Storage::upload(const Common::String &remotePath, const Common::Path &localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback) errorCallback = getErrorPrintingCallback();
|
||||
|
||||
Common::File *f = new Common::File();
|
||||
if (!f->open(localPath)) {
|
||||
warning("Storage: unable to open file to upload from");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
|
||||
delete errorCallback;
|
||||
delete callback;
|
||||
delete f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return upload(remotePath, f, callback, errorCallback);
|
||||
}
|
||||
|
||||
bool Storage::uploadStreamSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Networking::Request *Storage::streamFile(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
//most Storages use paths instead of ids, so this should work
|
||||
return streamFileById(path, callback, errorCallback);
|
||||
}
|
||||
|
||||
Networking::Request *Storage::download(const Common::String &remotePath, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
//most Storages use paths instead of ids, so this should work
|
||||
return downloadById(remotePath, localPath, callback, errorCallback);
|
||||
}
|
||||
|
||||
Networking::Request *Storage::downloadById(const Common::String &remoteId, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
if (!errorCallback) errorCallback = getErrorPrintingCallback();
|
||||
|
||||
Common::DumpFile *f = new Common::DumpFile();
|
||||
if (!f->open(localPath, true)) {
|
||||
warning("Storage: unable to open file to download into");
|
||||
if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1));
|
||||
delete errorCallback;
|
||||
delete callback;
|
||||
delete f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return addRequest(new DownloadRequest(this, callback, errorCallback, remoteId, f));
|
||||
}
|
||||
|
||||
Networking::Request *Storage::downloadFolder(const Common::String &remotePath, const Common::Path &localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
if (!_isEnabled) {
|
||||
warning("Storage::downloadFolder: cannot be run while Storage is disabled");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "Storage is disabled.", -1));
|
||||
return nullptr;
|
||||
}
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
|
||||
}
|
||||
|
||||
SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
_runningRequestsMutex.lock();
|
||||
if (!_isEnabled) {
|
||||
warning("Storage::syncSaves: cannot be run while Storage is disabled");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "Storage is disabled.", -1));
|
||||
_runningRequestsMutex.unlock();
|
||||
return nullptr;
|
||||
}
|
||||
if (_savesSyncRequest) {
|
||||
warning("Storage::syncSaves: there is a sync in progress already");
|
||||
_syncRestartRequestsed = true;
|
||||
_runningRequestsMutex.unlock();
|
||||
return _savesSyncRequest;
|
||||
}
|
||||
if (!callback)
|
||||
callback = new Common::Callback<Storage, const BoolResponse &>(this, &Storage::savesSyncDefaultCallback);
|
||||
if (!errorCallback)
|
||||
errorCallback = new Common::Callback<Storage, const Networking::ErrorResponse &>(this, &Storage::savesSyncDefaultErrorCallback);
|
||||
_savesSyncRequest = new SavesSyncRequest(this, callback, errorCallback);
|
||||
_syncRestartRequestsed = false;
|
||||
_runningRequestsMutex.unlock();
|
||||
return (SavesSyncRequest *)addRequest(_savesSyncRequest); //who knows what that ConnMan could return in the future
|
||||
}
|
||||
|
||||
bool Storage::isWorking() {
|
||||
_runningRequestsMutex.lock();
|
||||
bool working = _runningRequestsCount > 0;
|
||||
_runningRequestsMutex.unlock();
|
||||
return working;
|
||||
}
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
bool Storage::isSyncing() {
|
||||
_runningRequestsMutex.lock();
|
||||
bool syncing = _savesSyncRequest != nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
return syncing;
|
||||
}
|
||||
|
||||
double Storage::getSyncDownloadingProgress() {
|
||||
double result = 1;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_savesSyncRequest)
|
||||
result = _savesSyncRequest->getDownloadingProgress();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Storage::getSyncDownloadingInfo(SyncDownloadingInfo& info) {
|
||||
_runningRequestsMutex.lock();
|
||||
if (_savesSyncRequest) {
|
||||
_savesSyncRequest->getDownloadingInfo(info);
|
||||
}
|
||||
_runningRequestsMutex.unlock();
|
||||
}
|
||||
|
||||
double Storage::getSyncProgress() {
|
||||
double result = 1;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_savesSyncRequest)
|
||||
result = _savesSyncRequest->getProgress();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Array<Common::String> Storage::getSyncingFiles() {
|
||||
Common::Array<Common::String> result;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_savesSyncRequest)
|
||||
result = _savesSyncRequest->getFilesToDownload();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Storage::cancelSync() {
|
||||
_runningRequestsMutex.lock();
|
||||
if (_savesSyncRequest)
|
||||
_savesSyncRequest->finish();
|
||||
_runningRequestsMutex.unlock();
|
||||
}
|
||||
|
||||
void Storage::savesSyncDefaultCallback(const BoolResponse &response) {
|
||||
_runningRequestsMutex.lock();
|
||||
_savesSyncRequest = nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
|
||||
if (!response.value)
|
||||
warning("SavesSyncRequest called success callback with `false` argument");
|
||||
}
|
||||
|
||||
void Storage::savesSyncDefaultErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_runningRequestsMutex.lock();
|
||||
_savesSyncRequest = nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
|
||||
printErrorResponse(error);
|
||||
|
||||
if (error.interrupted)
|
||||
Common::OSDMessageQueue::instance().addMessage(_("Saved games sync was cancelled."));
|
||||
else
|
||||
Common::OSDMessageQueue::instance().addMessage(_("Saved games sync failed.\nCheck your Internet connection."));
|
||||
}
|
||||
|
||||
///// DownloadFolderRequest-related /////
|
||||
|
||||
bool Storage::startDownload(const Common::String &remotePath, const Common::Path &localPath) {
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest) {
|
||||
warning("Storage::startDownload: there is a download in progress already");
|
||||
_runningRequestsMutex.unlock();
|
||||
return false;
|
||||
}
|
||||
_downloadFolderRequest = (FolderDownloadRequest *)downloadFolder(
|
||||
remotePath, localPath,
|
||||
new Common::Callback<Storage, const FileArrayResponse &>(this, &Storage::directoryDownloadedCallback),
|
||||
new Common::Callback<Storage, const Networking::ErrorResponse &>(this, &Storage::directoryDownloadedErrorCallback),
|
||||
true
|
||||
);
|
||||
_runningRequestsMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Storage::cancelDownload() {
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
_downloadFolderRequest->finish();
|
||||
_runningRequestsMutex.unlock();
|
||||
}
|
||||
|
||||
void Storage::setDownloadTarget(GUI::CommandReceiver *target) {
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
_downloadFolderRequest->setTarget(target);
|
||||
_runningRequestsMutex.unlock();
|
||||
}
|
||||
|
||||
bool Storage::isDownloading() {
|
||||
_runningRequestsMutex.lock();
|
||||
bool syncing = _downloadFolderRequest != nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
return syncing;
|
||||
}
|
||||
|
||||
double Storage::getDownloadingProgress() {
|
||||
double result = 1;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getProgress();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64 Storage::getDownloadBytesNumber() {
|
||||
uint64 result = 0;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getDownloadedBytes();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64 Storage::getDownloadTotalBytesNumber() {
|
||||
uint64 result = 0;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getTotalBytesToDownload();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64 Storage::getDownloadSpeed() {
|
||||
uint64 result = 0;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getDownloadSpeed();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::String Storage::getDownloadRemoteDirectory() {
|
||||
Common::String result = "";
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getRemotePath();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Path Storage::getDownloadLocalDirectory() {
|
||||
Common::Path result;
|
||||
_runningRequestsMutex.lock();
|
||||
if (_downloadFolderRequest)
|
||||
result = _downloadFolderRequest->getLocalPath();
|
||||
_runningRequestsMutex.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Storage::directoryDownloadedCallback(const FileArrayResponse &response) {
|
||||
_runningRequestsMutex.lock();
|
||||
_downloadFolderRequest = nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
|
||||
Common::U32String message;
|
||||
if (response.value.size()) {
|
||||
message = Common::U32String::format(_("Download complete.\nFailed to download %u files."), response.value.size());
|
||||
} else {
|
||||
message = _("Download complete.");
|
||||
}
|
||||
Common::OSDMessageQueue::instance().addMessage(message);
|
||||
}
|
||||
|
||||
void Storage::directoryDownloadedErrorCallback(const Networking::ErrorResponse &error) {
|
||||
_runningRequestsMutex.lock();
|
||||
_downloadFolderRequest = nullptr;
|
||||
_runningRequestsMutex.unlock();
|
||||
|
||||
Common::OSDMessageQueue::instance().addMessage(_("Download failed."));
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
257
backends/cloud/storage.h
Normal file
257
backends/cloud/storage.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_STORAGE_H
|
||||
#define BACKENDS_CLOUD_STORAGE_H
|
||||
|
||||
#include "backends/cloud/storagefile.h"
|
||||
#include "backends/cloud/storageinfo.h"
|
||||
#include "backends/networking/http/request.h"
|
||||
#include "backends/networking/http/httprequest.h"
|
||||
#include "common/array.h"
|
||||
#include "common/callback.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/path.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class CommandReceiver;
|
||||
|
||||
}
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
class SavesSyncRequest;
|
||||
class FolderDownloadRequest;
|
||||
|
||||
class Storage {
|
||||
public:
|
||||
typedef Networking::Response<const Common::Array<StorageFile> &> FileArrayResponse;
|
||||
typedef Networking::Response<const StorageInfo &> StorageInfoResponse;
|
||||
typedef Networking::Response<bool> BoolResponse;
|
||||
typedef Networking::Response<const StorageFile &> UploadResponse;
|
||||
typedef Networking::Response<const Common::Array<StorageFile> &> ListDirectoryResponse;
|
||||
|
||||
typedef Common::BaseCallback<const FileArrayResponse &> *FileArrayCallback;
|
||||
typedef Common::BaseCallback<const StorageInfoResponse &> *StorageInfoCallback;
|
||||
typedef Common::BaseCallback<const BoolResponse &> *BoolCallback;
|
||||
typedef Common::BaseCallback<const UploadResponse &> *UploadCallback;
|
||||
typedef Common::BaseCallback<const ListDirectoryResponse &> *ListDirectoryCallback;
|
||||
|
||||
protected:
|
||||
/** Keeps track of running requests. */
|
||||
uint32 _runningRequestsCount;
|
||||
Common::Mutex _runningRequestsMutex;
|
||||
|
||||
/** SavesSyncRequest-related */
|
||||
SavesSyncRequest *_savesSyncRequest;
|
||||
bool _syncRestartRequestsed;
|
||||
|
||||
/** FolderDownloadRequest-related */
|
||||
FolderDownloadRequest *_downloadFolderRequest;
|
||||
|
||||
/** Whether user manually enabled the Storage. */
|
||||
bool _isEnabled;
|
||||
|
||||
/** Returns default error callback (printErrorResponse). */
|
||||
virtual Networking::ErrorCallback getErrorPrintingCallback();
|
||||
|
||||
/** Prints ErrorResponse contents with debug(). */
|
||||
virtual void printErrorResponse(const Networking::ErrorResponse &error);
|
||||
|
||||
/**
|
||||
* Adds request to the ConnMan, but also increases _runningRequestsCount.
|
||||
* This method should be used by Storage implementations instead of
|
||||
* direct ConnMan.addRequest() call.
|
||||
*
|
||||
* @return the same Request pointer, just as a shortcut
|
||||
*/
|
||||
virtual Networking::Request *addRequest(Networking::Request *request);
|
||||
|
||||
/**
|
||||
* Decreases _runningRequestCount. It's called from ConnMan automatically.
|
||||
* Passed pointer is dangling, but one can use the address to determine
|
||||
* some special Requests (which addresses were remembered somewhere).
|
||||
*/
|
||||
virtual void requestFinishedCallback(Networking::Request *invalidRequestPointer);
|
||||
|
||||
public:
|
||||
Storage();
|
||||
virtual ~Storage();
|
||||
|
||||
/**
|
||||
* Storage methods, which are used by CloudManager to save
|
||||
* storage in configuration file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save storage data using ConfMan.
|
||||
* @param keyPrefix all saved keys must start with this prefix.
|
||||
* @note every Storage must write keyPrefix + "type" key
|
||||
* with common value (e.g. "Dropbox").
|
||||
*/
|
||||
virtual void saveConfig(const Common::String &keyPrefix) = 0;
|
||||
|
||||
/**
|
||||
* Return unique storage name.
|
||||
* @returns some unique storage name (for example, "Dropbox (user@example.com)")
|
||||
*/
|
||||
virtual Common::String name() const = 0;
|
||||
|
||||
/**
|
||||
* Return whether Storage has been manually enabled by user.
|
||||
*/
|
||||
bool isEnabled() const;
|
||||
|
||||
/**
|
||||
* Set _isEnabled to true.
|
||||
*/
|
||||
void enable();
|
||||
|
||||
/**
|
||||
* Public Cloud API comes down there.
|
||||
*
|
||||
* All Cloud API methods return Networking::Request *, which
|
||||
* might be used to control request. All methods also accept
|
||||
* a callback, which is called, when request is complete.
|
||||
*/
|
||||
|
||||
/** Returns ListDirectoryResponse with list of files. */
|
||||
virtual Networking::Request *listDirectory(const Common::String &path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0;
|
||||
|
||||
/** Returns StorageFile with info about uploaded file. */
|
||||
virtual Networking::Request *upload(const Common::String &path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
virtual Networking::Request *upload(const Common::String &remotePath, const Common::Path &localPath, UploadCallback callback, Networking::ErrorCallback errorCallback);
|
||||
|
||||
/** Returns whether Storage supports upload(ReadStream). */
|
||||
virtual bool uploadStreamSupported();
|
||||
|
||||
/** Returns pointer to Networking::NetworkReadStream. */
|
||||
virtual Networking::Request *streamFile(const Common::String &path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
|
||||
virtual Networking::Request *streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
virtual Networking::Request *download(const Common::String &remotePath, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
|
||||
virtual Networking::Request *downloadById(const Common::String &remoteId, const Common::Path &localPath, BoolCallback callback, Networking::ErrorCallback errorCallback);
|
||||
|
||||
/** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */
|
||||
virtual Networking::Request *downloadFolder(const Common::String &remotePath, const Common::Path &localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
virtual SavesSyncRequest *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback);
|
||||
|
||||
/** Calls the callback when finished. */
|
||||
virtual Networking::Request *createDirectory(const Common::String &path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
|
||||
/**
|
||||
* Returns the StorageInfo struct via <callback>.
|
||||
* Calls the <errorCallback> if failed to get information.
|
||||
*
|
||||
* @note on success Storage should also call
|
||||
* CloudMan.setStorageUsername().
|
||||
*/
|
||||
virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0;
|
||||
|
||||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
virtual Common::String savesDirectoryPath() = 0;
|
||||
|
||||
/** Returns whether there are any requests running. */
|
||||
virtual bool isWorking();
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
/** Returns whether there is a SavesSyncRequest running. */
|
||||
virtual bool isSyncing();
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current sync downloading progress (1 = complete). */
|
||||
virtual double getSyncDownloadingProgress();
|
||||
|
||||
struct SyncDownloadingInfo {
|
||||
uint64 bytesDownloaded = 0, bytesToDownload = 0;
|
||||
uint64 filesDownloaded = 0, filesToDownload = 0;
|
||||
bool inProgress = false;
|
||||
};
|
||||
|
||||
/** Fills a struct with numbers about current sync downloading progress. */
|
||||
virtual void getSyncDownloadingInfo(SyncDownloadingInfo &info);
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current sync progress (1 = complete). */
|
||||
virtual double getSyncProgress();
|
||||
|
||||
/** Returns an array of saves names which are not yet synced (thus cannot be used). */
|
||||
virtual Common::Array<Common::String> getSyncingFiles();
|
||||
|
||||
/** Cancels running sync. */
|
||||
virtual void cancelSync();
|
||||
|
||||
protected:
|
||||
/** Finishes the sync. Shows an OSD message. */
|
||||
virtual void savesSyncDefaultCallback(const BoolResponse &response);
|
||||
|
||||
/** Finishes the sync. Shows an OSD message. */
|
||||
virtual void savesSyncDefaultErrorCallback(const Networking::ErrorResponse &error);
|
||||
|
||||
public:
|
||||
///// DownloadFolderRequest-related /////
|
||||
|
||||
/** Starts a folder download. */
|
||||
virtual bool startDownload(const Common::String &remotePath, const Common::Path &localPath);
|
||||
|
||||
/** Cancels running download. */
|
||||
virtual void cancelDownload();
|
||||
|
||||
/** Sets FolderDownloadRequest's target to given CommandReceiver. */
|
||||
virtual void setDownloadTarget(GUI::CommandReceiver *target);
|
||||
|
||||
/** Returns whether there is a FolderDownloadRequest running. */
|
||||
virtual bool isDownloading();
|
||||
|
||||
/** Returns a number in [0, 1] range which represents current download progress (1 = complete). */
|
||||
virtual double getDownloadingProgress();
|
||||
|
||||
/** Returns a number of bytes that is downloaded in current download progress. */
|
||||
virtual uint64 getDownloadBytesNumber();
|
||||
|
||||
/** Returns a total number of bytes to be downloaded in current download progress. */
|
||||
virtual uint64 getDownloadTotalBytesNumber();
|
||||
|
||||
/** Returns download speed of current download progress. */
|
||||
virtual uint64 getDownloadSpeed();
|
||||
|
||||
/** Returns remote directory path. */
|
||||
virtual Common::String getDownloadRemoteDirectory();
|
||||
|
||||
/** Returns local directory path. */
|
||||
virtual Common::Path getDownloadLocalDirectory();
|
||||
|
||||
protected:
|
||||
/** Finishes the download. Shows an OSD message. */
|
||||
virtual void directoryDownloadedCallback(const FileArrayResponse &response);
|
||||
|
||||
/** Finishes the download. Shows an OSD message. */
|
||||
virtual void directoryDownloadedErrorCallback(const Networking::ErrorResponse &error);
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
67
backends/cloud/storagefile.cpp
Normal file
67
backends/cloud/storagefile.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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 "backends/cloud/storagefile.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
StorageFile::StorageFile() {
|
||||
_id = "";
|
||||
_path = "";
|
||||
_name = "";
|
||||
_size = 0;
|
||||
_timestamp = 0;
|
||||
_isDirectory = false;
|
||||
}
|
||||
|
||||
StorageFile::StorageFile(const Common::String &pth, uint32 sz, uint32 ts, bool dir) {
|
||||
_id = pth;
|
||||
_path = pth;
|
||||
|
||||
_name = pth;
|
||||
if (_name.size() != 0) {
|
||||
uint32 i = _name.size() - 1;
|
||||
while (true) {
|
||||
if (_name[i] == '/' || _name[i] == '\\') {
|
||||
_name.erase(0, i + 1);
|
||||
break;
|
||||
}
|
||||
if (i == 0)
|
||||
break;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
_size = sz;
|
||||
_timestamp = ts;
|
||||
_isDirectory = dir;
|
||||
}
|
||||
|
||||
StorageFile::StorageFile(const Common::String &fileId, const Common::String &filePath, const Common::String &fileName, uint32 sz, uint32 ts, bool dir) {
|
||||
_id = fileId;
|
||||
_path = filePath;
|
||||
_name = fileName;
|
||||
_size = sz;
|
||||
_timestamp = ts;
|
||||
_isDirectory = dir;
|
||||
}
|
||||
|
||||
} // End of namespace Cloud
|
||||
64
backends/cloud/storagefile.h
Normal file
64
backends/cloud/storagefile.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_STORAGEFILE_H
|
||||
#define BACKENDS_CLOUD_STORAGEFILE_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
/**
|
||||
* StorageFile represents a file storaged on remote cloud storage.
|
||||
* It contains basic information about a file, and might be used
|
||||
* when listing directories or syncing files.
|
||||
*
|
||||
* Some storages (Google Drive, for example) don't have an actual
|
||||
* path notation to address files. Instead, they are using ids.
|
||||
* As resolving id by path is not a fast operation, it's required
|
||||
* to use ids if they are known, but user-friendly paths are
|
||||
* necessary too, because these are used by Requests.
|
||||
*
|
||||
* If storage supports path notation, id would actually contain path.
|
||||
*/
|
||||
class StorageFile {
|
||||
Common::String _id, _path, _name;
|
||||
uint32 _size, _timestamp;
|
||||
bool _isDirectory;
|
||||
|
||||
public:
|
||||
StorageFile(); //invalid empty file
|
||||
StorageFile(const Common::String &pth, uint32 sz, uint32 ts, bool dir);
|
||||
StorageFile(const Common::String &fileId, const Common::String &filePath, const Common::String &fileName, uint32 sz, uint32 ts, bool dir);
|
||||
|
||||
Common::String id() const { return _id; }
|
||||
Common::String path() const { return _path; }
|
||||
Common::String name() const { return _name; }
|
||||
uint32 size() const { return _size; }
|
||||
uint32 timestamp() const { return _timestamp; }
|
||||
bool isDirectory() const { return _isDirectory; }
|
||||
|
||||
void setPath(const Common::String &path_) { _path = path_; }
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
52
backends/cloud/storageinfo.h
Normal file
52
backends/cloud/storageinfo.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_CLOUD_STORAGEINFO_H
|
||||
#define BACKENDS_CLOUD_STORAGEINFO_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
/**
|
||||
* StorageInfo contains information about remote cloud storage.
|
||||
* It's disk quota usage, owner name, and such.
|
||||
*/
|
||||
|
||||
class StorageInfo {
|
||||
Common::String _uid, _name, _email;
|
||||
uint64 _usedBytes, _allocatedBytes;
|
||||
|
||||
public:
|
||||
StorageInfo(const Common::String &uid_, const Common::String &name_, const Common::String &email_, uint64 used_, uint64 allocated):
|
||||
_uid(uid_), _name(name_), _email(email_), _usedBytes(used_), _allocatedBytes(allocated) {}
|
||||
|
||||
Common::String uid() const { return _uid; }
|
||||
Common::String name() const { return _name; }
|
||||
Common::String email() const { return _email; }
|
||||
uint64 used() const { return _usedBytes; }
|
||||
uint64 available() const { return _allocatedBytes; }
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Cloud
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user