diff options
29 files changed, 652 insertions, 507 deletions
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp index 8d5e244e45..7dde74f88d 100644 --- a/backends/cloud/downloadrequest.cpp +++ b/backends/cloud/downloadrequest.cpp @@ -27,31 +27,55 @@ namespace Cloud { -DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile): - Request(0), _boolCallback(callback), _remoteFileStream(0), _localFile(dumpFile) { - storage->streamFile(remoteFile, new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback)); +DownloadRequest::DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile): + Request(nullptr, ecb), _boolCallback(callback), _localFile(dumpFile), _remoteFileName(remoteFile), _storage(storage), + _remoteFileStream(nullptr), _workingRequest(nullptr), _ignoreCallback(false) { + start(); } -void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse pair) { - if (!pair.value) { - warning("DownloadRequest: no ReadStream passed"); - finish(); - return; - } +DownloadRequest::~DownloadRequest() { + _ignoreCallback = true; + if (_workingRequest) _workingRequest->finish(); + delete _boolCallback; + delete _localFile; +} + +void DownloadRequest::start() { + _ignoreCallback = true; + if (_workingRequest) _workingRequest->finish(); + _remoteFileStream = nullptr; + //TODO: reopen DumpFile + _ignoreCallback = false; + + _workingRequest = _storage->streamFile( + _remoteFileName, + new Common::Callback<DownloadRequest, Networking::NetworkReadStreamResponse>(this, &DownloadRequest::streamCallback), + new Common::Callback<DownloadRequest, Networking::ErrorResponse>(this, &DownloadRequest::streamErrorCallback) + ); +} - _remoteFileStream = (Networking::NetworkReadStream *)pair.value; +void DownloadRequest::streamCallback(Networking::NetworkReadStreamResponse response) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + _remoteFileStream = (Networking::NetworkReadStream *)response.value; +} + +void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); } void DownloadRequest::handle() { if (!_localFile) { warning("DownloadRequest: no file to write"); - finish(); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); return; } if (!_localFile->isOpen()) { warning("DownloadRequest: failed to open file to write"); - finish(); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); return; } @@ -67,7 +91,7 @@ void DownloadRequest::handle() { if (readBytes != 0) if (_localFile->write(buf, readBytes) != readBytes) { warning("DownloadRequest: unable to write all received bytes into output file"); - finish(); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); return; } @@ -77,26 +101,20 @@ void DownloadRequest::handle() { //TODO: do something about it actually } - finishBool(_remoteFileStream->httpResponseCode() == 200); + finishSuccess(_remoteFileStream->httpResponseCode() == 200); _localFile->close(); //yes, I know it's closed automatically in ~DumpFile() } } void DownloadRequest::restart() { - //this request doesn't know anything about the _remoteFileStream it's reading - //thus, it can't restart it - warning("DownloadRequest: cannot be restarted"); - finish(); - //TODO: fix that -} - -void DownloadRequest::finish() { - finishBool(false); + warning("DownloadRequest: can't restart as there are no means to reopen DumpFile"); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); + //start(); } -void DownloadRequest::finishBool(bool success) { - Request::finish(); +void DownloadRequest::finishSuccess(bool success) { + Request::finishSuccess(); if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success)); } diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h index 0bad5df279..9e3421d777 100644 --- a/backends/cloud/downloadrequest.h +++ b/backends/cloud/downloadrequest.h @@ -31,23 +31,24 @@ namespace Cloud { class DownloadRequest: public Networking::Request { - Storage::BoolCallback _boolCallback; + Storage::BoolCallback _boolCallback; + Common::DumpFile *_localFile; + Common::String _remoteFileName; + Storage *_storage; Networking::NetworkReadStream *_remoteFileStream; - Common::DumpFile *_localFile; + Request *_workingRequest; + bool _ignoreCallback; - void streamCallback(Networking::NetworkReadStreamResponse pair); - - void finishBool(bool success); + void start(); + void streamCallback(Networking::NetworkReadStreamResponse response); + void streamErrorCallback(Networking::ErrorResponse error); + void finishSuccess(bool success); public: - DownloadRequest(Storage *storage, Storage::BoolCallback callback, Common::String remoteFile, Common::DumpFile *dumpFile); - virtual ~DownloadRequest() { - delete _boolCallback; - delete _localFile; - } + DownloadRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb, Common::String remoteFile, Common::DumpFile *dumpFile); + virtual ~DownloadRequest(); virtual void handle(); virtual void restart(); - virtual void finish(); }; } // End of namespace Cloud diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp index 2796a4c19e..d782f81a69 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp @@ -31,8 +31,8 @@ namespace Cloud { namespace Dropbox { -DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive): - Networking::Request(0), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb), +DropboxListDirectoryRequest::DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive): + Networking::Request(nullptr, ecb), _requestedPath(path), _requestedRecursive(recursive), _listDirectoryCallback(cb), _token(token), _workingRequest(nullptr), _ignoreCallback(false) { start(); } @@ -49,8 +49,9 @@ void DropboxListDirectoryRequest::start() { _files.clear(); _ignoreCallback = false; - Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder"); + Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); + Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder"); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); @@ -66,33 +67,32 @@ void DropboxListDirectoryRequest::start() { _workingRequest = ConnMan.addRequest(request); } - -void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair) { +void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse response) { _workingRequest = nullptr; if (_ignoreCallback) return; - ListDirectoryStatus status(_files); - Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request; + Networking::ErrorResponse error(this); + Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; if (rq && rq->getNetworkReadStream()) - status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); + error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); - Common::JSONValue *json = pair.value; + Common::JSONValue *json = response.value; if (json) { - Common::JSONObject response = json->asObject(); + Common::JSONObject responseObjecct = json->asObject(); - if (response.contains("error") || response.contains("error_summary")) { - warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str()); - status.failed = true; - status.response = json->stringify(); - finishStatus(status); + if (responseObjecct.contains("error") || responseObjecct.contains("error_summary")) { + warning("Dropbox returned error: %s", responseObjecct.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 (response.contains("entries")) { - Common::JSONArray items = response.getVal("entries")->asArray(); + if (responseObjecct.contains("entries")) { + Common::JSONArray items = responseObjecct.getVal("entries")->asArray(); for (uint32 i = 0; i < items.size(); ++i) { Common::JSONObject item = items[i]->asObject(); Common::String path = item.getVal("path_lower")->asString(); @@ -106,47 +106,47 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse pair } } - bool hasMore = (response.contains("has_more") && response.getVal("has_more")->asBool()); + bool hasMore = (responseObjecct.contains("has_more") && responseObjecct.getVal("has_more")->asBool()); - if (hasMore) { - Networking::JsonCallback innerCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/2/files/list_folder/continue"); + if (hasMore) { + Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, Networking::JsonResponse>(this, &DropboxListDirectoryRequest::responseCallback); + Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, Networking::ErrorResponse>(this, &DropboxListDirectoryRequest::errorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, "https://api.dropboxapi.com/2/files/list_folder/continue"); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/json"); Common::JSONObject jsonRequestParameters; - jsonRequestParameters.setVal("cursor", new Common::JSONValue(response.getVal("cursor")->asString())); + jsonRequestParameters.setVal("cursor", new Common::JSONValue(responseObjecct.getVal("cursor")->asString())); Common::JSONValue value(jsonRequestParameters); request->addPostField(Common::JSON::stringify(&value)); _workingRequest = ConnMan.addRequest(request); } else { - finishStatus(status); + finishSuccess(_files); } } else { warning("null, not json"); - status.failed = true; - finishStatus(status); + error.failed = true; + finishError(error); } delete json; } +void DropboxListDirectoryRequest::errorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); +} + void DropboxListDirectoryRequest::handle() {} void DropboxListDirectoryRequest::restart() { start(); } -void DropboxListDirectoryRequest::finish() { - Common::Array<StorageFile> files; - ListDirectoryStatus status(files); - status.interrupted = true; - finishStatus(status); -} - -void DropboxListDirectoryRequest::finishStatus(ListDirectoryStatus status) { - Request::finish(); - if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status)); +void DropboxListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) { + Request::finishSuccess(); + if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files)); } } // End of namespace Dropbox diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h index 3c7c1fd464..3a83af06aa 100644 --- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h +++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h @@ -42,15 +42,15 @@ class DropboxListDirectoryRequest: public Networking::Request { bool _ignoreCallback; void start(); - void responseCallback(Networking::JsonResponse pair); - void finishStatus(ListDirectoryStatus status); + void responseCallback(Networking::JsonResponse response); + void errorCallback(Networking::ErrorResponse error); + void finishSuccess(Common::Array<StorageFile> &files); public: - DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false); + DropboxListDirectoryRequest(Common::String token, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false); virtual ~DropboxListDirectoryRequest(); virtual void handle(); virtual void restart(); - virtual void finish(); }; } // End of namespace Dropbox diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp index b33e2b6776..d22e0abf60 100644 --- a/backends/cloud/dropbox/dropboxstorage.cpp +++ b/backends/cloud/dropbox/dropboxstorage.cpp @@ -96,118 +96,93 @@ void DropboxStorage::saveConfig(Common::String keyPrefix) { ConfMan.set(keyPrefix + "user_id", _uid, "cloud"); } -void DropboxStorage::printFiles(FileArrayResponse pair) { +void DropboxStorage::printFiles(FileArrayResponse response) { debug("files:"); - Common::Array<StorageFile> &files = pair.value; + Common::Array<StorageFile> &files = response.value; for (uint32 i = 0; i < files.size(); ++i) debug("\t%s", files[i].name().c_str()); } -void DropboxStorage::printBool(BoolResponse pair) { - debug("bool: %s", (pair.value?"true":"false")); +void DropboxStorage::printBool(BoolResponse response) { + debug("bool: %s", (response.value?"true":"false")); } -void DropboxStorage::printUploadStatus(UploadResponse pair) { - debug(" "); - UploadStatus status = pair.value; - if (status.interrupted) { - debug("upload interrupted by user"); - return; - } - if (status.failed) { - debug("upload failed with following response:"); - debug("%s", status.response.c_str()); - return; - } - debug("upload HTTP response code = %ld", status.httpResponseCode); - if (!status.failed) { - debug("uploaded file info:"); - debug("\tpath: %s", status.file.path().c_str()); - debug("\tsize: %u", status.file.size()); - debug("\ttimestamp: %u", status.file.timestamp()); - } +void DropboxStorage::printStorageFile(UploadResponse response) { + debug("\nuploaded file info:"); + debug("\tpath: %s", response.value.path().c_str()); + debug("\tsize: %u", response.value.size()); + debug("\ttimestamp: %u", response.value.timestamp()); } -Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, bool recursive) { - return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, recursive)); +void DropboxStorage::printErrorResponse(Networking::ErrorResponse error) { + debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode); + debug("%s", error.response.c_str()); } -Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) { - return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback)); +Networking::ErrorCallback DropboxStorage::getErrorPrintingCallback() { + return new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::printErrorResponse); } -Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback) { +Networking::Request *DropboxStorage::listDirectory(Common::String path, ListDirectoryCallback outerCallback, Networking::ErrorCallback errorCallback, bool recursive) { + return ConnMan.addRequest(new DropboxListDirectoryRequest(_token, path, outerCallback, errorCallback, recursive)); +} + +Networking::Request *DropboxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { + return ConnMan.addRequest(new DropboxUploadRequest(_token, path, contents, callback, errorCallback)); +} + +Networking::Request *DropboxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) { Common::File *f = new Common::File(); if (!f->open(localPath)) { warning("DropboxStorage: unable to open file to upload from"); - UploadStatus status(false, true, StorageFile(), "", -1); - if (callback) (*callback)(UploadResponse(nullptr, status)); + if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1)); + delete errorCallback; + delete callback; delete f; return nullptr; } - return upload(remotePath, f, callback); + return upload(remotePath, f, callback, errorCallback); } -Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) { +Networking::Request *DropboxStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) { Common::JSONObject jsonRequestParameters; jsonRequestParameters.setVal("path", new Common::JSONValue(path)); Common::JSONValue value(jsonRequestParameters); - Networking::CurlRequest *request = new Networking::CurlRequest(0, "https://content.dropboxapi.com/2/files/download"); + Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, "https://content.dropboxapi.com/2/files/download"); //TODO: is it right? 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 pair = request->execute(); - if (callback) (*callback)(pair); - return pair.request; + Networking::NetworkReadStreamResponse response = request->execute(); + if (callback) (*callback)(response); + return response.request; } -Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { +Networking::Request *DropboxStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) { Common::DumpFile *f = new Common::DumpFile(); if (!f->open(localPath, true)) { warning("DropboxStorage: unable to open file to download into"); - if (callback) (*callback)(BoolResponse(nullptr, false)); + if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1)); delete f; return nullptr; } - return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f)); + return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f)); } -Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) { - return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive)); +Networking::Request *DropboxStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) { + return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive)); } -Networking::Request *DropboxStorage::syncSaves(BoolCallback callback) { - //this is not the real syncSaves() implementation - //"" is root in Dropbox, not "/" - //this must create all these directories: - //return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0); - /* - return downloadFolder( - "/not_flat", "local/not_flat_1_level/", - new Common::Callback<DropboxStorage, FileArrayResponse>(this, &DropboxStorage::printFiles), - false - ); - */ - /* - debug("%s", ConfMan.get("savepath").c_str()); - Common::StringArray arr = g_system->getSavefileManager()->listSavefiles("*"); - for (uint32 i = 0; i < arr.size(); ++i) { - debug("%s", arr[i].c_str()); - } - debug("EOL"); - */ - //return upload("/remote/backslash", "C:\\Users\\Tkachov\\AppData\\Roaming\\ScummVM\\Saved games\\sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus)); - //return upload("/remote/slash", "C:/Users/Tkachov/AppData/Roaming/ScummVM/Saved games/sword25.000", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus)); - return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool))); - //return upload("/remote/test4.bmp", "final.bmp", new Common::Callback<DropboxStorage, UploadResponse>(this, &DropboxStorage::printUploadStatus)); +Networking::Request *DropboxStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) { + //this might be the real syncSaves() implementation + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<DropboxStorage, BoolResponse>(this, &DropboxStorage::printBool), getErrorPrintingCallback())); //TODO } -Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { +Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback, Networking::ErrorCallback errorCallback) { Networking::JsonCallback innerCallback = new Common::CallbackBridge<DropboxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &DropboxStorage::infoInnerCallback, outerCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://api.dropboxapi.com/1/account/info"); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, "https://api.dropboxapi.com/1/account/info"); request->addHeader("Authorization: Bearer " + _token); return ConnMan.addRequest(request); //that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback @@ -216,8 +191,8 @@ Networking::Request *DropboxStorage::info(StorageInfoCallback outerCallback) { //and then calls the outerCallback (which wants to receive StorageInfo, not void *) } -void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse pair) { - Common::JSONValue *json = pair.value; +void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) { + Common::JSONValue *json = response.value; if (!json) { warning("NULL passed instead of JSON"); delete outerCallback; @@ -241,11 +216,11 @@ void DropboxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networ delete json; } -void DropboxStorage::infoMethodCallback(StorageInfoResponse pair) { +void DropboxStorage::infoMethodCallback(StorageInfoResponse response) { debug("\nStorage info:"); - debug("User name: %s", pair.value.name().c_str()); - debug("Email: %s", pair.value.email().c_str()); - debug("Disk usage: %u/%u", pair.value.used(), pair.value.available()); + debug("User name: %s", response.value.name().c_str()); + debug("Email: %s", response.value.email().c_str()); + debug("Disk usage: %u/%u", response.value.used(), response.value.available()); } DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) { @@ -298,7 +273,7 @@ void DropboxStorage::authThroughConsole() { void DropboxStorage::getAccessToken(Common::String code) { Networking::JsonCallback callback = new Common::GlobalFunctionCallback<Networking::JsonResponse>(saveAccessTokenCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, "https://api.dropboxapi.com/1/oauth2/token"); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, nullptr, "https://api.dropboxapi.com/1/oauth2/token"); //TODO request->addPostField("code=" + code); request->addPostField("grant_type=authorization_code"); request->addPostField("client_id=" + Common::String(KEY)); diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h index d9967d69f6..0082e5cebd 100644 --- a/backends/cloud/dropbox/dropboxstorage.h +++ b/backends/cloud/dropbox/dropboxstorage.h @@ -45,9 +45,12 @@ class DropboxStorage: public Cloud::Storage { /** Constructs StorageInfo based on JSON response from cloud. */ void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json); - void printFiles(FileArrayResponse pair); - void printBool(BoolResponse pair); - void printUploadStatus(UploadResponse pair); + void printFiles(FileArrayResponse response); + void printBool(BoolResponse response); + void printStorageFile(UploadResponse response); + void printErrorResponse(Networking::ErrorResponse error); + + Networking::ErrorCallback getErrorPrintingCallback(); public: virtual ~DropboxStorage(); @@ -68,38 +71,38 @@ public: /** Public Cloud API comes down there. */ /** Returns ListDirectoryStatus struct with list of files. */ - virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false); + 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); - virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback); + virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback); + virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback); /** Returns pointer to Networking::NetworkReadStream. */ - virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback); + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback); /** Calls the callback when finished. */ - virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback); + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback); /** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */ - virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false); + virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false); /** Calls the callback when finished. */ - virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual Networking::Request *syncSaves(BoolCallback callback); + virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback); /** Calls the callback when finished. */ - virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Returns the StorageInfo struct. */ - virtual Networking::Request *info(StorageInfoCallback callback); + virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback); /** This method is passed into info(). (Temporary) */ - void infoMethodCallback(StorageInfoResponse pair); + void infoMethodCallback(StorageInfoResponse response); /** Returns whether saves sync process is running. */ virtual bool isSyncing() { return false; } //TODO diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp index e64a8837b8..50a1b8a612 100644 --- a/backends/cloud/dropbox/dropboxuploadrequest.cpp +++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp @@ -32,8 +32,8 @@ namespace Cloud { namespace Dropbox { -DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback): - Networking::Request(0), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback), +DropboxUploadRequest::DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb): + Networking::Request(nullptr, ecb), _token(token), _savePath(path), _contentsStream(contents), _uploadCallback(callback), _workingRequest(nullptr), _ignoreCallback(false) { start(); } @@ -50,7 +50,7 @@ void DropboxUploadRequest::start() { if (_workingRequest) _workingRequest->finish(); if (!_contentsStream->seek(0)) { warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)"); - finish(); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); } _ignoreCallback = false; @@ -97,8 +97,9 @@ void DropboxUploadRequest::uploadNextPart() { } Common::JSONValue value(jsonRequestParameters); - Networking::JsonCallback innerCallback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, url); + Networking::JsonCallback callback = new Common::Callback<DropboxUploadRequest, Networking::JsonResponse>(this, &DropboxUploadRequest::partUploadedCallback); + Networking::ErrorCallback failureCallback = new Common::Callback<DropboxUploadRequest, Networking::ErrorResponse>(this, &DropboxUploadRequest::partUploadedErrorCallback); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, failureCallback, url); request->addHeader("Authorization: Bearer " + _token); request->addHeader("Content-Type: application/octet-stream"); request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value)); @@ -110,46 +111,45 @@ void DropboxUploadRequest::uploadNextPart() { _workingRequest = ConnMan.addRequest(request); } -void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) { - if (_ignoreCallback) return; +void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse response) { + debug("partUploadedCallback"); _workingRequest = nullptr; + if (_ignoreCallback) return; - UploadStatus status; - Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request; + Networking::ErrorResponse error(this, false, true, "", -1); + Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; if (rq && rq->getNetworkReadStream()) - status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); + error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); - Common::JSONValue *json = pair.value; + Common::JSONValue *json = response.value; if (json) { bool needsFinishRequest = false; if (json->isObject()) { - Common::JSONObject response = json->asObject(); + Common::JSONObject object = json->asObject(); //debug("%s", json->stringify(true).c_str()); - if (response.contains("error") || response.contains("error_summary")) { - warning("Dropbox returned error: %s", response.getVal("error_summary")->asString().c_str()); + if (object.contains("error") || object.contains("error_summary")) { + warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str()); delete json; - status.failed = true; - status.response = json->stringify(true); - finishUpload(status); + error.response = json->stringify(true); + finishError(error); return; } - if (response.contains("server_modified")) { + if (object.contains("server_modified")) { //finished - Common::String path = response.getVal("path_lower")->asString(); - uint32 size = response.getVal("size")->asIntegerNumber(); - uint32 timestamp = ISO8601::convertToTimestamp(response.getVal("server_modified")->asString()); - status.file = StorageFile(path, size, timestamp, false); - finishUpload(status); + Common::String path = object.getVal("path_lower")->asString(); + uint32 size = object.getVal("size")->asIntegerNumber(); + uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("server_modified")->asString()); + finishSuccess(StorageFile(path, size, timestamp, false)); return; } if (_sessionId == "") { - if (response.contains("session_id")) - _sessionId = response.getVal("session_id")->asString(); + if (object.contains("session_id")) + _sessionId = object.getVal("session_id")->asString(); else warning("no session_id found in Dropbox's response"); needsFinishRequest = true; @@ -157,36 +157,33 @@ void DropboxUploadRequest::partUploadedCallback(Networking::JsonResponse pair) { } if (!needsFinishRequest && (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1)) { - if (status.file.name() == "") { - status.file = StorageFile(_savePath, 0, 0, false); - warning("no file info to put into status"); - } - finishUpload(status); + warning("no file info to return"); + finishSuccess(StorageFile(_savePath, 0, 0, false)); } else { uploadNextPart(); } } else { - warning("null, not json"); - status.failed = true; - finishUpload(status); + warning("null, not json"); + finishError(error); } delete json; } +void DropboxUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) { + debug("partUploadedErrorCallback"); + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); +} + void DropboxUploadRequest::handle() {} void DropboxUploadRequest::restart() { start(); } -void DropboxUploadRequest::finish() { - UploadStatus status; - status.interrupted = true; - finishUpload(status); -} - -void DropboxUploadRequest::finishUpload(UploadStatus status) { - Request::finish(); - if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, status)); +void DropboxUploadRequest::finishSuccess(StorageFile file) { + Request::finishSuccess(); + if (_uploadCallback) (*_uploadCallback)(Storage::UploadResponse(this, file)); } } // End of namespace Dropbox diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h index 9b68995969..a85d7ef883 100644 --- a/backends/cloud/dropbox/dropboxuploadrequest.h +++ b/backends/cloud/dropbox/dropboxuploadrequest.h @@ -42,16 +42,16 @@ class DropboxUploadRequest: public Networking::Request { void start(); void uploadNextPart(); - void partUploadedCallback(Networking::JsonResponse pair); - void finishUpload(UploadStatus status); + void partUploadedCallback(Networking::JsonResponse response); + void partUploadedErrorCallback(Networking::ErrorResponse error); + void finishSuccess(StorageFile status); public: - DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback); + DropboxUploadRequest(Common::String token, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb); virtual ~DropboxUploadRequest(); virtual void handle(); virtual void restart(); - virtual void finish(); }; } // End of namespace Dropbox diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp index db132ffc8a..19f6c6c9b7 100644 --- a/backends/cloud/folderdownloadrequest.cpp +++ b/backends/cloud/folderdownloadrequest.cpp @@ -25,8 +25,8 @@ namespace Cloud { -FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive): - Request(nullptr), _storage(storage), _fileArrayCallback(callback), +FolderDownloadRequest::FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive): + Request(nullptr, ecb), _storage(storage), _fileArrayCallback(callback), _remoteDirectoryPath(remoteDirectoryPath), _localDirectoryPath(localDirectoryPath), _recursive(recursive), _workingRequest(nullptr), _ignoreCallback(false) { start(); @@ -51,33 +51,39 @@ void FolderDownloadRequest::start() { _workingRequest = _storage->listDirectory( _remoteDirectoryPath, new Common::Callback<FolderDownloadRequest, Storage::ListDirectoryResponse>(this, &FolderDownloadRequest::directoryListedCallback), + new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::directoryListedErrorCallback), _recursive ); } -void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) { +void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryResponse response) { + _workingRequest = nullptr; if (_ignoreCallback) return; - - ListDirectoryStatus status = pair.value; - if (status.failed || status.interrupted) { - finish(); - return; - } - - _files = pair.value.files; + _files = response.value; downloadNextFile(); } -void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse pair) { +void FolderDownloadRequest::directoryListedErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); +} + +void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse response) { + _workingRequest = nullptr; if (_ignoreCallback) return; - if (!pair.value) _failedFiles.push_back(_currentFile); + if (!response.value) _failedFiles.push_back(_currentFile); downloadNextFile(); } +void FolderDownloadRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) { + fileDownloadedCallback(Storage::BoolResponse(error.request, false)); +} + void FolderDownloadRequest::downloadNextFile() { do { if (_files.empty()) { - finishFiles(_failedFiles); + finishSuccess(_failedFiles); return; } @@ -105,18 +111,17 @@ void FolderDownloadRequest::downloadNextFile() { debug("%s -> %s", remotePath.c_str(), localPath.c_str()); _workingRequest = _storage->download( remotePath, localPath, - new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback) + new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback), + new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::fileDownloadedErrorCallback) ); } -void FolderDownloadRequest::finish() { - //TODO: somehow indicate that request was interrupted - Common::Array<StorageFile> files; - finishFiles(files); -} +void FolderDownloadRequest::handle() {} + +void FolderDownloadRequest::restart() { start(); } -void FolderDownloadRequest::finishFiles(Common::Array<StorageFile> &files) { - Request::finish(); +void FolderDownloadRequest::finishSuccess(Common::Array<StorageFile> &files) { + Request::finishSuccess(); if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files)); } diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h index 779ea3334f..8fa3b1188b 100644 --- a/backends/cloud/folderdownloadrequest.h +++ b/backends/cloud/folderdownloadrequest.h @@ -40,17 +40,18 @@ class FolderDownloadRequest: public Networking::Request { bool _ignoreCallback; void start(); - void directoryListedCallback(Storage::ListDirectoryResponse pair); - void fileDownloadedCallback(Storage::BoolResponse pair); + void directoryListedCallback(Storage::ListDirectoryResponse response); + void directoryListedErrorCallback(Networking::ErrorResponse error); + void fileDownloadedCallback(Storage::BoolResponse response); + void fileDownloadedErrorCallback(Networking::ErrorResponse error); void downloadNextFile(); - void finishFiles(Common::Array<StorageFile> &files); + void finishSuccess(Common::Array<StorageFile> &files); public: - FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive); + FolderDownloadRequest(Storage *storage, Storage::FileArrayCallback callback, Networking::ErrorCallback ecb, Common::String remoteDirectoryPath, Common::String localDirectoryPath, bool recursive); virtual ~FolderDownloadRequest(); - virtual void handle() {} - virtual void restart() { start(); } - virtual void finish(); + virtual void handle(); + virtual void restart(); }; } // End of namespace Cloud diff --git a/backends/cloud/manager.cpp b/backends/cloud/manager.cpp index 13f23b8e6e..2f1533c50d 100644 --- a/backends/cloud/manager.cpp +++ b/backends/cloud/manager.cpp @@ -110,9 +110,9 @@ Storage *Manager::getCurrentStorage() { return nullptr; } -void Manager::syncSaves(Storage::BoolCallback callback) { +void Manager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) { Storage *storage = getCurrentStorage(); - if (storage) storage->syncSaves(callback); + if (storage) storage->syncSaves(callback, errorCallback); } } // End of namespace Cloud diff --git a/backends/cloud/manager.h b/backends/cloud/manager.h index f472b80ae3..013e117046 100644 --- a/backends/cloud/manager.h +++ b/backends/cloud/manager.h @@ -42,7 +42,7 @@ public: virtual void addStorage(Cloud::Storage *storage, bool makeCurrent = true, bool saveConfig = true); virtual Storage *getCurrentStorage(); - virtual void syncSaves(Storage::BoolCallback callback); + virtual void syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback); }; } // End of namespace Cloud diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp index dbd5e44c0b..e362600389 100644 --- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp +++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp @@ -31,8 +31,8 @@ namespace Cloud { namespace OneDrive { -OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive): - Networking::Request(0), +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(); @@ -55,12 +55,12 @@ void OneDriveListDirectoryRequest::start() { _ignoreCallback = false; _directoriesQueue.push_back(_requestedPath); - listNextDirectory(_files); + listNextDirectory(); } -void OneDriveListDirectoryRequest::listNextDirectory(ListDirectoryStatus status) { +void OneDriveListDirectoryRequest::listNextDirectory() { if (_directoriesQueue.empty()) { - finishStatus(status); + finishSuccess(_files); return; } @@ -78,36 +78,37 @@ void OneDriveListDirectoryRequest::listNextDirectory(ListDirectoryStatus status) void OneDriveListDirectoryRequest::makeRequest(Common::String url) { Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback); - Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, url.c_str()); + 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 pair) { +void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse response) { _workingRequest = nullptr; - Common::JSONValue *json = pair.value; + Common::JSONValue *json = response.value; if (_ignoreCallback) { delete json; return; } - ListDirectoryStatus status(_files); - Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)pair.request; + Networking::ErrorResponse error(this); + Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; if (rq && rq->getNetworkReadStream()) - status.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); + error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); if (!json) { - status.failed = true; - finishStatus(status); + error.failed = true; + finishError(error); return; } - Common::JSONObject response = json->asObject(); + Common::JSONObject object = json->asObject(); //TODO: check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults - Common::JSONArray items = response.getVal("value")->asArray(); + Common::JSONArray items = object.getVal("value")->asArray(); for (uint32 i = 0; i < items.size(); ++i) { Common::JSONObject item = items[i]->asObject(); @@ -123,26 +124,29 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo } } - bool hasMore = response.contains("@odata.nextLink"); + bool hasMore = object.contains("@odata.nextLink"); if (hasMore) { - makeRequest(response.getVal("@odata.nextLink")->asString()); + makeRequest(object.getVal("@odata.nextLink")->asString()); } else { - listNextDirectory(status); + listNextDirectory(); } delete json; } -void OneDriveListDirectoryRequest::finish() { - Common::Array<StorageFile> files; - ListDirectoryStatus status(files); - status.interrupted = true; - finishStatus(status); +void OneDriveListDirectoryRequest::listedDirectoryErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + finishError(error); } -void OneDriveListDirectoryRequest::finishStatus(ListDirectoryStatus status) { - Request::finish(); - if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, status)); +void OneDriveListDirectoryRequest::handle() {} + +void OneDriveListDirectoryRequest::restart() { start(); } + +void OneDriveListDirectoryRequest::finishSuccess(Common::Array<StorageFile> &files) { + Request::finishSuccess(); + if (_listDirectoryCallback) (*_listDirectoryCallback)(Storage::ListDirectoryResponse(this, files)); } } // End of namespace OneDrive diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h index a05dd871dd..b8adfe7ef0 100644 --- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h +++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h @@ -45,17 +45,17 @@ class OneDriveListDirectoryRequest: public Networking::Request { bool _ignoreCallback; void start(); - void listNextDirectory(ListDirectoryStatus status); - void listedDirectoryCallback(Networking::JsonResponse pair); + void listNextDirectory(); + void listedDirectoryCallback(Networking::JsonResponse response); + void listedDirectoryErrorCallback(Networking::ErrorResponse error); void makeRequest(Common::String url); - void finishStatus(ListDirectoryStatus status); + void finishSuccess(Common::Array<StorageFile> &files); public: - OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, bool recursive = false); + OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive = false); virtual ~OneDriveListDirectoryRequest(); - virtual void handle() {} - virtual void restart() { start(); } - virtual void finish(); + virtual void handle(); + virtual void restart(); }; } // End of namespace OneDrive diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp index b0f4f7be65..8746b7ab33 100644 --- a/backends/cloud/onedrive/onedrivestorage.cpp +++ b/backends/cloud/onedrive/onedrivestorage.cpp @@ -74,7 +74,7 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) } Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback); - Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, "https://login.live.com/oauth20_token.srf"); + Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, getErrorPrintingCallback(), "https://login.live.com/oauth20_token.srf"); //TODO if (codeFlow) { request->addPostField("code=" + code); request->addPostField("grant_type=authorization_code"); @@ -88,8 +88,8 @@ void OneDriveStorage::getAccessToken(BoolCallback callback, Common::String code) ConnMan.addRequest(request); } -void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair) { - Common::JSONValue *json = pair.value; +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)); @@ -111,8 +111,8 @@ void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResp delete json; } -void OneDriveStorage::codeFlowComplete(BoolResponse pair) { - if (!pair.value) { +void OneDriveStorage::codeFlowComplete(BoolResponse response) { + if (!response.value) { warning("OneDriveStorage: failed to get access token through code flow"); return; } @@ -130,8 +130,8 @@ void OneDriveStorage::saveConfig(Common::String keyPrefix) { ConfMan.set(keyPrefix + "refresh_token", _refreshToken, "cloud"); } -void OneDriveStorage::printJson(Networking::JsonResponse pair) { - Common::JSONValue *json = pair.value; +void OneDriveStorage::printJson(Networking::JsonResponse response) { + Common::JSONValue *json = response.value; if (!json) { warning("printJson: NULL"); return; @@ -141,77 +141,85 @@ void OneDriveStorage::printJson(Networking::JsonResponse pair) { delete json; } -void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair) { - if (!pair.value) { +void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) { + if (!response.value) { warning("fileInfoCallback: NULL"); - if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0)); + if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0)); return; } - Common::JSONObject result = pair.value->asObject(); + Common::JSONObject result = response.value->asObject(); if (result.contains("@content.downloadUrl")) { const char *url = result.getVal("@content.downloadUrl")->asString().c_str(); if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse( - pair.request, + response.request, new Networking::NetworkReadStream(url, 0, "") )); } else { warning("downloadUrl not found in passed JSON"); - debug("%s", pair.value->stringify().c_str()); - if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(pair.request, 0)); + debug("%s", response.value->stringify().c_str()); + if (outerCallback) (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, 0)); } - delete pair.value; + delete response.value; } -Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive) { - return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, recursive)); +Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) { + return ConnMan.addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive)); } -Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback) { +Networking::Request *OneDriveStorage::streamFile(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) { Common::String url = "https://api.onedrive.com/v1.0/drive/special/approot:/" + path; Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback); - Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, url.c_str()); + Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str()); request->addHeader("Authorization: Bearer " + _token); return ConnMan.addRequest(request); } -Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback) { +Networking::Request *OneDriveStorage::download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) { Common::DumpFile *f = new Common::DumpFile(); if (!f->open(localPath, true)) { warning("OneDriveStorage: unable to open file to download into"); - if (callback) (*callback)(BoolResponse(nullptr, false)); + if (errorCallback) (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "", -1)); delete f; return nullptr; } - return ConnMan.addRequest(new DownloadRequest(this, callback, remotePath, f)); + return ConnMan.addRequest(new DownloadRequest(this, callback, errorCallback, remotePath, f)); } /** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */ -Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive) { - return ConnMan.addRequest(new FolderDownloadRequest(this, callback, remotePath, localPath, recursive)); +Networking::Request *OneDriveStorage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) { + return ConnMan.addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive)); } -void OneDriveStorage::fileDownloaded(BoolResponse pair) { - if (pair.value) debug("file downloaded!"); +void OneDriveStorage::fileDownloaded(BoolResponse response) { + if (response.value) debug("file downloaded!"); else debug("download failed!"); } -void OneDriveStorage::printFiles(FileArrayResponse pair) { +void OneDriveStorage::printFiles(FileArrayResponse response) { debug("files:"); - Common::Array<StorageFile> &files = pair.value; + Common::Array<StorageFile> &files = response.value; for (uint32 i = 0; i < files.size(); ++i) debug("\t%s", files[i].path().c_str()); } -void OneDriveStorage::printBool(BoolResponse pair) { - debug("bool: %s", pair.value ? "true" : "false"); +void OneDriveStorage::printBool(BoolResponse response) { + debug("bool: %s", response.value ? "true" : "false"); +} + +void OneDriveStorage::printErrorResponse(Networking::ErrorResponse error) { + debug("error response (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode); + debug("%s", error.response.c_str()); } +Networking::ErrorCallback OneDriveStorage::getErrorPrintingCallback() { + return new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::printErrorResponse); +} -Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) { +Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) { //this is not the real syncSaves() implementation /* Networking::JsonCallback innerCallback = new Common::Callback<OneDriveStorage, Networking::RequestJsonPair>(this, &OneDriveStorage::printJson); @@ -220,7 +228,7 @@ Networking::Request *OneDriveStorage::syncSaves(BoolCallback callback) { return ConnMan.addRequest(request); */ //return downloadFolder("subfolder", "local/onedrive/subfolder_downloaded", new Common::Callback<OneDriveStorage, FileArrayResponse>(this, &OneDriveStorage::printFiles), false); - return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool))); + return ConnMan.addRequest(new SavesSyncRequest(this, new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::printBool), getErrorPrintingCallback())); //TODO } OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h index 43675fbfa5..f95ea89bc1 100644 --- a/backends/cloud/onedrive/onedrivestorage.h +++ b/backends/cloud/onedrive/onedrivestorage.h @@ -46,15 +46,18 @@ class OneDriveStorage: public Cloud::Storage { */ OneDriveStorage(Common::String code); - void tokenRefreshed(BoolCallback callback, Networking::JsonResponse pair); - void codeFlowComplete(BoolResponse pair); + void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response); + void codeFlowComplete(BoolResponse response); - void printJson(Networking::JsonResponse pair); - void fileDownloaded(BoolResponse pair); - void printFiles(FileArrayResponse pair); - void printBool(BoolResponse pair); + void printJson(Networking::JsonResponse response); + void fileDownloaded(BoolResponse response); + void printFiles(FileArrayResponse response); + void printBool(BoolResponse response); + void printErrorResponse(Networking::ErrorResponse error); - void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse pair); + Networking::ErrorCallback getErrorPrintingCallback(); + + void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response); public: virtual ~OneDriveStorage(); @@ -74,35 +77,35 @@ public: /** Public Cloud API comes down there. */ /** Returns ListDirectoryStatus struct with list of files. */ - virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false); + 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) { return nullptr; } //TODO - virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) { return nullptr; } + virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO + virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Returns pointer to Networking::NetworkReadStream. */ - virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback); + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback); /** Calls the callback when finished. */ - virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback); + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback); /** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */ - virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false); + virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false); /** Calls the callback when finished. */ - virtual Networking::Request *remove(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual Networking::Request *syncSaves(BoolCallback callback); + virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback); /** Calls the callback when finished. */ - virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Calls the callback when finished. */ - virtual Networking::Request *touch(Common::String path, BoolCallback callback) { return nullptr; } //TODO + virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Returns the StorageInfo struct. */ - virtual Networking::Request *info(StorageInfoCallback callback) { return nullptr; } //TODO + virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) { return nullptr; } //TODO /** Returns whether saves sync process is running. */ virtual bool isSyncing() { return false; } //TODO diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp index a0c41ff471..bc7bd74dbe 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp +++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp @@ -31,16 +31,16 @@ namespace Cloud { namespace OneDrive { -OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url): - CurlJsonRequest(callback, url), _parentStorage(parent) {} +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 pair) { - if (!pair.value) { +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"); - finish(); + finishError(Networking::ErrorResponse(this, false, true, "", -1)); return; } @@ -56,11 +56,10 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse pair) { retry(0); } -void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) { +void OneDriveTokenRefresher::finishSuccess(Common::JSONValue *json) { if (!json) { - //notify user of failure - warning("OneDriveTokenRefresher: got NULL instead of JSON"); - CurlJsonRequest::finishJson(nullptr); + //that's probably not an error (200 OK) + CurlJsonRequest::finishSuccess(nullptr); return; } @@ -74,19 +73,26 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) { Common::JSONObject error = result.getVal("error")->asObject(); bool irrecoverable = true; + Common::String code, message; if (error.contains("code")) { - Common::String code = error.getVal("code")->asString(); - debug("code = %s", code.c_str()); - //if (code == "itemNotFound") irrecoverable = true; + code = error.getVal("code")->asString(); + debug("code = %s", code.c_str()); } if (error.contains("message")) { - Common::String message = error.getVal("message")->asString(); + message = error.getVal("message")->asString(); debug("message = %s", message.c_str()); } - if (irrecoverable) { - CurlJsonRequest::finishJson(nullptr); + //determine whether token refreshing would help in this situation + if (code == "itemNotFound") { + if (message.contains("application ID")) + irrecoverable = false; + } + + if (irrecoverable) { + finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), -1)); //TODO: httpCode + delete json; return; } @@ -97,7 +103,7 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) { } //notify user of success - CurlJsonRequest::finishJson(json); + CurlJsonRequest::finishSuccess(json); } void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) { @@ -108,6 +114,10 @@ void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) 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 index 90ca9d603a..04b0bf26b8 100644 --- a/backends/cloud/onedrive/onedrivetokenrefresher.h +++ b/backends/cloud/onedrive/onedrivetokenrefresher.h @@ -35,19 +35,15 @@ class OneDriveTokenRefresher: public Networking::CurlJsonRequest { OneDriveStorage *_parentStorage; Common::Array<Common::String> _headers; - void tokenRefreshed(Storage::BoolResponse pair); + void tokenRefreshed(Storage::BoolResponse response); - virtual void finishJson(Common::JSONValue *json); + virtual void finishSuccess(Common::JSONValue *json); public: - OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, const char *url); + 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) { - _headers.push_back(header); - CurlJsonRequest::addHeader(header); - } + virtual void addHeader(Common::String header); }; } // End of namespace OneDrive diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp index e632bfff84..b48a99a48c 100644 --- a/backends/cloud/savessyncrequest.cpp +++ b/backends/cloud/savessyncrequest.cpp @@ -32,8 +32,8 @@ namespace Cloud { const char *SavesSyncRequest::TIMESTAMPS_FILENAME = "timestamps"; -SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback): - Request(nullptr), _storage(storage), _boolCallback(callback), +SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb): + Request(nullptr, ecb), _storage(storage), _boolCallback(callback), _workingRequest(nullptr), _ignoreCallback(false) { start(); } @@ -59,48 +59,35 @@ void SavesSyncRequest::start() { loadTimestamps(); //list saves directory - _workingRequest = _storage->listDirectory("/saves", new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback)); + _workingRequest = _storage->listDirectory( + "/saves", + new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback), + new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback) + ); } -void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pair) { +void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) { _workingRequest = nullptr; if (_ignoreCallback) return; - ListDirectoryStatus status = pair.value; - bool irrecoverable = status.interrupted || status.failed; - if (status.failed) { - Common::JSONValue *value = Common::JSON::parse(status.response.c_str()); - if (value) { - if (value->isObject()) { - Common::JSONObject object = value->asObject(); - //Dropbox-related error: - if (object.contains("error_summary")) { - Common::String summary = object.getVal("error_summary")->asString(); - if (summary.contains("not_found")) { - //oh how lucky we are! It's just user don't have /cloud/ folder yet! - irrecoverable = false; - } - } - } - delete value; - } + Common::HashMap<Common::String, bool> localFileNotAvailableInCloud; + for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) { + localFileNotAvailableInCloud[i->_key] = true; } - if (irrecoverable) { - finishBool(false); - return; - } - //determine which files to download and which files to upload - Common::Array<StorageFile> &remoteFiles = status.files; + Common::Array<StorageFile> &remoteFiles = response.value; for (uint32 i = 0; i < remoteFiles.size(); ++i) { StorageFile &file = remoteFiles[i]; if (file.isDirectory()) continue; if (file.name() == TIMESTAMPS_FILENAME) continue; + Common::String name = file.name(); if (!_localFilesTimestamps.contains(name)) _filesToDownload.push_back(file); else { + localFileNotAvailableInCloud[name] = false; + if (_localFilesTimestamps[name] != INVALID_TIMESTAMP) { if (_localFilesTimestamps[name] == file.timestamp()) continue; @@ -115,11 +102,10 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pa } } - //upload files with invalid timestamp (the ones we've added - means they might not have any remote version) - for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) { + //upload files which are unavailable in cloud + for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) { if (i->_key == TIMESTAMPS_FILENAME) continue; - if (i->_value == INVALID_TIMESTAMP) - _filesToUpload.push_back(i->_key); + if (i->_value) _filesToUpload.push_back(i->_key); } /////// @@ -137,6 +123,50 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse pa downloadNextFile(); } +void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + + bool irrecoverable = error.interrupted || error.failed; + if (error.failed) { + Common::JSONValue *value = Common::JSON::parse(error.response.c_str()); + if (value) { + if (value->isObject()) { + Common::JSONObject object = value->asObject(); + + //Dropbox-related error: + if (object.contains("error_summary")) { + 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 (errorNode.contains("code") && errorNode.contains("message")) { + Common::String code = errorNode.getVal("code")->asString(); + if (code == "itemNotFound") { + irrecoverable = false; + } + } + } + } + delete value; + } + } + + if (irrecoverable) { + finishError(error); + return; + } + + //we're lucky - user just lacks his "/cloud/" folder + Common::Array<StorageFile> files; + directoryListedCallback(Storage::ListDirectoryResponse(error.request, files)); +} + void SavesSyncRequest::downloadNextFile() { if (_filesToDownload.empty()) { uploadNextFile(); @@ -150,17 +180,18 @@ void SavesSyncRequest::downloadNextFile() { debug("downloading %s", _currentDownloadingFile.name().c_str()); /////// _workingRequest = _storage->download(_currentDownloadingFile.path(), concatWithSavesPath(_currentDownloadingFile.name()), - new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback) + new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback), + new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback) ); } -void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) { +void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) { _workingRequest = nullptr; if (_ignoreCallback) return; //stop syncing if download failed - if (!pair.value) { - finish(); + if (!response.value) { + finishError(Networking::ErrorResponse(this, false, true, "", -1)); return; } @@ -171,9 +202,17 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse pair) { downloadNextFile(); } +void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + + //stop syncing if download failed + finishError(error); +} + void SavesSyncRequest::uploadNextFile() { if (_filesToUpload.empty()) { - finishBool(true); + finishSuccess(true); return; } @@ -184,36 +223,45 @@ void SavesSyncRequest::uploadNextFile() { debug("uploading %s", _currentUploadingFile.c_str()); /////// _workingRequest = _storage->upload("/saves/" + _currentUploadingFile, g_system->getSavefileManager()->openRawFile(_currentUploadingFile), - new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback) + new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback), + new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback) ); } -void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse pair) { +void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) { _workingRequest = nullptr; if (_ignoreCallback) return; - UploadStatus status = pair.value; - - //stop syncing if upload failed - if (status.interrupted || status.failed) { - finish(); - return; - } - + //update local timestamp for the uploaded file - _localFilesTimestamps[_currentUploadingFile] = status.file.timestamp(); + _localFilesTimestamps[_currentUploadingFile] = response.value.timestamp(); //continue uploading files uploadNextFile(); } +void SavesSyncRequest::fileUploadedErrorCallback(Networking::ErrorResponse error) { + _workingRequest = nullptr; + if (_ignoreCallback) return; + + //stop syncing if upload failed + finishError(error); +} + void SavesSyncRequest::handle() {} void SavesSyncRequest::restart() { start(); } -void SavesSyncRequest::finish() { finishBool(false); } +void SavesSyncRequest::finishError(Networking::ErrorResponse error) { + debug("SavesSync::finishError"); + + //save updated timestamps (even if Request failed, there would be only valid timestamps) + saveTimestamps(); + + Request::finishError(error); +} -void SavesSyncRequest::finishBool(bool success) { - Request::finish(); +void SavesSyncRequest::finishSuccess(bool success) { + Request::finishSuccess(); //save updated timestamps (even if Request failed, there would be only valid timestamps) saveTimestamps(); @@ -233,7 +281,6 @@ void SavesSyncRequest::loadTimestamps() { warning("SavesSyncRequest: failed to open '%s' file to load timestamps", TIMESTAMPS_FILENAME); return; } - while (!file->eos()) { //read filename into buffer (reading until the first ' ') diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h index da7b27e9b6..bf44b70390 100644 --- a/backends/cloud/savessyncrequest.h +++ b/backends/cloud/savessyncrequest.h @@ -45,22 +45,25 @@ class SavesSyncRequest: public Networking::Request { bool _ignoreCallback; void start(); - void directoryListedCallback(Storage::ListDirectoryResponse pair); - void fileDownloadedCallback(Storage::BoolResponse pair); - void fileUploadedCallback(Storage::UploadResponse pair); + void directoryListedCallback(Storage::ListDirectoryResponse response); + void directoryListedErrorCallback(Networking::ErrorResponse error); + void fileDownloadedCallback(Storage::BoolResponse response); + void fileDownloadedErrorCallback(Networking::ErrorResponse error); + void fileUploadedCallback(Storage::UploadResponse response); + void fileUploadedErrorCallback(Networking::ErrorResponse error); void downloadNextFile(); void uploadNextFile(); - void finishBool(bool success); + virtual void finishError(Networking::ErrorResponse error); + void finishSuccess(bool success); void loadTimestamps(); void saveTimestamps(); Common::String concatWithSavesPath(Common::String name); public: - SavesSyncRequest(Storage *storage, Storage::BoolCallback callback); + SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb); virtual ~SavesSyncRequest(); virtual void handle(); - virtual void restart(); - virtual void finish(); + virtual void restart(); }; } // End of namespace Cloud diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h index 311b3fdc9f..32c437857c 100644 --- a/backends/cloud/storage.h +++ b/backends/cloud/storage.h @@ -34,53 +34,13 @@ namespace Cloud { -/** Struct to represent upload() resulting status. */ -struct UploadStatus { - /** true if Request was interrupted (finished by user with finish()) */ - bool interrupted; - /** true if Request has failed (bad server response or some other error occurred) */ - bool failed; - /** Contains uploaded file description (empty if failed) */ - StorageFile file; - /** Server's original response (empty if not failed) */ - Common::String response; - /** Server's HTTP response code. */ - long httpResponseCode; - - UploadStatus(): - interrupted(false), failed(false), file(), response(), httpResponseCode(-1) {} - - UploadStatus(bool interrupt, bool failure, StorageFile f, Common::String resp, long code): - interrupted(interrupt), failed(failure), file(f), response(resp), httpResponseCode(code) {} -}; - -/** Struct to represent upload() resulting status. */ -struct ListDirectoryStatus { - /** true if Request was interrupted (finished by user with finish()) */ - bool interrupted; - /** true if Request has failed (bad server response or some other error occurred) */ - bool failed; - /** Contains listed files (might be incomplete if failed or interrupted) */ - Common::Array<StorageFile> &files; - /** Server's original response (empty if not failed) */ - Common::String response; - /** Server's HTTP response code. */ - long httpResponseCode; - - ListDirectoryStatus(Common::Array<StorageFile> &f) : - interrupted(false), failed(false), files(f), response(), httpResponseCode(-1) {} - - ListDirectoryStatus(bool interrupt, bool failure, Common::Array<StorageFile> &f, Common::String resp, long code) : - interrupted(interrupt), failed(failure), files(f), response(resp), httpResponseCode(code) {} -}; - class Storage { public: typedef Networking::Response<Common::Array<StorageFile>&> FileArrayResponse; typedef Networking::Response<StorageInfo> StorageInfoResponse; typedef Networking::Response<bool> BoolResponse; - typedef Networking::Response<UploadStatus> UploadResponse; - typedef Networking::Response<ListDirectoryStatus> ListDirectoryResponse; + typedef Networking::Response<StorageFile> UploadResponse; + typedef Networking::Response<Common::Array<StorageFile> &> ListDirectoryResponse; typedef Common::BaseCallback<FileArrayResponse> *FileArrayCallback; typedef Common::BaseCallback<StorageInfoResponse> *StorageInfoCallback; @@ -113,35 +73,35 @@ public: */ /** Returns ListDirectoryStatus struct with list of files. */ - virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, bool recursive = false) = 0; + virtual Networking::Request *listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0; /** Returns UploadStatus struct with info about uploaded file. */ - virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback) = 0; - virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback) = 0; + virtual Networking::Request *upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0; + virtual Networking::Request *upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Returns pointer to Networking::NetworkReadStream. */ - virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback) = 0; + virtual Networking::Request *streamFile(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Calls the callback when finished. */ - virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback) = 0; + virtual Networking::Request *download(Common::String remotePath, Common::String localPath, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Returns Common::Array<StorageFile> with list of files, which were not downloaded. */ - virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, bool recursive = false) = 0; + virtual Networking::Request *downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false) = 0; /** Calls the callback when finished. */ - virtual Networking::Request *remove(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *remove(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Calls the callback when finished. */ - virtual Networking::Request *syncSaves(BoolCallback callback) = 0; + virtual Networking::Request *syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Calls the callback when finished. */ - virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Calls the callback when finished. */ - virtual Networking::Request *touch(Common::String path, BoolCallback callback) = 0; + virtual Networking::Request *touch(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Returns the StorageInfo struct. */ - virtual Networking::Request *info(StorageInfoCallback callback) = 0; + virtual Networking::Request *info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) = 0; /** Returns whether saves sync process is running. */ virtual bool isSyncing() = 0; diff --git a/backends/module.mk b/backends/module.mk index 281c6a9060..c40781d63a 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -40,7 +40,8 @@ MODULE_OBJS += \ networking/curl/connectionmanager.o \ networking/curl/networkreadstream.o \ networking/curl/curlrequest.o \ - networking/curl/curljsonrequest.o + networking/curl/curljsonrequest.o \ + networking/curl/request.o endif ifdef USE_ELF_LOADER diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp index fee0932129..df982bc814 100644 --- a/backends/networking/curl/curljsonrequest.cpp +++ b/backends/networking/curl/curljsonrequest.cpp @@ -31,8 +31,8 @@ namespace Networking { -CurlJsonRequest::CurlJsonRequest(JsonCallback cb, Common::String url): - CurlRequest(0, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {} +CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url): + CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES) {} CurlJsonRequest::~CurlJsonRequest() { delete _jsonCallback; } @@ -65,34 +65,32 @@ void CurlJsonRequest::handle() { if (_contentsStream.write(buf, readBytes) != readBytes) warning("MemoryWriteStreamDynamic was unable to write all the bytes"); - if (_stream->eos()) { - if (_stream->httpResponseCode() != 200) - warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode()); - + if (_stream->eos()) { char *contents = getPreparedContents(); - if (_stream->httpResponseCode() != 200) - debug("%s", contents); Common::JSONValue *json = Common::JSON::parse(contents); - finishJson(json); + if (json) { + finishSuccess(json); //it's JSON even if's not 200 OK? That's fine!.. + } else { + if (_stream->httpResponseCode() == 200) //no JSON, but 200 OK? That's fine!.. + finishSuccess(nullptr); + else + finishError(ErrorResponse(this, false, true, contents, _stream->httpResponseCode())); + } } } } void CurlJsonRequest::restart() { if (_stream) delete _stream; - _stream = 0; + _stream = nullptr; _contentsStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); //with no stream available next handle() will create another one } -void CurlJsonRequest::finishJson(Common::JSONValue *json) { - Request::finish(); +void CurlJsonRequest::finishSuccess(Common::JSONValue *json) { + Request::finishSuccess(); if (_jsonCallback) (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks! else delete json; } -void CurlJsonRequest::finish() { - finishJson(0); -} - } // End of namespace Networking diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h index 5e08be24c9..83005a79d5 100644 --- a/backends/networking/curl/curljsonrequest.h +++ b/backends/networking/curl/curljsonrequest.h @@ -41,15 +41,14 @@ protected: char *getPreparedContents(); /** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */ - virtual void finishJson(Common::JSONValue *json); + virtual void finishSuccess(Common::JSONValue *json); public: - CurlJsonRequest(JsonCallback cb, Common::String url); + CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url); virtual ~CurlJsonRequest(); virtual void handle(); virtual void restart(); - virtual void finish(); }; } // End of namespace Networking diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp index 64f6c26fb9..861546b906 100644 --- a/backends/networking/curl/curlrequest.cpp +++ b/backends/networking/curl/curlrequest.cpp @@ -30,8 +30,8 @@ namespace Networking { -CurlRequest::CurlRequest(DataCallback cb, Common::String url): - Request(cb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {} +CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url): + Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr), _bytesBufferSize(0) {} CurlRequest::~CurlRequest() { delete _stream; @@ -49,9 +49,14 @@ void CurlRequest::handle() { if (!_stream) _stream = makeStream(); if (_stream && _stream->eos()) { - if (_stream->httpResponseCode() != 200) + if (_stream->httpResponseCode() != 200) { warning("HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode()); - finish(); + ErrorResponse error(this, false, true, "", _stream->httpResponseCode()); + finishError(error); + return; + } + + finishSuccess(); //note that this Request doesn't call its callback on success (that's because it has nothing to return) } } @@ -101,4 +106,6 @@ NetworkReadStreamResponse CurlRequest::execute() { return NetworkReadStreamResponse(this, _stream); } +const NetworkReadStream *CurlRequest::getNetworkReadStream() const { return _stream; } + } // End of namespace Networking diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h index 461f153b9d..660479e34a 100644 --- a/backends/networking/curl/curlrequest.h +++ b/backends/networking/curl/curlrequest.h @@ -48,7 +48,7 @@ protected: virtual NetworkReadStream *makeStream(); public: - CurlRequest(DataCallback cb, Common::String url); + CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url); virtual ~CurlRequest(); virtual void handle(); @@ -73,7 +73,7 @@ public: virtual NetworkReadStreamResponse execute(); /** Returns Request's NetworkReadStream. */ - const NetworkReadStream *getNetworkReadStream() const { return _stream; } + const NetworkReadStream *getNetworkReadStream() const; }; } // End of namespace Networking diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp new file mode 100644 index 0000000000..d2f91586a0 --- /dev/null +++ b/backends/networking/curl/request.cpp @@ -0,0 +1,70 @@ +/* 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/networking/curl/request.h" + +namespace Networking { + +ErrorResponse::ErrorResponse(Request *rq): + request(rq), interrupted(false), failed(true), response(""), httpResponseCode(-1) {} + +ErrorResponse::ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode): + request(rq), interrupted(interrupt), failed(failure), response(resp), httpResponseCode(httpCode) {} + +Request::Request(DataCallback cb, ErrorCallback ecb): + _callback(cb), _errorCallback(ecb), _state(PROCESSING), _retryInSeconds(0) {} + +Request::~Request() { + delete _callback; + delete _errorCallback; +} + +void Request::handleRetry() { + if (_retryInSeconds > 0) --_retryInSeconds; + else { + _state = PROCESSING; + restart(); + } +} + +void Request::pause() { _state = PAUSED; } + +void Request::finish() { + ErrorResponse error(this, true, false, "", -1); + finishError(error); +} + +void Request::retry(uint32 seconds) { + _state = RETRY; + _retryInSeconds = seconds; +} + +RequestState Request::state() const { return _state; } + +void Request::finishError(ErrorResponse error) { + _state = FINISHED; + if (_errorCallback) (*_errorCallback)(error); +} + +void Request::finishSuccess() { _state = FINISHED; } + +} // End of namespace Networking diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h index a3c723d218..de5308fa52 100644 --- a/backends/networking/curl/request.h +++ b/backends/networking/curl/request.h @@ -25,6 +25,7 @@ #include "common/callback.h" #include "common/scummsys.h" +#include "common/str.h" namespace Networking { @@ -51,8 +52,39 @@ template<typename T> struct Response { Response(Request *rq, T v) : request(rq), value(v) {} }; +/** + * ErrorResponse is a struct to be returned from Request + * to user's failure callbacks. + * + * It keeps a Request pointer together with some useful + * information fields, which would explain why failure + * callback was called. + * + * <interrupted> flag is set when Request was interrupted, + * i.e. finished by user with finish() call. + * + * <failed> flag is set when Request has failed because of + * some error (bad server response, for example). + * + * <response> contains server's original response. + * + * <httpResponseCode> contains server's HTTP response code. + */ + +struct ErrorResponse { + Request *request; + bool interrupted; + bool failed; + Common::String response; + long httpResponseCode; + + ErrorResponse(Request *rq); + ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode); +}; + typedef Response<void *> DataReponse; typedef Common::BaseCallback<DataReponse> *DataCallback; +typedef Common::BaseCallback<ErrorResponse> *ErrorCallback; /** * RequestState is used to indicate current Request state. @@ -74,6 +106,9 @@ typedef Common::BaseCallback<DataReponse> *DataCallback; * After this state is set, but before ConnectionManager deletes the Request, * Request calls user's callback. User can ask Request to change its state * by calling retry() or pause() methods and Request won't be deleted. + * + * Request get a success and failure callbacks. Request must call one + * (and only one!) of these callbacks when it sets FINISHED state. */ enum RequestState { PROCESSING, @@ -94,6 +129,13 @@ protected: DataCallback _callback; /** + * Callback, which should be called when Request is failed/interrupted. + * That's the way Requests pass error information to the code which asked to create this request. + * @note callback must be called in finish() or similar method. + */ + ErrorCallback _errorCallback; + + /** * Request state, which is used by ConnectionManager to determine * whether request might be deleted or it's still working. * @@ -106,45 +148,42 @@ protected: /** In RETRY state this indicates whether it's time to call restart(). */ uint32 _retryInSeconds; + /** Sets FINISHED state and calls the _errorCallback with given error. */ + virtual void finishError(ErrorResponse error); + + /** Sets FINISHED state. Implementations might extend it if needed. */ + virtual void finishSuccess(); + public: - Request(DataCallback cb): _callback(cb), _state(PROCESSING), _retryInSeconds(0) {} - virtual ~Request() { delete _callback; } + Request(DataCallback cb, ErrorCallback ecb); + virtual ~Request(); /** Method, which does actual work. Depends on what this Request is doing. */ virtual void handle() = 0; /** Method, which is called by ConnectionManager when Request's state is RETRY. */ - virtual void handleRetry() { - if (_retryInSeconds > 0) --_retryInSeconds; - else { - _state = PROCESSING; - restart(); - } - } + virtual void handleRetry(); /** Method, which is used to restart the Request. */ virtual void restart() = 0; /** Method, which is called to pause the Request. */ - virtual void pause() { _state = PAUSED; } + virtual void pause(); /** * Method, which is called to *interrupt* the Request. * When it's called, Request must stop its work and - * call the callback to notify user of failure. + * call the failure callback to notify user. */ - virtual void finish() { _state = FINISHED; } + virtual void finish(); /** Method, which is called to retry the Request. */ - virtual void retry(uint32 seconds) { - _state = RETRY; - _retryInSeconds = seconds; - } + virtual void retry(uint32 seconds); /** Returns Request's current state. */ - RequestState state() const { return _state; } + RequestState state() const; }; -} // End of namespace Cloud +} // End of namespace Networking #endif diff --git a/common/cloudmanager.h b/common/cloudmanager.h index 350901e35f..51c98e7d0c 100644 --- a/common/cloudmanager.h +++ b/common/cloudmanager.h @@ -64,7 +64,7 @@ public: /** * Starts saves syncing process in currently active storage if there is any. */ - virtual void syncSaves(Cloud::Storage::BoolCallback callback = 0) = 0; + virtual void syncSaves(Cloud::Storage::BoolCallback callback = nullptr, Networking::ErrorCallback errorCallback = nullptr) = 0; }; } // End of namespace Common |