aboutsummaryrefslogtreecommitdiff
path: root/backends/cloud/onedrive
diff options
context:
space:
mode:
Diffstat (limited to 'backends/cloud/onedrive')
-rw-r--r--backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp150
-rw-r--r--backends/cloud/onedrive/onedrivecreatedirectoryrequest.h59
-rw-r--r--backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp193
-rw-r--r--backends/cloud/onedrive/onedrivelistdirectoryrequest.h66
-rw-r--r--backends/cloud/onedrive/onedrivestorage.cpp327
-rw-r--r--backends/cloud/onedrive/onedrivestorage.h113
-rw-r--r--backends/cloud/onedrive/onedrivetokenrefresher.cpp131
-rw-r--r--backends/cloud/onedrive/onedrivetokenrefresher.h52
-rw-r--r--backends/cloud/onedrive/onedriveuploadrequest.cpp191
-rw-r--r--backends/cloud/onedrive/onedriveuploadrequest.h61
10 files changed, 1343 insertions, 0 deletions
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
new file mode 100644
index 0000000000..da67266d12
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -0,0 +1,150 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
+
+OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, 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 += ":/" + ConnMan.urlEncode(parent) + ":";
+ url += "/children";
+ Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::JsonResponse>(this, &OneDriveCreateDirectoryRequest::responseCallback);
+ Networking::ErrorCallback errorCallback = new Common::Callback<OneDriveCreateDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveCreateDirectoryRequest::errorCallback);
+ Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorCallback, 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(Networking::JsonResponse response) {
+ Common::JSONValue *json = response.value;
+ _workingRequest = nullptr;
+ if (_ignoreCallback) {
+ delete json;
+ return;
+ }
+ if (response.request)
+ _date = response.request->date();
+
+ Networking::ErrorResponse error(this);
+ Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)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(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
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
new file mode 100644
index 0000000000..a04009c27d
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/request.h"
+#include "backends/networking/curl/curljsonrequest.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(Networking::JsonResponse response);
+ void errorCallback(Networking::ErrorResponse error);
+ void finishCreation(bool success);
+public:
+ OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb);
+ virtual ~OneDriveCreateDirectoryRequest();
+
+ virtual void handle();
+ virtual void restart();
+ virtual Common::String date() const;
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
new file mode 100644
index 0000000000..953845d343
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/children"
+
+OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, 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, ConnMan.urlEncode(dir).c_str());
+ makeRequest(url);
+}
+
+void OneDriveListDirectoryRequest::makeRequest(Common::String url) {
+ Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
+ Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
+ Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+ request->addHeader("Authorization: Bearer " + _storage->accessToken());
+ _workingRequest = ConnMan.addRequest(request);
+}
+
+void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse response) {
+ _workingRequest = nullptr;
+ Common::JSONValue *json = response.value;
+
+ if (_ignoreCallback) {
+ delete json;
+ return;
+ }
+
+ if (response.request)
+ _date = response.request->date();
+
+ Networking::ErrorResponse error(this);
+ Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)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::CurlJsonRequest::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::CurlJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;
+
+ Common::JSONObject item = items[i]->asObject();
+
+ if (!Networking::CurlJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
+ if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
+ if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
+ if (!Networking::CurlJsonRequest::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::CurlJsonRequest::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(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(Common::Array<StorageFile> &files) {
+ Request::finishSuccess();
+ if (_listDirectoryCallback)
+ (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files));
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
new file mode 100644
index 0000000000..6fd0ad9fa0
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -0,0 +1,66 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/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(Networking::JsonResponse response);
+ void listedDirectoryErrorCallback(Networking::ErrorResponse error);
+ void makeRequest(Common::String url);
+ void finishListing(Common::Array<StorageFile> &files);
+public:
+ OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false);
+ virtual ~OneDriveListDirectoryRequest();
+
+ virtual void handle();
+ virtual void restart();
+ virtual Common::String date() const;
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
new file mode 100644
index 0000000000..8799f3d69f
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -0,0 +1,327 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#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/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+#ifdef ENABLE_RELEASE
+#include "dists/clouds/cloud_keys.h"
+#endif
+
+namespace Cloud {
+namespace OneDrive {
+
+#define ONEDRIVE_OAUTH2_TOKEN "https://login.live.com/oauth20_token.srf"
+#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://api.onedrive.com/v1.0/drive/special/approot:/"
+#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
+
+char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
+char *OneDriveStorage::SECRET = nullptr;
+
+void OneDriveStorage::loadKeyAndSecret() {
+#ifdef ENABLE_RELEASE
+ KEY = RELEASE_ONEDRIVE_KEY;
+ SECRET = RELEASE_ONEDRIVE_SECRET;
+#else
+ Common::String k = ConfMan.get("ONEDRIVE_KEY", ConfMan.kCloudDomain);
+ KEY = new char[k.size() + 1];
+ memcpy(KEY, k.c_str(), k.size());
+ KEY[k.size()] = 0;
+
+ k = ConfMan.get("ONEDRIVE_SECRET", ConfMan.kCloudDomain);
+ SECRET = new char[k.size() + 1];
+ memcpy(SECRET, k.c_str(), k.size());
+ SECRET[k.size()] = 0;
+#endif
+}
+
+OneDriveStorage::OneDriveStorage(Common::String accessToken, Common::String userId, Common::String refreshToken):
+ _token(accessToken), _uid(userId), _refreshToken(refreshToken) {}
+
+OneDriveStorage::OneDriveStorage(Common::String code) {
+ getAccessToken(
+ new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
+ new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
+ code
+ );
+}
+
+OneDriveStorage::~OneDriveStorage() {}
+
+void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
+ if (!KEY || !SECRET)
+ loadKeyAndSecret();
+ bool codeFlow = (code != "");
+
+ if (!codeFlow && _refreshToken == "") {
+ warning("OneDriveStorage: no refresh token available to get new access token.");
+ if (callback)
+ (*callback)(BoolResponse(nullptr, false));
+ return;
+ }
+
+ Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
+ if (errorCallback == nullptr)
+ errorCallback = getErrorPrintingCallback();
+ Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, ONEDRIVE_OAUTH2_TOKEN);
+ if (codeFlow) {
+ request->addPostField("code=" + code);
+ request->addPostField("grant_type=authorization_code");
+ } else {
+ request->addPostField("refresh_token=" + _refreshToken);
+ request->addPostField("grant_type=refresh_token");
+ }
+ request->addPostField("client_id=" + Common::String(KEY));
+ request->addPostField("client_secret=" + Common::String(SECRET));
+ if (Cloud::CloudManager::couldUseLocalServer()) {
+ request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
+ } else {
+ request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
+ }
+ addRequest(request);
+}
+
+void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
+ Common::JSONValue *json = response.value;
+ if (!json) {
+ warning("OneDriveStorage: got NULL instead of JSON");
+ if (callback)
+ (*callback)(BoolResponse(nullptr, false));
+ delete callback;
+ return;
+ }
+
+ if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage")) {
+ if (callback)
+ (*callback)(BoolResponse(nullptr, false));
+ delete json;
+ delete callback;
+ return;
+ }
+
+ Common::JSONObject result = json->asObject();
+ if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "OneDriveStorage") ||
+ !Networking::CurlJsonRequest::jsonContainsString(result, "user_id", "OneDriveStorage") ||
+ !Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "OneDriveStorage")) {
+ warning("OneDriveStorage: bad response, no token or user_id passed");
+ debug(9, "%s", json->stringify().c_str());
+ if (callback)
+ (*callback)(BoolResponse(nullptr, false));
+ } else {
+ _token = result.getVal("access_token")->asString();
+ _uid = result.getVal("user_id")->asString();
+ _refreshToken = result.getVal("refresh_token")->asString();
+ CloudMan.save(); //ask CloudManager to save our new refreshToken
+ if (callback)
+ (*callback)(BoolResponse(nullptr, true));
+ }
+ delete json;
+ delete callback;
+}
+
+void OneDriveStorage::codeFlowComplete(BoolResponse response) {
+ if (!response.value) {
+ warning("OneDriveStorage: failed to get access token through code flow");
+ CloudMan.removeStorage(this);
+ return;
+ }
+
+ ConfMan.removeKey("onedrive_code", ConfMan.kCloudDomain);
+ CloudMan.replaceStorage(this, kStorageOneDriveId);
+ ConfMan.flushToDisk();
+}
+
+void OneDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
+ debug(9, "OneDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
+ debug(9, "%s", error.response.c_str());
+ CloudMan.removeStorage(this);
+}
+
+void OneDriveStorage::saveConfig(Common::String keyPrefix) {
+ ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
+ ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
+ ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
+}
+
+Common::String OneDriveStorage::name() const {
+ return "OneDrive";
+}
+
+void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
+ Common::JSONValue *json = response.value;
+ if (!json) {
+ warning("OneDriveStorage::infoInnerCallback: NULL passed instead of JSON");
+ delete outerCallback;
+ return;
+ }
+
+ if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
+ delete json;
+ delete outerCallback;
+ return;
+ }
+
+ Common::JSONObject info = json->asObject();
+
+ Common::String uid, name, email;
+ uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one
+
+ if (Networking::CurlJsonRequest::jsonContainsObject(info, "createdBy", "OneDriveStorage::infoInnerCallback")) {
+ Common::JSONObject createdBy = info.getVal("createdBy")->asObject();
+ if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
+ Common::JSONObject user = createdBy.getVal("user")->asObject();
+ if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
+ uid = user.getVal("id")->asString();
+ if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
+ name = user.getVal("displayName")->asString();
+ }
+ }
+
+ if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "size", "OneDriveStorage::infoInnerCallback")) {
+ quotaUsed = info.getVal("size")->asIntegerNumber();
+ }
+
+ Common::String username = email;
+ if (username == "")
+ username = name;
+ if (username == "")
+ username = uid;
+ CloudMan.setStorageUsername(kStorageOneDriveId, username);
+
+ if (outerCallback) {
+ (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
+ delete outerCallback;
+ }
+
+ delete json;
+}
+
+void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
+ 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::CurlJsonRequest::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::CurlJsonRequest::jsonContainsString(result, "@content.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("@content.downloadUrl")->asString().c_str();
+ if (outerCallback)
+ (*outerCallback)(Networking::NetworkReadStreamResponse(
+ response.request,
+ new Networking::NetworkReadStream(url, nullptr, "")
+ ));
+
+ delete json;
+ delete outerCallback;
+}
+
+Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
+ return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
+}
+
+Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
+ return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
+}
+
+Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
+ Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
+ Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
+ Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+ request->addHeader("Authorization: Bearer " + _token);
+ return addRequest(request);
+}
+
+Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
+ if (!errorCallback)
+ errorCallback = getErrorPrintingCallback();
+ return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
+}
+
+Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
+ Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
+ Networking::CurlJsonRequest *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(Common::String keyPrefix) {
+ loadKeyAndSecret();
+
+ if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
+ warning("OneDriveStorage: no access_token found");
+ return nullptr;
+ }
+
+ if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
+ warning("OneDriveStorage: no user_id 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 userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
+ Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
+ return new OneDriveStorage(accessToken, userId, refreshToken);
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
new file mode 100644
index 0000000000..5d24eb2c2e
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -0,0 +1,113 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage: public Cloud::Storage {
+ static char *KEY, *SECRET;
+
+ static void loadKeyAndSecret();
+
+ Common::String _token, _uid, _refreshToken;
+
+ /** This private constructor is called from loadFromConfig(). */
+ OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
+
+ void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
+ void codeFlowComplete(BoolResponse response);
+ void codeFlowFailed(Networking::ErrorResponse error);
+
+ /** Constructs StorageInfo based on JSON response from cloud. */
+ void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
+
+ void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
+public:
+ /** This constructor uses OAuth code flow to get tokens. */
+ OneDriveStorage(Common::String code);
+ virtual ~OneDriveStorage();
+
+ /**
+ * 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(Common::String keyPrefix);
+
+ /**
+ * Return unique storage name.
+ * @returns some unique storage name (for example, "Dropbox (user@example.com)")
+ */
+ virtual Common::String name() const;
+
+ /** Public Cloud API comes down there. */
+
+ /** Returns ListDirectoryStatus struct with list of files. */
+ virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
+
+ /** Returns UploadStatus struct with info about uploaded file. */
+ virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback);
+
+ /** Returns pointer to Networking::NetworkReadStream. */
+ virtual Networking::Request *streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback);
+
+ /** Calls the callback when finished. */
+ virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback);
+
+ /** Returns the StorageInfo struct. */
+ virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback);
+
+ /** Returns storage's saves directory path with the trailing slash. */
+ virtual Common::String savesDirectoryPath();
+
+ /**
+ * Load token and user id from configs and return OneDriveStorage for those.
+ * @return pointer to the newly created OneDriveStorage or 0 if some problem occured.
+ */
+ static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
+
+ /**
+ * Gets new access_token. If <code> passed is "", refresh_token is used.
+ * Use "" in order to refresh token and pass a callback, so you could
+ * continue your work when new token is available.
+ */
+ void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
+
+ Common::String accessToken() const { return _token; }
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
new file mode 100644
index 0000000000..7104dc6bb6
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/cloud/onedrive/onedrivetokenrefresher.h"
+#include "backends/cloud/onedrive/onedrivestorage.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Cloud {
+namespace OneDrive {
+
+OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
+ CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+
+OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
+
+void OneDriveTokenRefresher::tokenRefreshed(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, "", -1));
+ return;
+ }
+
+ //update headers: first change header with token, then pass those to request
+ for (uint32 i = 0; i < _headers.size(); ++i) {
+ if (_headers[i].contains("Authorization")) {
+ _headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
+ }
+ }
+ setHeaders(_headers);
+
+ //successfully received refreshed token, can restart the original request now
+ retry(0);
+}
+
+void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
+ if (!json) {
+ //that's probably not an error (200 OK)
+ CurlJsonRequest::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")
+ irrecoverable = false;
+
+ if (irrecoverable) {
+ finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
+ delete json;
+ return;
+ }
+
+ pause();
+ delete json;
+ _parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
+ return;
+ }
+ }
+
+ //notify user of success
+ CurlJsonRequest::finishJson(json);
+}
+
+void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
+ _headers = headers;
+ curl_slist_free_all(_headersList);
+ _headersList = 0;
+ for (uint32 i = 0; i < headers.size(); ++i)
+ CurlJsonRequest::addHeader(headers[i]);
+}
+
+void OneDriveTokenRefresher::addHeader(Common::String header) {
+ _headers.push_back(header);
+ CurlJsonRequest::addHeader(header);
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
new file mode 100644
index 0000000000..d190bc4666
--- /dev/null
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+class OneDriveStorage;
+
+class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
+ OneDriveStorage *_parentStorage;
+ Common::Array<Common::String> _headers;
+
+ void tokenRefreshed(Storage::BoolResponse response);
+
+ virtual void finishJson(Common::JSONValue *json);
+public:
+ OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
+ virtual ~OneDriveTokenRefresher();
+
+ virtual void setHeaders(Common::Array<Common::String> &headers);
+ virtual void addHeader(Common::String header);
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
new file mode 100644
index 0000000000..ebf387fca2
--- /dev/null
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -0,0 +1,191 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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/curl/connectionmanager.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/json.h"
+#include "onedrivetokenrefresher.h"
+
+namespace Cloud {
+namespace OneDrive {
+
+#define ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/upload.createSession"
+#define ONEDRIVE_API_SPECIAL_APPROOT_CONTENT "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/content"
+
+OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, 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, "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, "", -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, ConnMan.urlEncode(_savePath).c_str()); //folder must exist
+ Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
+ Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
+ Networking::CurlJsonRequest *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, ConnMan.urlEncode(_savePath).c_str());
+ } else {
+ url = _uploadUrl;
+ }
+
+ Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, Networking::JsonResponse>(this, &OneDriveUploadRequest::partUploadedCallback);
+ Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, Networking::ErrorResponse>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
+ Networking::CurlJsonRequest *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-%u/%u", oldPos, _contentsStream->pos() - 1, _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(Networking::JsonResponse response) {
+ _workingRequest = nullptr;
+ if (_ignoreCallback)
+ return;
+
+ Networking::ErrorResponse error(this, false, true, "", -1);
+ Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
+ if (rq && rq->getNetworkReadStream())
+ error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
+
+ 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::CurlJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
+ Networking::CurlJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
+ Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
+ Networking::CurlJsonRequest::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::CurlJsonRequest::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(Networking::ErrorResponse error) {
+ _workingRequest = nullptr;
+ if (_ignoreCallback)
+ return;
+ finishError(error);
+}
+
+void OneDriveUploadRequest::handle() {}
+
+void OneDriveUploadRequest::restart() { start(); }
+
+void OneDriveUploadRequest::finishUpload(StorageFile file) {
+ Request::finishSuccess();
+ if (_uploadCallback)
+ (*_uploadCallback)(Storage::UploadResponse(this, file));
+}
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.h b/backends/cloud/onedrive/onedriveuploadrequest.h
new file mode 100644
index 0000000000..ba3c013f71
--- /dev/null
+++ b/backends/cloud/onedrive/onedriveuploadrequest.h
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
+#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
+
+#include "backends/cloud/storage.h"
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/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(Networking::JsonResponse response);
+ void partUploadedErrorCallback(Networking::ErrorResponse error);
+ void finishUpload(StorageFile status);
+
+public:
+ OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb);
+ virtual ~OneDriveUploadRequest();
+
+ virtual void handle();
+ virtual void restart();
+};
+
+} // End of namespace OneDrive
+} // End of namespace Cloud
+
+#endif