From d96cdacb38afd9394ab442e7b5a74cc87a495092 Mon Sep 17 00:00:00 2001 From: Alexander Tkachev Date: Wed, 13 Jul 2016 20:05:56 +0600 Subject: [PATCH] CLOUD: Add BoxUploadRequest --- backends/cloud/box/boxstorage.cpp | 10 +- backends/cloud/box/boxstorage.h | 1 + backends/cloud/box/boxuploadrequest.cpp | 213 ++++++++++++++++++++++++ backends/cloud/box/boxuploadrequest.h | 63 +++++++ backends/module.mk | 1 + 5 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 backends/cloud/box/boxuploadrequest.cpp create mode 100644 backends/cloud/box/boxuploadrequest.h diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp index e14157ccf2a..628d18b89ad 100644 --- a/backends/cloud/box/boxstorage.cpp +++ b/backends/cloud/box/boxstorage.cpp @@ -24,6 +24,7 @@ #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/curl/connectionmanager.h" #include "backends/networking/curl/curljsonrequest.h" @@ -244,9 +245,14 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String pare return addRequest(request); } +Networking::Request *BoxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) { + if (!errorCallback) errorCallback = getErrorPrintingCallback(); + return addRequest(new BoxUploadRequest(this, remotePath, localPath, callback, errorCallback)); +} + Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { - //return addRequest(new BoxUploadRequest(this, path, contents, callback, errorCallback)); - return nullptr; //TODO + warning("BoxStorage::upload(ReadStream) not implemented"); + return nullptr; } Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) { diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h index 3b02f88212b..93afe4fabbb 100644 --- a/backends/cloud/box/boxstorage.h +++ b/backends/cloud/box/boxstorage.h @@ -80,6 +80,7 @@ public: virtual Networking::Request *createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback); /** Returns UploadStatus struct with info about uploaded file. */ + virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback); virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback); /** Returns pointer to Networking::NetworkReadStream. */ diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp new file mode 100644 index 00000000000..c94494efac8 --- /dev/null +++ b/backends/cloud/box/boxuploadrequest.cpp @@ -0,0 +1,213 @@ +/* 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/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/curl/connectionmanager.h" +#include "backends/networking/curl/curljsonrequest.h" +#include "backends/networking/curl/networkreadstream.h" +#include "common/json.h" +#include "common/debug.h" + +namespace Cloud { +namespace Box { + +BoxUploadRequest::BoxUploadRequest(BoxStorage *storage, Common::String path, Common::String 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(this, &BoxUploadRequest::idResolvedCallback); + Networking::ErrorCallback innerErrorCallback = new Common::Callback(this, &BoxUploadRequest::idResolveFailedCallback); + _workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback); +} + +void BoxUploadRequest::idResolvedCallback(Storage::UploadResponse response) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + _resolvedId = response.value.id(); + upload(); +} + +void BoxUploadRequest::idResolveFailedCallback(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 = "https://upload.box.com/api/2.0/files"; + if (_resolvedId != "") url += "/" + _resolvedId; + url += "/content"; + Networking::JsonCallback callback = new Common::Callback(this, &BoxUploadRequest::uploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback(this, &BoxUploadRequest::notUploadedCallback); + Networking::CurlJsonRequest *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(Networking::JsonResponse response) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + + Networking::ErrorResponse error(this, false, true, "", -1); + Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)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("looks like an error"); + + Common::JSONValue *json = response.value; + if (json) { + if (json->isObject()) { + Common::JSONObject object = json->asObject(); + if (object.contains("entries") && object.getVal("entries")->isArray()) { + Common::JSONArray entries = object.getVal("entries")->asArray(); + if (entries.size() > 0) { + Common::JSONObject entry = entries[0]->asObject(); + + //finished + Common::String id = entry.getVal("id")->asString(); + Common::String name = entry.getVal("name")->asString(); + bool isDirectory = (entry.getVal("type")->asString() == "folder"); + uint32 size = 0, timestamp = 0; + if (entry.contains("size")) { + if (entry.getVal("size")->isString()) + size = entry.getVal("size")->asString().asUint64(); + else if (entry.getVal("size")->isIntegerNumber()) + size = entry.getVal("size")->asIntegerNumber(); + else + warning("strange type for field 'size'"); + } + if (entry.contains("modified_at") && entry.getVal("modified_at")->isString()) + timestamp = ISO8601::convertToTimestamp(entry.getVal("modified_at")->asString()); + + //as we list directory by id, we can't determine full path for the file, so we leave it empty + finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory)); + 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("no file info to return"); + finishUpload(StorageFile(_savePath, 0, 0, false)); + } else { + warning("null, not json"); + finishError(error); + } + + delete json; +} + +void BoxUploadRequest::notUploadedCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); +} + +void BoxUploadRequest::handle() {} + +void BoxUploadRequest::restart() { start(); } + +void BoxUploadRequest::finishUpload(StorageFile file) { + Request::finishSuccess(); + if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file)); +} + +} // End of namespace Box +} // End of namespace Cloud diff --git a/backends/cloud/box/boxuploadrequest.h b/backends/cloud/box/boxuploadrequest.h new file mode 100644 index 00000000000..3d15aa70b3e --- /dev/null +++ b/backends/cloud/box/boxuploadrequest.h @@ -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 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_BOX_BOXUPLOADREQUEST_H +#define BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_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 Box { +class BoxStorage; + +class BoxUploadRequest: public Networking::Request { + BoxStorage *_storage; + Common::String _savePath, _localPath; + Storage::UploadCallback _uploadCallback; + Request *_workingRequest; + bool _ignoreCallback; + Common::String _resolvedId, _parentId; + + void start(); + void resolveId(); + void idResolvedCallback(Storage::UploadResponse response); + void idResolveFailedCallback(Networking::ErrorResponse error); + void upload(); + void uploadedCallback(Networking::JsonResponse response); + void notUploadedCallback(Networking::ErrorResponse error); + void finishUpload(StorageFile status); + +public: + BoxUploadRequest(BoxStorage *storage, Common::String path, Common::String localPath, Storage::UploadCallback callback, Networking::ErrorCallback ecb); + virtual ~BoxUploadRequest(); + + virtual void handle(); + virtual void restart(); +}; + +} // End of namespace Box +} // End of namespace Cloud + +#endif diff --git a/backends/module.mk b/backends/module.mk index 67246b8b02c..2d0a61bba58 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -31,6 +31,7 @@ MODULE_OBJS += \ cloud/box/boxstorage.o \ cloud/box/boxlistdirectorybyidrequest.o \ cloud/box/boxtokenrefresher.o \ + cloud/box/boxuploadrequest.o \ cloud/dropbox/dropboxstorage.o \ cloud/dropbox/dropboxcreatedirectoryrequest.o \ cloud/dropbox/dropboxlistdirectoryrequest.o \