ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

savessyncrequest.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "backends/cloud/savessyncrequest.h"
00024 #include "backends/cloud/cloudmanager.h"
00025 #include "backends/networking/curl/curljsonrequest.h"
00026 #include "backends/saves/default/default-saves.h"
00027 #include "common/config-manager.h"
00028 #include "common/debug.h"
00029 #include "common/file.h"
00030 #include "common/json.h"
00031 #include "common/savefile.h"
00032 #include "common/system.h"
00033 #include "gui/saveload-dialog.h"
00034 
00035 namespace Cloud {
00036 
00037 SavesSyncRequest::SavesSyncRequest(Storage *storage, Storage::BoolCallback callback, Networking::ErrorCallback ecb):
00038     Request(nullptr, ecb), CommandSender(nullptr), _storage(storage), _boolCallback(callback),
00039     _workingRequest(nullptr), _ignoreCallback(false) {
00040     start();
00041 }
00042 
00043 SavesSyncRequest::~SavesSyncRequest() {
00044     _ignoreCallback = true;
00045     if (_workingRequest)
00046         _workingRequest->finish();
00047     delete _boolCallback;
00048 }
00049 
00050 void SavesSyncRequest::start() {
00051     //cleanup
00052     _ignoreCallback = true;
00053     if (_workingRequest)
00054         _workingRequest->finish();
00055     _currentDownloadingFile = StorageFile();
00056     _currentUploadingFile = "";
00057     _filesToDownload.clear();
00058     _filesToUpload.clear();
00059     _localFilesTimestamps.clear();
00060     _totalFilesToHandle = 0;
00061     _ignoreCallback = false;
00062 
00063     //load timestamps
00064     _localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
00065 
00066     //list saves directory
00067     Common::String dir = _storage->savesDirectoryPath();
00068     if (dir.lastChar() == '/')
00069         dir.deleteLastChar();
00070     _workingRequest = _storage->listDirectory(
00071         dir,
00072         new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
00073         new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
00074     );
00075     if (!_workingRequest) finishError(Networking::ErrorResponse(this));
00076 }
00077 
00078 void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
00079     _workingRequest = nullptr;
00080     if (_ignoreCallback)
00081         return;
00082 
00083     if (response.request) _date = response.request->date();
00084 
00085     Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
00086     for (Common::HashMap<Common::String, uint32>::iterator i = _localFilesTimestamps.begin(); i != _localFilesTimestamps.end(); ++i) {
00087         localFileNotAvailableInCloud[i->_key] = true;
00088     }
00089 
00090     //determine which files to download and which files to upload
00091     Common::Array<StorageFile> &remoteFiles = response.value;
00092     uint64 totalSize = 0;
00093     for (uint32 i = 0; i < remoteFiles.size(); ++i) {
00094         StorageFile &file = remoteFiles[i];
00095         if (file.isDirectory())
00096             continue;
00097         totalSize += file.size();
00098         if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
00099             continue;
00100 
00101         Common::String name = file.name();
00102         if (!_localFilesTimestamps.contains(name)) {
00103             _filesToDownload.push_back(file);
00104         } else {
00105             localFileNotAvailableInCloud[name] = false;
00106 
00107             if (_localFilesTimestamps[name] == file.timestamp())
00108                 continue;
00109 
00110             //we actually can have some files not only with timestamp < remote
00111             //but also with timestamp > remote (when we have been using ANOTHER CLOUD and then switched back)
00112             if (_localFilesTimestamps[name] > file.timestamp() || _localFilesTimestamps[name] == DefaultSaveFileManager::INVALID_TIMESTAMP)
00113                 _filesToUpload.push_back(file.name());
00114             else
00115                 _filesToDownload.push_back(file);
00116         }
00117     }
00118 
00119     CloudMan.setStorageUsedSpace(CloudMan.getStorageIndex(), totalSize);
00120 
00121     //upload files which are unavailable in cloud
00122     for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
00123         if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
00124             continue;
00125         if (i->_value)
00126             _filesToUpload.push_back(i->_key);
00127     }
00128 
00129     debug(9, "\nSavesSyncRequest: download files:");
00130     for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
00131         debug(9, "%s", _filesToDownload[i].name().c_str());
00132     }
00133     debug(9, "\nSavesSyncRequest: upload files:");
00134     for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
00135         debug(9, "%s", _filesToUpload[i].c_str());
00136     }
00137     _totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
00138 
00139     //start downloading files
00140     downloadNextFile();
00141 }
00142 
00143 void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
00144     _workingRequest = nullptr;
00145     if (_ignoreCallback)
00146         return;
00147 
00148     bool irrecoverable = error.interrupted || error.failed;
00149     if (error.failed) {
00150         Common::JSONValue *value = Common::JSON::parse(error.response.c_str());
00151         if (value) {
00152             if (value->isObject()) {
00153                 Common::JSONObject object = value->asObject();
00154 
00155                 //Dropbox-related error:
00156                 if (object.contains("error_summary") && object.getVal("error_summary")->isString()) {
00157                     Common::String summary = object.getVal("error_summary")->asString();
00158                     if (summary.contains("not_found")) {
00159                         irrecoverable = false;
00160                     }
00161                 }
00162 
00163                 //OneDrive-related error:
00164                 if (object.contains("error") && object.getVal("error")->isObject()) {
00165                     Common::JSONObject errorNode = object.getVal("error")->asObject();
00166                     if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
00167                         Common::String code = errorNode.getVal("code")->asString();
00168                         if (code == "itemNotFound") {
00169                             irrecoverable = false;
00170                         }
00171                     }
00172                 }
00173             }
00174             delete value;
00175         }
00176 
00177         //Google Drive and Box-related ScummVM-based error
00178         if (error.response.contains("subdirectory not found")) {
00179             irrecoverable = false; //base "/ScummVM/" folder not found
00180         } else if (error.response.contains("no such file found in its parent directory")) {
00181             irrecoverable = false; //"Saves" folder within "/ScummVM/" not found
00182         }
00183     }
00184 
00185     if (irrecoverable) {
00186         finishError(error);
00187         return;
00188     }
00189 
00190     //we're lucky - user just lacks his "/cloud/" folder - let's create one
00191     Common::String dir = _storage->savesDirectoryPath();
00192     if (dir.lastChar() == '/')
00193         dir.deleteLastChar();
00194     debug(9, "SavesSyncRequest: creating %s", dir.c_str());
00195     _workingRequest = _storage->createDirectory(
00196         dir,
00197         new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
00198         new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
00199     );
00200     if (!_workingRequest)
00201         finishError(Networking::ErrorResponse(this));
00202 }
00203 
00204 void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
00205     _workingRequest = nullptr;
00206     if (_ignoreCallback)
00207         return;
00208 
00209     //stop syncing if failed to create saves directory
00210     if (!response.value) {
00211         finishError(Networking::ErrorResponse(this, false, true, "", -1));
00212         return;
00213     }
00214 
00215     //continue with empty files list
00216     Common::Array<StorageFile> files;
00217     directoryListedCallback(Storage::ListDirectoryResponse(response.request, files));
00218 }
00219 
00220 void SavesSyncRequest::directoryCreatedErrorCallback(Networking::ErrorResponse error) {
00221     _workingRequest = nullptr;
00222     if (_ignoreCallback)
00223         return;
00224 
00225     //stop syncing if failed to create saves directory
00226     finishError(error);
00227 }
00228 
00229 void SavesSyncRequest::downloadNextFile() {
00230     if (_filesToDownload.empty()) {
00231         _currentDownloadingFile = StorageFile("", 0, 0, false); //so getFilesToDownload() would return an empty array
00232         sendCommand(GUI::kSavesSyncEndedCmd, 0);
00233         uploadNextFile();
00234         return;
00235     }
00236 
00237     _currentDownloadingFile = _filesToDownload.back();
00238     _filesToDownload.pop_back();
00239 
00240     sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
00241 
00242     debug(9, "SavesSyncRequest: downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
00243     _workingRequest = _storage->downloadById(
00244         _currentDownloadingFile.id(),
00245         DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
00246         new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::fileDownloadedCallback),
00247         new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
00248     );
00249     if (!_workingRequest)
00250         finishError(Networking::ErrorResponse(this));
00251 }
00252 
00253 void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
00254     _workingRequest = nullptr;
00255     if (_ignoreCallback)
00256         return;
00257 
00258     //stop syncing if download failed
00259     if (!response.value) {
00260         //delete the incomplete file
00261         g_system->getSavefileManager()->removeSavefile(_currentDownloadingFile.name());
00262         finishError(Networking::ErrorResponse(this, false, true, "", -1));
00263         return;
00264     }
00265 
00266     //update local timestamp for downloaded file
00267     _localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
00268     _localFilesTimestamps[_currentDownloadingFile.name()] = _currentDownloadingFile.timestamp();
00269     DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
00270 
00271     //continue downloading files
00272     downloadNextFile();
00273 }
00274 
00275 void SavesSyncRequest::fileDownloadedErrorCallback(Networking::ErrorResponse error) {
00276     _workingRequest = nullptr;
00277     if (_ignoreCallback)
00278         return;
00279 
00280     //stop syncing if download failed
00281     finishError(error);
00282 }
00283 
00284 void SavesSyncRequest::uploadNextFile() {
00285     if (_filesToUpload.empty()) {
00286         finishSync(true);
00287         return;
00288     }
00289 
00290     _currentUploadingFile = _filesToUpload.back();
00291     _filesToUpload.pop_back();
00292 
00293     debug(9, "SavesSyncRequest: uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
00294     if (_storage->uploadStreamSupported()) {
00295         _workingRequest = _storage->upload(
00296             _storage->savesDirectoryPath() + _currentUploadingFile,
00297             g_system->getSavefileManager()->openRawFile(_currentUploadingFile),
00298             new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
00299             new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
00300         );
00301     } else {
00302         _workingRequest = _storage->upload(
00303             _storage->savesDirectoryPath() + _currentUploadingFile,
00304             DefaultSaveFileManager::concatWithSavesPath(_currentUploadingFile),
00305             new Common::Callback<SavesSyncRequest, Storage::UploadResponse>(this, &SavesSyncRequest::fileUploadedCallback),
00306             new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
00307         );
00308     }
00309     if (!_workingRequest) finishError(Networking::ErrorResponse(this));
00310 }
00311 
00312 void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
00313     _workingRequest = nullptr;
00314     if (_ignoreCallback)
00315         return;
00316 
00317     //update local timestamp for the uploaded file
00318     _localFilesTimestamps = DefaultSaveFileManager::loadTimestamps();
00319     _localFilesTimestamps[_currentUploadingFile] = response.value.timestamp();
00320     DefaultSaveFileManager::saveTimestamps(_localFilesTimestamps);
00321 
00322     //continue uploading files
00323     uploadNextFile();
00324 }
00325 
00326 void SavesSyncRequest::fileUploadedErrorCallback(Networking::ErrorResponse error) {
00327     _workingRequest = nullptr;
00328     if (_ignoreCallback)
00329         return;
00330 
00331     //stop syncing if upload failed
00332     finishError(error);
00333 }
00334 
00335 void SavesSyncRequest::handle() {}
00336 
00337 void SavesSyncRequest::restart() { start(); }
00338 
00339 double SavesSyncRequest::getDownloadingProgress() const {
00340     if (_totalFilesToHandle == 0) {
00341         if (_state == Networking::FINISHED)
00342             return 1; //nothing to upload and download => Request ends soon
00343         return 0; //directory not listed yet
00344     }
00345 
00346     if (_totalFilesToHandle == _filesToUpload.size())
00347         return 1; //nothing to download => download complete
00348 
00349     uint32 totalFilesToDownload = _totalFilesToHandle - _filesToUpload.size();
00350     uint32 filesLeftToDownload = _filesToDownload.size() + (_currentDownloadingFile.name() != "" ? 1 : 0);
00351     return (double)(totalFilesToDownload - filesLeftToDownload) / (double)(totalFilesToDownload);
00352 }
00353 
00354 double SavesSyncRequest::getProgress() const {
00355     if (_totalFilesToHandle == 0) {
00356         if (_state == Networking::FINISHED)
00357             return 1; //nothing to upload and download => Request ends soon
00358         return 0; //directory not listed yet
00359     }
00360 
00361     return (double)(_totalFilesToHandle - _filesToDownload.size() - _filesToUpload.size()) / (double)(_totalFilesToHandle);
00362 }
00363 
00364 Common::Array<Common::String> SavesSyncRequest::getFilesToDownload() {
00365     Common::Array<Common::String> result;
00366     for (uint32 i = 0; i < _filesToDownload.size(); ++i)
00367         result.push_back(_filesToDownload[i].name());
00368     if (_currentDownloadingFile.name() != "")
00369         result.push_back(_currentDownloadingFile.name());
00370     return result;
00371 }
00372 
00373 void SavesSyncRequest::finishError(Networking::ErrorResponse error) {
00374     debug(9, "SavesSync::finishError");
00375     //if we were downloading a file - remember the name
00376     //and make the Request close() it, so we can delete it
00377     Common::String name = _currentDownloadingFile.name();
00378     if (_workingRequest) {
00379         _ignoreCallback = true;
00380         _workingRequest->finish();
00381         _workingRequest = nullptr;
00382         _ignoreCallback = false;
00383     }
00384     //unlock all the files by making getFilesToDownload() return empty array
00385     _currentDownloadingFile = StorageFile();
00386     _filesToDownload.clear();
00387     //delete the incomplete file
00388     if (name != "")
00389         g_system->getSavefileManager()->removeSavefile(name);
00390     Request::finishError(error);
00391 }
00392 
00393 void SavesSyncRequest::finishSync(bool success) {
00394     Request::finishSuccess();
00395 
00396     //update last successful sync date
00397     CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
00398 
00399     if (_boolCallback)
00400         (*_boolCallback)(Storage::BoolResponse(this, success));
00401 }
00402 
00403 } // End of namespace Cloud


Generated on Sat May 18 2019 05:01:19 for ResidualVM by doxygen 1.7.1
curved edge   curved edge