CLOUD: Refactor Request

Added ErrorResponse and ErrorCallback. Each Request now has an
ErrorCallback, which should be called instead of usual callback in case
of failure.
This commit is contained in:
Alexander Tkachev 2016-05-31 01:51:32 +06:00
parent 001b417f33
commit eb63b50b7f
29 changed files with 652 additions and 507 deletions

View file

@ -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;
}
_remoteFileStream = (Networking::NetworkReadStream *)pair.value;
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)
);
}
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
warning("DownloadRequest: can't restart as there are no means to reopen DumpFile");
finishError(Networking::ErrorResponse(this, false, true, "", -1));
//start();
}
void DownloadRequest::finish() {
finishBool(false);
}
void DownloadRequest::finishBool(bool success) {
Request::finish();
void DownloadRequest::finishSuccess(bool success) {
Request::finishSuccess();
if (_boolCallback) (*_boolCallback)(Storage::BoolResponse(this, success));
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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));

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
if (!pair.value) _failedFiles.push_back(_currentFile);
finishError(error);
}
void FolderDownloadRequest::fileDownloadedCallback(Storage::BoolResponse response) {
_workingRequest = nullptr;
if (_ignoreCallback) return;
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::finishFiles(Common::Array<StorageFile> &files) {
Request::finish();
void FolderDownloadRequest::restart() { start(); }
void FolderDownloadRequest::finishSuccess(Common::Array<StorageFile> &files) {
Request::finishSuccess();
if (_fileArrayCallback) (*_fileArrayCallback)(Storage::FileArrayResponse(this, files));
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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::Request *OneDriveStorage::syncSaves(BoolCallback callback) {
Networking::ErrorCallback OneDriveStorage::getErrorPrintingCallback() {
return new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::printErrorResponse);
}
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) {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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");
void SavesSyncRequest::finishBool(bool success) {
Request::finish();
//save updated timestamps (even if Request failed, there would be only valid timestamps)
saveTimestamps();
Request::finishError(error);
}
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 ' ')

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,
@ -93,6 +128,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

View file

@ -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