ALL: Sync with ScummVM rev. 55dba55056
This commit is contained in:
parent
aaec28e12f
commit
feaf9dc365
200 changed files with 13353 additions and 13638 deletions
2
Makefile
2
Makefile
|
@ -126,7 +126,7 @@ endif
|
|||
|
||||
.PHONY: print-dists print-executables print-version print-distversion
|
||||
print-dists:
|
||||
@echo $(DIST_FILES_DOCS) $(DIST_FILES_THEMES) $(DIST_FILES_NETWORKING) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_PLATFORM) $(srcdir)/doc
|
||||
@echo $(DIST_FILES_DOCS) $(DIST_FILES_THEMES) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_PLATFORM) $(srcdir)/doc
|
||||
|
||||
print-executables:
|
||||
@echo $(if $(DIST_EXECUTABLES),$(DIST_EXECUTABLES),$(EXECUTABLE) $(PLUGINS))
|
||||
|
|
|
@ -138,6 +138,10 @@ ifdef CXX_UPDATE_DEP_FLAG
|
|||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_AS)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(ASFLAGS) -c $(<) -o $*.o
|
||||
|
||||
base/version.o: base/version.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(VERFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
|
||||
|
||||
else
|
||||
|
||||
# Dumb compile rule, for C++ compilers that don't allow dependency tracking or
|
||||
|
@ -151,6 +155,9 @@ else
|
|||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_AS)$(CXX) $(ASFLAGS) -c $(<) -o $*.o
|
||||
|
||||
base/version.o: base/version.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_CXX)$(CXX) $(CXXFLAGS) $(VERFLAGS) $(CPPFLAGS) -c $(<) -o $*.o
|
||||
endif
|
||||
|
||||
# Build rule for assembler files
|
||||
|
@ -158,6 +165,12 @@ endif
|
|||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_AS)$(AS) $(ASFLAGS) $(<) -o $*.o
|
||||
|
||||
# Build rule for Windows resource files
|
||||
# TODO: Support dependency tracking
|
||||
%.o: %.rc
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_WINDRES)$(WINDRES) $(WINDRESFLAGS) $(CPPFLAGS) $(<) -o $*.o
|
||||
|
||||
ifdef USE_NASM
|
||||
# Build rule for NASM assembler files
|
||||
%.o: %.asm
|
||||
|
@ -188,7 +201,7 @@ VER_EXTRA = $(shell echo $(VERSION) | cut -d. -f 3 | cut -c2-)
|
|||
ifdef AMIGAOS
|
||||
# Amiga needs date in specific format for the version cookie
|
||||
AMIGA_DATE = $(shell gdate '+%d.%m.%Y')
|
||||
base/version.o: CXXFLAGS:=$(CXXFLAGS) -DAMIGA_DATE=\"$(AMIGA_DATE)\"
|
||||
VERFLAGS += -DAMIGA_DATE=\"$(AMIGA_DATE)\"
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
@ -211,7 +224,7 @@ endif
|
|||
# Define the Subversion revision if available, either autodetected or
|
||||
# specified by the user, but only for base/version.cpp.
|
||||
ifneq ($(origin VER_REV), undefined)
|
||||
base/version.o: CXXFLAGS:=$(CXXFLAGS) -DSCUMMVM_REVISION=\"$(VER_REV)\"
|
||||
VERFLAGS += -DSCUMMVM_REVISION=\"$(VER_REV)\"
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "gui/EventRecorder.h"
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/mixer_intern.h"
|
||||
|
@ -173,8 +172,7 @@ private:
|
|||
#pragma mark --- Mixer ---
|
||||
#pragma mark -
|
||||
|
||||
// TODO: parameter "system" is unused
|
||||
MixerImpl::MixerImpl(OSystem *system, uint sampleRate)
|
||||
MixerImpl::MixerImpl(uint sampleRate)
|
||||
: _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
|
||||
|
||||
assert(sampleRate > 0);
|
||||
|
|
|
@ -73,7 +73,7 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
MixerImpl(OSystem *system, uint sampleRate);
|
||||
MixerImpl(uint sampleRate);
|
||||
~MixerImpl();
|
||||
|
||||
virtual bool isReady() const { return _mixerReady; }
|
||||
|
|
|
@ -57,7 +57,7 @@ void BaseBackend::initBackend() {
|
|||
|
||||
void BaseBackend::fillScreen(uint32 col) {
|
||||
Graphics::Surface *screen = lockScreen();
|
||||
if (screen && screen->getPixels())
|
||||
memset(screen->getPixels(), col, screen->h * screen->pitch);
|
||||
if (screen)
|
||||
screen->fillRect(Common::Rect(screen->w, screen->h), col);
|
||||
unlockScreen();
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
|
|||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "BoxListDirectoryByIdRequest::responseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -35,142 +35,34 @@
|
|||
#include "common/debug.h"
|
||||
#include "common/json.h"
|
||||
|
||||
#ifdef ENABLE_RELEASE
|
||||
#include "dists/clouds/cloud_keys.h"
|
||||
#endif
|
||||
|
||||
namespace Cloud {
|
||||
namespace Box {
|
||||
|
||||
#define BOX_OAUTH2_TOKEN "https://api.box.com/oauth2/token"
|
||||
#define BOX_API_FOLDERS "https://api.box.com/2.0/folders"
|
||||
#define BOX_API_FILES_CONTENT "https://api.box.com/2.0/files/%s/content"
|
||||
#define BOX_API_USERS_ME "https://api.box.com/2.0/users/me"
|
||||
|
||||
char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
|
||||
char *BoxStorage::SECRET = nullptr;
|
||||
BoxStorage::BoxStorage(Common::String token, Common::String refreshToken, bool enabled):
|
||||
IdStorage(token, refreshToken, enabled) {}
|
||||
|
||||
void BoxStorage::loadKeyAndSecret() {
|
||||
#ifdef ENABLE_RELEASE
|
||||
KEY = RELEASE_BOX_KEY;
|
||||
SECRET = RELEASE_BOX_SECRET;
|
||||
#else
|
||||
Common::String k = ConfMan.get("BOX_KEY", ConfMan.kCloudDomain);
|
||||
KEY = new char[k.size() + 1];
|
||||
memcpy(KEY, k.c_str(), k.size());
|
||||
KEY[k.size()] = 0;
|
||||
|
||||
k = ConfMan.get("BOX_SECRET", ConfMan.kCloudDomain);
|
||||
SECRET = new char[k.size() + 1];
|
||||
memcpy(SECRET, k.c_str(), k.size());
|
||||
SECRET[k.size()] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
BoxStorage::BoxStorage(Common::String token, Common::String refreshToken):
|
||||
_token(token), _refreshToken(refreshToken) {}
|
||||
|
||||
BoxStorage::BoxStorage(Common::String code) {
|
||||
getAccessToken(
|
||||
new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
|
||||
new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
|
||||
code
|
||||
);
|
||||
BoxStorage::BoxStorage(Common::String code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
BoxStorage::~BoxStorage() {}
|
||||
|
||||
void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
|
||||
if (!KEY || !SECRET)
|
||||
loadKeyAndSecret();
|
||||
bool codeFlow = (code != "");
|
||||
Common::String BoxStorage::cloudProvider() { return "box"; }
|
||||
|
||||
if (!codeFlow && _refreshToken == "") {
|
||||
warning("BoxStorage: no refresh token available to get new access token.");
|
||||
if (callback) (*callback)(BoolResponse(nullptr, false));
|
||||
return;
|
||||
}
|
||||
uint32 BoxStorage::storageIndex() { return kStorageBoxId; }
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback);
|
||||
if (errorCallback == nullptr)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
bool BoxStorage::needsRefreshToken() { return true; }
|
||||
|
||||
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, BOX_OAUTH2_TOKEN);
|
||||
if (codeFlow) {
|
||||
request->addPostField("grant_type=authorization_code");
|
||||
request->addPostField("code=" + code);
|
||||
} else {
|
||||
request->addPostField("grant_type=refresh_token");
|
||||
request->addPostField("refresh_token=" + _refreshToken);
|
||||
}
|
||||
request->addPostField("client_id=" + Common::String(KEY));
|
||||
request->addPostField("client_secret=" + Common::String(SECRET));
|
||||
/*
|
||||
if (Cloud::CloudManager::couldUseLocalServer()) {
|
||||
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
|
||||
} else {
|
||||
request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
|
||||
}
|
||||
*/
|
||||
addRequest(request);
|
||||
}
|
||||
|
||||
void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
|
||||
Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("BoxStorage: got NULL instead of JSON");
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage")) {
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete json;
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "BoxStorage") ||
|
||||
!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "BoxStorage")) {
|
||||
warning("BoxStorage: bad response, no token passed");
|
||||
debug(9, "%s", json->stringify().c_str());
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
} else {
|
||||
_token = result.getVal("access_token")->asString();
|
||||
_refreshToken = result.getVal("refresh_token")->asString();
|
||||
CloudMan.save(); //ask CloudManager to save our new refreshToken
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, true));
|
||||
}
|
||||
delete json;
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void BoxStorage::codeFlowComplete(BoolResponse response) {
|
||||
if (!response.value) {
|
||||
warning("BoxStorage: failed to get access token through code flow");
|
||||
CloudMan.removeStorage(this);
|
||||
return;
|
||||
}
|
||||
|
||||
CloudMan.replaceStorage(this, kStorageBoxId);
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
void BoxStorage::codeFlowFailed(Networking::ErrorResponse error) {
|
||||
debug(9, "BoxStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
}
|
||||
bool BoxStorage::canReuseRefreshToken() { return false; }
|
||||
|
||||
void BoxStorage::saveConfig(Common::String keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String BoxStorage::name() const {
|
||||
|
@ -321,8 +213,6 @@ Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::
|
|||
Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; }
|
||||
|
||||
BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
|
||||
loadKeyAndSecret();
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("BoxStorage: no access_token found");
|
||||
return nullptr;
|
||||
|
@ -335,7 +225,13 @@ BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
|
|||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new BoxStorage(accessToken, refreshToken);
|
||||
return new BoxStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void BoxStorage::removeFromConfig(Common::String keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String BoxStorage::getRootDirectoryId() {
|
||||
|
|
|
@ -30,26 +30,32 @@ namespace Cloud {
|
|||
namespace Box {
|
||||
|
||||
class BoxStorage: public Id::IdStorage {
|
||||
static char *KEY, *SECRET;
|
||||
|
||||
static void loadKeyAndSecret();
|
||||
|
||||
Common::String _token, _refreshToken;
|
||||
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
BoxStorage(Common::String token, Common::String refreshToken);
|
||||
|
||||
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
|
||||
void codeFlowComplete(BoolResponse response);
|
||||
void codeFlowFailed(Networking::ErrorResponse error);
|
||||
BoxStorage(Common::String token, Common::String refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
|
||||
|
||||
void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "box"
|
||||
*/
|
||||
virtual Common::String cloudProvider();
|
||||
|
||||
/**
|
||||
* @return kStorageBoxId
|
||||
*/
|
||||
virtual uint32 storageIndex();
|
||||
|
||||
virtual bool needsRefreshToken();
|
||||
|
||||
virtual bool canReuseRefreshToken();
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
BoxStorage(Common::String code);
|
||||
BoxStorage(Common::String code, Networking::ErrorCallback cb);
|
||||
virtual ~BoxStorage();
|
||||
|
||||
/**
|
||||
|
@ -98,14 +104,12 @@ public:
|
|||
*/
|
||||
static BoxStorage *loadFromConfig(Common::String keyPrefix);
|
||||
|
||||
virtual Common::String getRootDirectoryId();
|
||||
|
||||
/**
|
||||
* Gets new access_token. If <code> passed is "", refresh_token is used.
|
||||
* Use "" in order to refresh token and pass a callback, so you could
|
||||
* continue your work when new token is available.
|
||||
* Remove all BoxStorage-related data from config.
|
||||
*/
|
||||
void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
|
||||
static void removeFromConfig(Common::String keyPrefix);
|
||||
|
||||
virtual Common::String getRootDirectoryId();
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ void BoxTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
|
|||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("BoxTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "BoxTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
void BoxTokenRefresher::finishError(Networking::ErrorResponse error) {
|
||||
if (error.httpResponseCode == 401) { // invalid_token
|
||||
pause();
|
||||
_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,12 +148,17 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
|
|||
}
|
||||
_activeStorage = storage;
|
||||
_currentStorageIndex = index;
|
||||
if (_storages[index].username == "") {
|
||||
// options' Cloud tab believes Storage is connected once it has non-empty username
|
||||
_storages[index].username = _("<syncing...>");
|
||||
_storages[index].lastSyncDate = _("<right now>");
|
||||
_storages[index].usedBytes = 0;
|
||||
}
|
||||
save();
|
||||
|
||||
//do what should be done on first Storage connect
|
||||
if (_activeStorage) {
|
||||
_activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
|
||||
_activeStorage->syncSaves(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,21 +255,21 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
|
|||
save();
|
||||
}
|
||||
|
||||
void CloudManager::connectStorage(uint32 index, Common::String code) {
|
||||
void CloudManager::connectStorage(uint32 index, Common::String code, Networking::ErrorCallback cb) {
|
||||
freeStorages();
|
||||
|
||||
switch (index) {
|
||||
case kStorageDropboxId:
|
||||
new Dropbox::DropboxStorage(code);
|
||||
new Dropbox::DropboxStorage(code, cb);
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
new OneDrive::OneDriveStorage(code);
|
||||
new OneDrive::OneDriveStorage(code, cb);
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
new GoogleDrive::GoogleDriveStorage(code);
|
||||
new GoogleDrive::GoogleDriveStorage(code, cb);
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
new Box::BoxStorage(code);
|
||||
new Box::BoxStorage(code, cb);
|
||||
break;
|
||||
}
|
||||
// in these constructors Storages request token using the passed code
|
||||
|
@ -273,6 +278,42 @@ void CloudManager::connectStorage(uint32 index, Common::String code) {
|
|||
// thus, no memory leak happens
|
||||
}
|
||||
|
||||
void CloudManager::disconnectStorage(uint32 index) {
|
||||
if (index >= kStorageTotal)
|
||||
error("CloudManager::disconnectStorage: invalid index passed");
|
||||
|
||||
Common::String name = getStorageConfigName(index);
|
||||
switch (index) {
|
||||
case kStorageDropboxId:
|
||||
Dropbox::DropboxStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageOneDriveId:
|
||||
OneDrive::OneDriveStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageGoogleDriveId:
|
||||
GoogleDrive::GoogleDriveStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
case kStorageBoxId:
|
||||
Box::BoxStorage::removeFromConfig(kStoragePrefix + name + "_");
|
||||
break;
|
||||
}
|
||||
|
||||
switchStorage(kStorageNoneId);
|
||||
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_username", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_lastSync", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(kStoragePrefix + name + "_usedBytes", ConfMan.kCloudDomain);
|
||||
|
||||
StorageConfig config;
|
||||
config.name = _(name);
|
||||
config.username = "";
|
||||
config.lastSyncDate = "";
|
||||
config.usedBytes = 0;
|
||||
|
||||
_storages[index] = config;
|
||||
}
|
||||
|
||||
|
||||
Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
|
@ -316,6 +357,28 @@ Common::String CloudManager::savesDirectoryPath() {
|
|||
return "";
|
||||
}
|
||||
|
||||
bool CloudManager::canSyncFilename(const Common::String &filename) const {
|
||||
if (filename == "" || filename[0] == '.')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloudManager::isStorageEnabled() const {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage)
|
||||
return storage->isEnabled();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CloudManager::enableStorage() {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
storage->enable();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
Storage *storage = getCurrentStorage();
|
||||
if (storage) {
|
||||
|
@ -336,14 +399,6 @@ bool CloudManager::isWorking() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CloudManager::couldUseLocalServer() {
|
||||
#ifdef USE_SDL_NET
|
||||
return Networking::LocalWebserver::getPort() == Networking::LocalWebserver::DEFAULT_SERVER_PORT;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
bool CloudManager::isSyncing() const {
|
||||
|
|
|
@ -204,8 +204,16 @@ public:
|
|||
*
|
||||
* @param index Storage's index
|
||||
* @param code OAuth2 code received from user
|
||||
* @param cb callback to notify of success or error
|
||||
*/
|
||||
void connectStorage(uint32 index, Common::String code);
|
||||
void connectStorage(uint32 index, Common::String code, Networking::ErrorCallback cb = nullptr);
|
||||
|
||||
/**
|
||||
* Remove Storage with a given index from config.
|
||||
*
|
||||
* @param index Storage's index
|
||||
*/
|
||||
void disconnectStorage(uint32 index);
|
||||
|
||||
/** Returns ListDirectoryResponse with list of files. */
|
||||
Networking::Request *listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false);
|
||||
|
@ -219,6 +227,15 @@ public:
|
|||
/** Returns storage's saves directory path with the trailing slash. */
|
||||
Common::String savesDirectoryPath();
|
||||
|
||||
/** Returns whether given filename could be uploaded to or downloaded from storage. */
|
||||
bool canSyncFilename(const Common::String &filename) const;
|
||||
|
||||
/** Returns whether current Storage is manually enabled by user (or false, if there is no active Storage). */
|
||||
bool isStorageEnabled() const;
|
||||
|
||||
/** Sets Storage::_isEnabled to true and updates the config. */
|
||||
void enableStorage();
|
||||
|
||||
/**
|
||||
* Starts saves syncing process in currently active storage if there is any.
|
||||
*/
|
||||
|
@ -227,9 +244,6 @@ public:
|
|||
/** Returns whether there are any requests running. */
|
||||
bool isWorking() const;
|
||||
|
||||
/** Returns whether LocalWebserver is available to use for auth. */
|
||||
static bool couldUseLocalServer();
|
||||
|
||||
///// SavesSyncRequest-related /////
|
||||
|
||||
/** Returns whether there is a SavesSyncRequest running. */
|
||||
|
|
|
@ -73,13 +73,13 @@ void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) {
|
|||
void DownloadRequest::handle() {
|
||||
if (!_localFile) {
|
||||
warning("DownloadRequest: no file to write");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: no file to write into", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_localFile->isOpen()) {
|
||||
warning("DownloadRequest: failed to open file to write");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: failed to open file to write", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ void DownloadRequest::handle() {
|
|||
if (readBytes != 0)
|
||||
if (_localFile->write(_buffer, readBytes) != readBytes) {
|
||||
warning("DownloadRequest: unable to write all received bytes into output file");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::handle: failed to write all bytes into a file", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ void DownloadRequest::handle() {
|
|||
|
||||
void DownloadRequest::restart() {
|
||||
warning("DownloadRequest: can't restart as there are no means to reopen DumpFile");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DownloadRequest::restart: can't restart as there are no means to reopen DumpFile", -1));
|
||||
//start();
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
|
|||
}
|
||||
if (response.request) _date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "DropboxCreateDirectoryRequest::responseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -71,7 +71,7 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
|
|||
return;
|
||||
}
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "DropboxInfoRequest::userResponseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
@ -125,7 +125,7 @@ void DropboxInfoRequest::quotaResponseCallback(Networking::JsonResponse response
|
|||
return;
|
||||
}
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "DropboxInfoRequest::quotaResponseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -84,7 +84,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
|
|||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "DropboxListDirectoryRequest::responseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -35,103 +35,30 @@
|
|||
#include "common/debug.h"
|
||||
#include "common/json.h"
|
||||
|
||||
#ifdef ENABLE_RELEASE
|
||||
#include "dists/clouds/cloud_keys.h"
|
||||
#endif
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
#define DROPBOX_OAUTH2_TOKEN "https://api.dropboxapi.com/oauth2/token"
|
||||
#define DROPBOX_API_FILES_DOWNLOAD "https://content.dropboxapi.com/2/files/download"
|
||||
|
||||
char *DropboxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
|
||||
char *DropboxStorage::SECRET = nullptr;
|
||||
DropboxStorage::DropboxStorage(Common::String accessToken, bool enabled): BaseStorage(accessToken, "", enabled) {}
|
||||
|
||||
void DropboxStorage::loadKeyAndSecret() {
|
||||
#ifdef ENABLE_RELEASE
|
||||
KEY = RELEASE_DROPBOX_KEY;
|
||||
SECRET = RELEASE_DROPBOX_SECRET;
|
||||
#else
|
||||
Common::String k = ConfMan.get("DROPBOX_KEY", ConfMan.kCloudDomain);
|
||||
KEY = new char[k.size() + 1];
|
||||
memcpy(KEY, k.c_str(), k.size());
|
||||
KEY[k.size()] = 0;
|
||||
|
||||
k = ConfMan.get("DROPBOX_SECRET", ConfMan.kCloudDomain);
|
||||
SECRET = new char[k.size() + 1];
|
||||
memcpy(SECRET, k.c_str(), k.size());
|
||||
SECRET[k.size()] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
DropboxStorage::DropboxStorage(Common::String accessToken, Common::String userId): _token(accessToken), _uid(userId) {}
|
||||
|
||||
DropboxStorage::DropboxStorage(Common::String code) {
|
||||
getAccessToken(code);
|
||||
DropboxStorage::DropboxStorage(Common::String code, Networking::ErrorCallback cb): BaseStorage() {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
DropboxStorage::~DropboxStorage() {}
|
||||
|
||||
void DropboxStorage::getAccessToken(Common::String code) {
|
||||
if (!KEY || !SECRET)
|
||||
loadKeyAndSecret();
|
||||
Networking::JsonCallback callback = new Common::Callback<DropboxStorage, Networking::JsonResponse>(this, &DropboxStorage::codeFlowComplete);
|
||||
Networking::ErrorCallback errorCallback = new Common::Callback<DropboxStorage, Networking::ErrorResponse>(this, &DropboxStorage::codeFlowFailed);
|
||||
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(callback, errorCallback, DROPBOX_OAUTH2_TOKEN);
|
||||
request->addPostField("code=" + code);
|
||||
request->addPostField("grant_type=authorization_code");
|
||||
request->addPostField("client_id=" + Common::String(KEY));
|
||||
request->addPostField("client_secret=" + Common::String(SECRET));
|
||||
if (Cloud::CloudManager::couldUseLocalServer()) {
|
||||
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
|
||||
} else {
|
||||
request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
|
||||
}
|
||||
addRequest(request);
|
||||
}
|
||||
Common::String DropboxStorage::cloudProvider() { return "dropbox"; }
|
||||
|
||||
void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
|
||||
Common::JSONValue *json = (Common::JSONValue *)response.value;
|
||||
if (json == nullptr) {
|
||||
debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
|
||||
CloudMan.removeStorage(this);
|
||||
return;
|
||||
}
|
||||
uint32 DropboxStorage::storageIndex() { return kStorageDropboxId; }
|
||||
|
||||
if (!json->isObject()) {
|
||||
debug(9, "DropboxStorage::codeFlowComplete: Passed JSON is not an object!");
|
||||
CloudMan.removeStorage(this);
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
bool DropboxStorage::needsRefreshToken() { return false; }
|
||||
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "DropboxStorage::codeFlowComplete") ||
|
||||
!Networking::CurlJsonRequest::jsonContainsString(result, "uid", "DropboxStorage::codeFlowComplete")) {
|
||||
warning("DropboxStorage: bad response, no token/uid passed");
|
||||
debug(9, "%s", json->stringify(true).c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
} else {
|
||||
_token = result.getVal("access_token")->asString();
|
||||
_uid = result.getVal("uid")->asString();
|
||||
ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
|
||||
CloudMan.replaceStorage(this, kStorageDropboxId);
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
delete json;
|
||||
}
|
||||
|
||||
void DropboxStorage::codeFlowFailed(Networking::ErrorResponse error) {
|
||||
debug(9, "DropboxStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
}
|
||||
bool DropboxStorage::canReuseRefreshToken() { return false; }
|
||||
|
||||
void DropboxStorage::saveConfig(Common::String keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String DropboxStorage::name() const {
|
||||
|
@ -177,22 +104,18 @@ Networking::Request *DropboxStorage::info(StorageInfoCallback callback, Networki
|
|||
Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; }
|
||||
|
||||
DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
|
||||
loadKeyAndSecret();
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("DropboxStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
|
||||
warning("DropboxStorage: no user_id found");
|
||||
return nullptr;
|
||||
}
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
return new DropboxStorage(accessToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
|
||||
|
||||
return new DropboxStorage(accessToken, userId);
|
||||
void DropboxStorage::removeFromConfig(Common::String keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
} // End of namespace Dropbox
|
||||
|
|
|
@ -23,30 +23,35 @@
|
|||
#ifndef BACKENDS_CLOUD_DROPBOX_STORAGE_H
|
||||
#define BACKENDS_CLOUD_DROPBOX_STORAGE_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "common/callback.h"
|
||||
#include "backends/networking/curl/curljsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace Dropbox {
|
||||
|
||||
class DropboxStorage: public Cloud::Storage {
|
||||
static char *KEY, *SECRET;
|
||||
|
||||
static void loadKeyAndSecret();
|
||||
|
||||
Common::String _token, _uid;
|
||||
|
||||
class DropboxStorage: public Cloud::BaseStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
DropboxStorage(Common::String token, Common::String uid);
|
||||
DropboxStorage(Common::String token, bool enabled);
|
||||
|
||||
void getAccessToken(Common::String code);
|
||||
void codeFlowComplete(Networking::JsonResponse response);
|
||||
void codeFlowFailed(Networking::ErrorResponse error);
|
||||
protected:
|
||||
/**
|
||||
* @return "dropbox"
|
||||
*/
|
||||
virtual Common::String cloudProvider();
|
||||
|
||||
/**
|
||||
* @return kStorageDropboxId
|
||||
*/
|
||||
virtual uint32 storageIndex();
|
||||
|
||||
virtual bool needsRefreshToken();
|
||||
|
||||
virtual bool canReuseRefreshToken();
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
DropboxStorage(Common::String code);
|
||||
DropboxStorage(Common::String code, Networking::ErrorCallback cb);
|
||||
virtual ~DropboxStorage();
|
||||
|
||||
/**
|
||||
|
@ -93,6 +98,11 @@ public:
|
|||
* @return pointer to the newly created DropboxStorage or 0 if some problem occured.
|
||||
*/
|
||||
static DropboxStorage *loadFromConfig(Common::String keyPrefix);
|
||||
|
||||
/**
|
||||
* Remove all DropboxStorage-related data from config.
|
||||
*/
|
||||
static void removeFromConfig(Common::String keyPrefix);
|
||||
};
|
||||
|
||||
} // End of namespace Dropbox
|
||||
|
|
|
@ -54,12 +54,12 @@ void DropboxUploadRequest::start() {
|
|||
_workingRequest->finish();
|
||||
if (!_contentsStream) {
|
||||
warning("DropboxUploadRequest: cannot start because stream is invalid");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DropboxUploadRequest::start: cannot start because stream is invalid", -1));
|
||||
return;
|
||||
}
|
||||
if (!_contentsStream->seek(0)) {
|
||||
warning("DropboxUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "DropboxUploadRequest::start: cannot restart because stream couldn't seek(0)", -1));
|
||||
return;
|
||||
}
|
||||
_ignoreCallback = false;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "common/debug.h"
|
||||
#include "gui/downloaddialog.h"
|
||||
#include <backends/networking/curl/connectionmanager.h>
|
||||
#include "cloudmanager.h"
|
||||
|
||||
namespace Cloud {
|
||||
|
||||
|
@ -73,8 +74,9 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
|
|||
|
||||
// remove all directories
|
||||
// non-empty directories would be created by DumpFile, and empty ones are just ignored
|
||||
// also skip all hidden files (with names starting with '.') or with other names that are forbidden to sync in CloudManager
|
||||
for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end();)
|
||||
if (i->isDirectory())
|
||||
if (i->isDirectory() || !CloudMan.canSyncFilename(i->name()))
|
||||
_pendingFiles.erase(i);
|
||||
else {
|
||||
_totalBytes += i->size();
|
||||
|
|
|
@ -81,7 +81,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
|
|||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "GoogleDriveListDirectoryByIdRequest::responseCallback");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -36,142 +36,34 @@
|
|||
#include "common/json.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#ifdef ENABLE_RELEASE
|
||||
#include "dists/clouds/cloud_keys.h"
|
||||
#endif
|
||||
|
||||
namespace Cloud {
|
||||
namespace GoogleDrive {
|
||||
|
||||
#define GOOGLEDRIVE_OAUTH2_TOKEN "https://accounts.google.com/o/oauth2/token"
|
||||
#define GOOGLEDRIVE_API_FILES_ALT_MEDIA "https://www.googleapis.com/drive/v3/files/%s?alt=media"
|
||||
#define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/drive/v3/files"
|
||||
#define GOOGLEDRIVE_API_ABOUT "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user"
|
||||
|
||||
char *GoogleDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
|
||||
char *GoogleDriveStorage::SECRET = nullptr;
|
||||
GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken, bool enabled):
|
||||
IdStorage(token, refreshToken, enabled) {}
|
||||
|
||||
void GoogleDriveStorage::loadKeyAndSecret() {
|
||||
#ifdef ENABLE_RELEASE
|
||||
KEY = RELEASE_GOOGLE_DRIVE_KEY;
|
||||
SECRET = RELEASE_GOOGLE_DRIVE_SECRET;
|
||||
#else
|
||||
Common::String k = ConfMan.get("GOOGLE_DRIVE_KEY", ConfMan.kCloudDomain);
|
||||
KEY = new char[k.size() + 1];
|
||||
memcpy(KEY, k.c_str(), k.size());
|
||||
KEY[k.size()] = 0;
|
||||
|
||||
k = ConfMan.get("GOOGLE_DRIVE_SECRET", ConfMan.kCloudDomain);
|
||||
SECRET = new char[k.size() + 1];
|
||||
memcpy(SECRET, k.c_str(), k.size());
|
||||
SECRET[k.size()] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken):
|
||||
_token(token), _refreshToken(refreshToken) {}
|
||||
|
||||
GoogleDriveStorage::GoogleDriveStorage(Common::String code) {
|
||||
getAccessToken(
|
||||
new Common::Callback<GoogleDriveStorage, BoolResponse>(this, &GoogleDriveStorage::codeFlowComplete),
|
||||
new Common::Callback<GoogleDriveStorage, Networking::ErrorResponse>(this, &GoogleDriveStorage::codeFlowFailed),
|
||||
code
|
||||
);
|
||||
GoogleDriveStorage::GoogleDriveStorage(Common::String code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
GoogleDriveStorage::~GoogleDriveStorage() {}
|
||||
|
||||
void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
|
||||
if (!KEY || !SECRET) loadKeyAndSecret();
|
||||
bool codeFlow = (code != "");
|
||||
Common::String GoogleDriveStorage::cloudProvider() { return "gdrive"; }
|
||||
|
||||
if (!codeFlow && _refreshToken == "") {
|
||||
warning("GoogleDriveStorage: no refresh token available to get new access token.");
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
return;
|
||||
}
|
||||
uint32 GoogleDriveStorage::storageIndex() { return kStorageGoogleDriveId; }
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback);
|
||||
if (errorCallback == nullptr)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, GOOGLEDRIVE_OAUTH2_TOKEN);
|
||||
if (codeFlow) {
|
||||
request->addPostField("code=" + code);
|
||||
request->addPostField("grant_type=authorization_code");
|
||||
} else {
|
||||
request->addPostField("refresh_token=" + _refreshToken);
|
||||
request->addPostField("grant_type=refresh_token");
|
||||
}
|
||||
request->addPostField("client_id=" + Common::String(KEY));
|
||||
request->addPostField("client_secret=" + Common::String(SECRET));
|
||||
if (Cloud::CloudManager::couldUseLocalServer()) {
|
||||
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
|
||||
} else {
|
||||
request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
|
||||
}
|
||||
addRequest(request);
|
||||
}
|
||||
bool GoogleDriveStorage::needsRefreshToken() { return true; }
|
||||
|
||||
void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
|
||||
Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("GoogleDriveStorage: got NULL instead of JSON");
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage")) {
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete json;
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "GoogleDriveStorage")) {
|
||||
warning("GoogleDriveStorage: bad response, no token passed");
|
||||
debug(9, "%s", json->stringify().c_str());
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
} else {
|
||||
_token = result.getVal("access_token")->asString();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "GoogleDriveStorage"))
|
||||
warning("GoogleDriveStorage: no refresh_token passed");
|
||||
else
|
||||
_refreshToken = result.getVal("refresh_token")->asString();
|
||||
CloudMan.save(); //ask CloudManager to save our new refreshToken
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, true));
|
||||
}
|
||||
delete json;
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::codeFlowComplete(BoolResponse response) {
|
||||
if (!response.value) {
|
||||
warning("GoogleDriveStorage: failed to get access token through code flow");
|
||||
CloudMan.removeStorage(this);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfMan.removeKey("googledrive_code", ConfMan.kCloudDomain);
|
||||
CloudMan.replaceStorage(this, kStorageGoogleDriveId);
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
|
||||
debug(9, "GoogleDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
}
|
||||
bool GoogleDriveStorage::canReuseRefreshToken() { return true; }
|
||||
|
||||
void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String GoogleDriveStorage::name() const {
|
||||
|
@ -325,8 +217,6 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
|
|||
Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; }
|
||||
|
||||
GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
|
||||
loadKeyAndSecret();
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("GoogleDriveStorage: no access_token found");
|
||||
return nullptr;
|
||||
|
@ -339,7 +229,13 @@ GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix)
|
|||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new GoogleDriveStorage(accessToken, refreshToken);
|
||||
return new GoogleDriveStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void GoogleDriveStorage::removeFromConfig(Common::String keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String GoogleDriveStorage::getRootDirectoryId() {
|
||||
|
|
|
@ -30,18 +30,8 @@ namespace Cloud {
|
|||
namespace GoogleDrive {
|
||||
|
||||
class GoogleDriveStorage: public Id::IdStorage {
|
||||
static char *KEY, *SECRET;
|
||||
|
||||
static void loadKeyAndSecret();
|
||||
|
||||
Common::String _token, _refreshToken;
|
||||
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
GoogleDriveStorage(Common::String token, Common::String refreshToken);
|
||||
|
||||
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
|
||||
void codeFlowComplete(BoolResponse response);
|
||||
void codeFlowFailed(Networking::ErrorResponse error);
|
||||
GoogleDriveStorage(Common::String token, Common::String refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
|
||||
|
@ -50,9 +40,25 @@ class GoogleDriveStorage: public Id::IdStorage {
|
|||
void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json);
|
||||
|
||||
void printInfo(StorageInfoResponse response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "gdrive"
|
||||
*/
|
||||
virtual Common::String cloudProvider();
|
||||
|
||||
/**
|
||||
* @return kStorageGoogleDriveId
|
||||
*/
|
||||
virtual uint32 storageIndex();
|
||||
|
||||
virtual bool needsRefreshToken();
|
||||
|
||||
virtual bool canReuseRefreshToken();
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
GoogleDriveStorage(Common::String code);
|
||||
GoogleDriveStorage(Common::String code, Networking::ErrorCallback cb);
|
||||
virtual ~GoogleDriveStorage();
|
||||
|
||||
/**
|
||||
|
@ -100,14 +106,12 @@ public:
|
|||
*/
|
||||
static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix);
|
||||
|
||||
virtual Common::String getRootDirectoryId();
|
||||
|
||||
/**
|
||||
* Gets new access_token. If <code> passed is "", refresh_token is used.
|
||||
* Use "" in order to refresh token and pass a callback, so you could
|
||||
* continue your work when new token is available.
|
||||
* Remove all GoogleDriveStorage-related data from config.
|
||||
*/
|
||||
void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
|
||||
static void removeFromConfig(Common::String keyPrefix);
|
||||
|
||||
virtual Common::String getRootDirectoryId();
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ void GoogleDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
|
|||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("GoogleDriveTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
Common::JSONObject error = result.getVal("error")->asObject();
|
||||
bool irrecoverable = true;
|
||||
|
||||
uint32 code = -1;
|
||||
uint32 code = 0xFFFFFFFF; // Invalid
|
||||
Common::String message;
|
||||
if (jsonContainsIntegerNumber(error, "code", "GoogleDriveTokenRefresher")) {
|
||||
code = error.getVal("code")->asIntegerNumber();
|
||||
|
@ -100,7 +100,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->getAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<GoogleDriveTokenRefresher, Storage::BoolResponse>(this, &GoogleDriveTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ void GoogleDriveUploadRequest::start() {
|
|||
_workingRequest->finish();
|
||||
if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
|
||||
warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveUploadRequest::start: couldn't restart because failed to seek(0)", -1));
|
||||
return;
|
||||
}
|
||||
_resolvedId = ""; //used to update file contents
|
||||
|
@ -146,33 +146,31 @@ void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse resp
|
|||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
Networking::ErrorResponse error(this, false, true, "", -1);
|
||||
Networking::ErrorResponse error(this, false, true, "GoogleDriveUploadRequest::startUploadCallback", -1);
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq) {
|
||||
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
|
||||
if (stream) {
|
||||
long code = stream->httpResponseCode();
|
||||
Common::String headers = stream->responseHeaders();
|
||||
if (code == 200) {
|
||||
const char *cstr = headers.c_str();
|
||||
const char *position = strstr(cstr, "Location: ");
|
||||
|
||||
if (position) {
|
||||
Common::String result = "";
|
||||
char c;
|
||||
for (const char *i = position + 10; c = *i, c != 0; ++i) {
|
||||
if (c == '\n' || c == '\r')
|
||||
break;
|
||||
result += c;
|
||||
}
|
||||
_uploadUrl = result;
|
||||
Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
|
||||
if (headers.contains("location")) {
|
||||
_uploadUrl = headers["location"];
|
||||
uploadNextPart();
|
||||
return;
|
||||
} else {
|
||||
error.response += ": response must provide Location header, but it's not there";
|
||||
}
|
||||
} else {
|
||||
error.response += ": response is not 200 OK";
|
||||
}
|
||||
|
||||
error.httpResponseCode = code;
|
||||
} else {
|
||||
error.response += ": missing response stream [improbable]";
|
||||
}
|
||||
} else {
|
||||
error.response += ": missing request object [improbable]";
|
||||
}
|
||||
|
||||
Common::JSONValue *json = response.value;
|
||||
|
@ -202,7 +200,7 @@ void GoogleDriveUploadRequest::uploadNextPart() {
|
|||
if (oldPos != _serverReceivedBytes) {
|
||||
if (!_contentsStream->seek(_serverReceivedBytes)) {
|
||||
warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%lu)", _serverReceivedBytes);
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "GoogleDriveUploadRequest::uploadNextPart: seek() didn't work", -1));
|
||||
return;
|
||||
}
|
||||
oldPos = _serverReceivedBytes;
|
||||
|
@ -230,25 +228,19 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
|
|||
if (stream->httpResponseCode() != 308)
|
||||
return false; //seriously
|
||||
|
||||
Common::String headers = stream->responseHeaders();
|
||||
const char *cstr = headers.c_str();
|
||||
for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
|
||||
const char *needle = (rangeTry == 0 ? "Range: 0-" : "Range: bytes=0-");
|
||||
uint32 needleLength = (rangeTry == 0 ? 9 : 15);
|
||||
Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
|
||||
if (headers.contains("range")) {
|
||||
Common::String range = headers["range"];
|
||||
for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
|
||||
const char *needle = (rangeTry == 0 ? "0-" : "bytes=0-"); //if it lost the first part, I refuse to talk with it
|
||||
uint32 needleLength = (rangeTry == 0 ? 2 : 8);
|
||||
|
||||
const char *position = strstr(cstr, needle); //if it lost the first part, I refuse to talk with it
|
||||
|
||||
if (position) {
|
||||
Common::String result = "";
|
||||
char c;
|
||||
for (const char *i = position + needleLength; c = *i, c != 0; ++i) {
|
||||
if (c == '\n' || c == '\r')
|
||||
break;
|
||||
result += c;
|
||||
if (range.hasPrefix(needle)) {
|
||||
range.erase(0, needleLength);
|
||||
_serverReceivedBytes = range.asUint64() + 1;
|
||||
uploadNextPart();
|
||||
return true;
|
||||
}
|
||||
_serverReceivedBytes = result.asUint64() + 1;
|
||||
uploadNextPart();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
IdStorage::IdStorage() {}
|
||||
|
||||
IdStorage::IdStorage(Common::String token, Common::String refreshToken, bool enabled):
|
||||
BaseStorage(token, refreshToken, enabled) {}
|
||||
|
||||
IdStorage::~IdStorage() {}
|
||||
|
||||
void IdStorage::printFiles(FileArrayResponse response) {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#ifndef BACKENDS_CLOUD_ID_IDSTORAGE_H
|
||||
#define BACKENDS_CLOUD_ID_IDSTORAGE_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "backends/networking/curl/curljsonrequest.h"
|
||||
|
||||
/*
|
||||
|
@ -43,7 +43,7 @@
|
|||
namespace Cloud {
|
||||
namespace Id {
|
||||
|
||||
class IdStorage: public Cloud::Storage {
|
||||
class IdStorage: public Cloud::BaseStorage {
|
||||
protected:
|
||||
void printFiles(FileArrayResponse response);
|
||||
void printBool(BoolResponse response);
|
||||
|
@ -52,6 +52,8 @@ protected:
|
|||
ListDirectoryCallback getPrintFilesCallback();
|
||||
|
||||
public:
|
||||
IdStorage();
|
||||
IdStorage(Common::String token, Common::String refreshToken, bool enabled);
|
||||
virtual ~IdStorage();
|
||||
|
||||
/** Public Cloud API comes down there. */
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://graph.microsoft.com/v1.0/drive/special/approot"
|
||||
|
||||
OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
|
||||
|
@ -96,7 +96,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
|
|||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "OneDriveCreateDirectoryRequest::responseCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/children"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/children"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN_ROOT_ITSELF "https://graph.microsoft.com/v1.0/drive/special/approot/children"
|
||||
|
||||
OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
|
||||
Networking::Request(nullptr, ecb),
|
||||
|
@ -77,6 +78,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
|
|||
Common::String dir = _currentDirectory;
|
||||
dir.deleteLastChar();
|
||||
Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, ConnMan.urlEncode(dir).c_str());
|
||||
if (dir == "") url = Common::String(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN_ROOT_ITSELF);
|
||||
makeRequest(url);
|
||||
}
|
||||
|
||||
|
@ -84,7 +86,7 @@ void OneDriveListDirectoryRequest::makeRequest(Common::String url) {
|
|||
Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
|
||||
Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
|
||||
Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _storage->accessToken());
|
||||
request->addHeader("Authorization: bearer " + _storage->accessToken());
|
||||
_workingRequest = ConnMan.addRequest(request);
|
||||
}
|
||||
|
||||
|
@ -100,7 +102,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
|
|||
if (response.request)
|
||||
_date = response.request->date();
|
||||
|
||||
Networking::ErrorResponse error(this);
|
||||
Networking::ErrorResponse error(this, "OneDriveListDirectoryRequest::listedDirectoryCallback: unknown error");
|
||||
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
|
||||
if (rq && rq->getNetworkReadStream())
|
||||
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
|
||||
|
|
|
@ -36,143 +36,33 @@
|
|||
#include "common/debug.h"
|
||||
#include "common/json.h"
|
||||
|
||||
#ifdef ENABLE_RELEASE
|
||||
#include "dists/clouds/cloud_keys.h"
|
||||
#endif
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_OAUTH2_TOKEN "https://login.live.com/oauth20_token.srf"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://api.onedrive.com/v1.0/drive/special/approot:/"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://graph.microsoft.com/v1.0/drive/special/approot:/"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT "https://graph.microsoft.com/v1.0/drive/special/approot"
|
||||
|
||||
char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
|
||||
char *OneDriveStorage::SECRET = nullptr;
|
||||
OneDriveStorage::OneDriveStorage(Common::String token, Common::String refreshToken, bool enabled):
|
||||
BaseStorage(token, refreshToken, enabled) {}
|
||||
|
||||
void OneDriveStorage::loadKeyAndSecret() {
|
||||
#ifdef ENABLE_RELEASE
|
||||
KEY = RELEASE_ONEDRIVE_KEY;
|
||||
SECRET = RELEASE_ONEDRIVE_SECRET;
|
||||
#else
|
||||
Common::String k = ConfMan.get("ONEDRIVE_KEY", ConfMan.kCloudDomain);
|
||||
KEY = new char[k.size() + 1];
|
||||
memcpy(KEY, k.c_str(), k.size());
|
||||
KEY[k.size()] = 0;
|
||||
|
||||
k = ConfMan.get("ONEDRIVE_SECRET", ConfMan.kCloudDomain);
|
||||
SECRET = new char[k.size() + 1];
|
||||
memcpy(SECRET, k.c_str(), k.size());
|
||||
SECRET[k.size()] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
OneDriveStorage::OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken):
|
||||
_token(token), _uid(uid), _refreshToken(refreshToken) {}
|
||||
|
||||
OneDriveStorage::OneDriveStorage(Common::String code) {
|
||||
getAccessToken(
|
||||
new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
|
||||
new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
|
||||
code
|
||||
);
|
||||
OneDriveStorage::OneDriveStorage(Common::String code, Networking::ErrorCallback cb) {
|
||||
getAccessToken(code, cb);
|
||||
}
|
||||
|
||||
OneDriveStorage::~OneDriveStorage() {}
|
||||
|
||||
void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
|
||||
if (!KEY || !SECRET)
|
||||
loadKeyAndSecret();
|
||||
bool codeFlow = (code != "");
|
||||
Common::String OneDriveStorage::cloudProvider() { return "onedrive"; }
|
||||
|
||||
if (!codeFlow && _refreshToken == "") {
|
||||
warning("OneDriveStorage: no refresh token available to get new access token.");
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
return;
|
||||
}
|
||||
uint32 OneDriveStorage::storageIndex() { return kStorageOneDriveId; }
|
||||
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
|
||||
if (errorCallback == nullptr)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, ONEDRIVE_OAUTH2_TOKEN);
|
||||
if (codeFlow) {
|
||||
request->addPostField("code=" + code);
|
||||
request->addPostField("grant_type=authorization_code");
|
||||
} else {
|
||||
request->addPostField("refresh_token=" + _refreshToken);
|
||||
request->addPostField("grant_type=refresh_token");
|
||||
}
|
||||
request->addPostField("client_id=" + Common::String(KEY));
|
||||
request->addPostField("client_secret=" + Common::String(SECRET));
|
||||
if (Cloud::CloudManager::couldUseLocalServer()) {
|
||||
request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
|
||||
} else {
|
||||
request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
|
||||
}
|
||||
addRequest(request);
|
||||
}
|
||||
bool OneDriveStorage::needsRefreshToken() { return true; }
|
||||
|
||||
void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
|
||||
Common::JSONValue *json = response.value;
|
||||
if (!json) {
|
||||
warning("OneDriveStorage: got NULL instead of JSON");
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage")) {
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
delete json;
|
||||
delete callback;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::JSONObject result = json->asObject();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "OneDriveStorage") ||
|
||||
!Networking::CurlJsonRequest::jsonContainsString(result, "user_id", "OneDriveStorage") ||
|
||||
!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "OneDriveStorage")) {
|
||||
warning("OneDriveStorage: bad response, no token or user_id passed");
|
||||
debug(9, "%s", json->stringify().c_str());
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, false));
|
||||
} else {
|
||||
_token = result.getVal("access_token")->asString();
|
||||
_uid = result.getVal("user_id")->asString();
|
||||
_refreshToken = result.getVal("refresh_token")->asString();
|
||||
CloudMan.save(); //ask CloudManager to save our new refreshToken
|
||||
if (callback)
|
||||
(*callback)(BoolResponse(nullptr, true));
|
||||
}
|
||||
delete json;
|
||||
delete callback;
|
||||
}
|
||||
|
||||
void OneDriveStorage::codeFlowComplete(BoolResponse response) {
|
||||
if (!response.value) {
|
||||
warning("OneDriveStorage: failed to get access token through code flow");
|
||||
CloudMan.removeStorage(this);
|
||||
return;
|
||||
}
|
||||
|
||||
ConfMan.removeKey("onedrive_code", ConfMan.kCloudDomain);
|
||||
CloudMan.replaceStorage(this, kStorageOneDriveId);
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
void OneDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
|
||||
debug(9, "OneDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
|
||||
debug(9, "%s", error.response.c_str());
|
||||
CloudMan.removeStorage(this);
|
||||
}
|
||||
bool OneDriveStorage::canReuseRefreshToken() { return false; }
|
||||
|
||||
void OneDriveStorage::saveConfig(Common::String keyPrefix) {
|
||||
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
|
||||
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
|
||||
saveIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
Common::String OneDriveStorage::name() const {
|
||||
|
@ -247,7 +137,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
|
|||
}
|
||||
|
||||
Common::JSONObject result = response.value->asObject();
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "@content.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
|
||||
if (!Networking::CurlJsonRequest::jsonContainsString(result, "@microsoft.graph.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
|
||||
warning("OneDriveStorage: downloadUrl not found in passed JSON");
|
||||
debug(9, "%s", response.value->stringify().c_str());
|
||||
if (outerCallback)
|
||||
|
@ -257,7 +147,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
|
|||
return;
|
||||
}
|
||||
|
||||
const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
|
||||
const char *url = result.getVal("@microsoft.graph.downloadUrl")->asString().c_str();
|
||||
if (outerCallback)
|
||||
(*outerCallback)(Networking::NetworkReadStreamResponse(
|
||||
response.request,
|
||||
|
@ -269,28 +159,33 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
|
|||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
debug(9, "OneDrive: `ls \"%s\"`", path.c_str());
|
||||
return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `upload \"%s\"`", path.c_str());
|
||||
return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `download \"%s\"`", path.c_str());
|
||||
Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
|
||||
Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
|
||||
request->addHeader("Authorization: Bearer " + _token);
|
||||
request->addHeader("Authorization: bearer " + _token);
|
||||
return addRequest(request);
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `mkdir \"%s\"`", path.c_str());
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
|
||||
}
|
||||
|
||||
Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
debug(9, "OneDrive: `info`");
|
||||
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
|
||||
Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
|
||||
request->addHeader("Authorization: bearer " + _token);
|
||||
|
@ -300,27 +195,25 @@ Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Network
|
|||
Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
|
||||
|
||||
OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
|
||||
loadKeyAndSecret();
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
|
||||
warning("OneDriveStorage: no access_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
|
||||
warning("OneDriveStorage: no user_id found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
|
||||
warning("OneDriveStorage: no refresh_token found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
|
||||
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
return new OneDriveStorage(accessToken, userId, refreshToken);
|
||||
return new OneDriveStorage(accessToken, refreshToken, loadIsEnabledFlag(keyPrefix));
|
||||
}
|
||||
|
||||
void OneDriveStorage::removeFromConfig(Common::String keyPrefix) {
|
||||
ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
|
||||
ConfMan.removeKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
|
||||
removeIsEnabledFlag(keyPrefix);
|
||||
}
|
||||
|
||||
} // End of namespace OneDrive
|
||||
|
|
|
@ -23,33 +23,39 @@
|
|||
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
|
||||
#define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
|
||||
|
||||
#include "backends/cloud/storage.h"
|
||||
#include "backends/cloud/basestorage.h"
|
||||
#include "backends/networking/curl/curljsonrequest.h"
|
||||
|
||||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
class OneDriveStorage: public Cloud::Storage {
|
||||
static char *KEY, *SECRET;
|
||||
|
||||
static void loadKeyAndSecret();
|
||||
|
||||
Common::String _token, _uid, _refreshToken;
|
||||
|
||||
class OneDriveStorage: public Cloud::BaseStorage {
|
||||
/** This private constructor is called from loadFromConfig(). */
|
||||
OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken);
|
||||
|
||||
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
|
||||
void codeFlowComplete(BoolResponse response);
|
||||
void codeFlowFailed(Networking::ErrorResponse error);
|
||||
OneDriveStorage(Common::String token, Common::String refreshToken, bool enabled);
|
||||
|
||||
/** Constructs StorageInfo based on JSON response from cloud. */
|
||||
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
|
||||
|
||||
void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @return "onedrive"
|
||||
*/
|
||||
virtual Common::String cloudProvider();
|
||||
|
||||
/**
|
||||
* @return kStorageOneDriveId
|
||||
*/
|
||||
virtual uint32 storageIndex();
|
||||
|
||||
virtual bool needsRefreshToken();
|
||||
|
||||
virtual bool canReuseRefreshToken();
|
||||
|
||||
public:
|
||||
/** This constructor uses OAuth code flow to get tokens. */
|
||||
OneDriveStorage(Common::String code);
|
||||
OneDriveStorage(Common::String code, Networking::ErrorCallback cb);
|
||||
virtual ~OneDriveStorage();
|
||||
|
||||
/**
|
||||
|
@ -98,11 +104,9 @@ public:
|
|||
static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
|
||||
|
||||
/**
|
||||
* Gets new access_token. If <code> passed is "", refresh_token is used.
|
||||
* Use "" in order to refresh token and pass a callback, so you could
|
||||
* continue your work when new token is available.
|
||||
* Remove all OneDriveStorage-related data from config.
|
||||
*/
|
||||
void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = "");
|
||||
static void removeFromConfig(Common::String keyPrefix);
|
||||
|
||||
Common::String accessToken() const { return _token; }
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
|
|||
if (!response.value) {
|
||||
//failed to refresh token, notify user with NULL in original callback
|
||||
warning("OneDriveTokenRefresher: failed to refresh token");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveTokenRefresher::tokenRefreshed: failed to refresh token", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,18 +94,19 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
irrecoverable = false;
|
||||
}
|
||||
|
||||
if (code == "unauthenticated")
|
||||
if (code == "unauthenticated" || code == "InvalidAuthenticationToken")
|
||||
irrecoverable = false;
|
||||
|
||||
if (irrecoverable) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, json->stringify(true), httpResponseCode));
|
||||
Common::String errorContents = "<irrecoverable> " + json->stringify(true);
|
||||
finishError(Networking::ErrorResponse(this, false, true, errorContents, httpResponseCode));
|
||||
delete json;
|
||||
return;
|
||||
}
|
||||
|
||||
pause();
|
||||
delete json;
|
||||
_parentStorage->getAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
|
||||
_parentStorage->refreshAccessToken(new Common::Callback<OneDriveTokenRefresher, Storage::BoolResponse>(this, &OneDriveTokenRefresher::tokenRefreshed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +115,29 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
|
|||
CurlJsonRequest::finishJson(json);
|
||||
}
|
||||
|
||||
void OneDriveTokenRefresher::finishError(Networking::ErrorResponse error) {
|
||||
if (error.failed) {
|
||||
Common::JSONValue *value = Common::JSON::parse(error.response.c_str());
|
||||
|
||||
//somehow OneDrive returns JSON with '.' in unexpected places, try fixing it
|
||||
if (!value) {
|
||||
Common::String fixedResponse = error.response;
|
||||
for (uint32 i = 0; i < fixedResponse.size(); ++i) {
|
||||
if (fixedResponse[i] == '.')
|
||||
fixedResponse.replace(i, 1, " ");
|
||||
}
|
||||
value = Common::JSON::parse(fixedResponse.c_str());
|
||||
}
|
||||
|
||||
if (value) {
|
||||
finishJson(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Request::finishError(error); //call closest base class's method
|
||||
}
|
||||
|
||||
void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
|
||||
_headers = headers;
|
||||
curl_slist_free_all(_headersList);
|
||||
|
|
|
@ -38,6 +38,7 @@ class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
|
|||
void tokenRefreshed(Storage::BoolResponse response);
|
||||
|
||||
virtual void finishJson(Common::JSONValue *json);
|
||||
virtual void finishError(Networking::ErrorResponse error);
|
||||
public:
|
||||
OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
|
||||
virtual ~OneDriveTokenRefresher();
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
namespace Cloud {
|
||||
namespace OneDrive {
|
||||
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/upload.createSession"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CONTENT "https://api.onedrive.com/v1.0/drive/special/approot:/%s:/content"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/upload.createSession"
|
||||
#define ONEDRIVE_API_SPECIAL_APPROOT_CONTENT "https://graph.microsoft.com/v1.0/drive/special/approot:/%s:/content"
|
||||
|
||||
OneDriveUploadRequest::OneDriveUploadRequest(OneDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
|
||||
Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
|
||||
|
@ -56,12 +56,12 @@ void OneDriveUploadRequest::start() {
|
|||
_workingRequest->finish();
|
||||
if (_contentsStream == nullptr) {
|
||||
warning("OneDriveUploadRequest: cannot restart because no stream given");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "No stream given", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveUploadRequest::start: can't restart, because no stream given", -1));
|
||||
return;
|
||||
}
|
||||
if (!_contentsStream->seek(0)) {
|
||||
warning("OneDriveUploadRequest: cannot restart because stream couldn't seek(0)");
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "OneDriveUploadRequest::start: can't restart, because seek(0) didn't work", -1));
|
||||
return;
|
||||
}
|
||||
_ignoreCallback = false;
|
||||
|
|
|
@ -72,7 +72,7 @@ void SavesSyncRequest::start() {
|
|||
new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
|
||||
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this));
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this, "SavesSyncRequest::start: Storage couldn't create Request to list directory"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse response) {
|
||||
|
@ -90,17 +90,19 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
|
|||
//determine which files to download and which files to upload
|
||||
Common::Array<StorageFile> &remoteFiles = response.value;
|
||||
uint64 totalSize = 0;
|
||||
debug(9, "SavesSyncRequest decisions:");
|
||||
for (uint32 i = 0; i < remoteFiles.size(); ++i) {
|
||||
StorageFile &file = remoteFiles[i];
|
||||
if (file.isDirectory())
|
||||
continue;
|
||||
totalSize += file.size();
|
||||
if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
|
||||
if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME || !CloudMan.canSyncFilename(file.name()))
|
||||
continue;
|
||||
|
||||
Common::String name = file.name();
|
||||
if (!_localFilesTimestamps.contains(name)) {
|
||||
_filesToDownload.push_back(file);
|
||||
debug(9, "- downloading file %s, because it is not present on local", name.c_str());
|
||||
} else {
|
||||
localFileNotAvailableInCloud[name] = false;
|
||||
|
||||
|
@ -113,6 +115,13 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
|
|||
_filesToUpload.push_back(file.name());
|
||||
else
|
||||
_filesToDownload.push_back(file);
|
||||
|
||||
if (_localFilesTimestamps[name] == DefaultSaveFileManager::INVALID_TIMESTAMP)
|
||||
debug(9, "- uploading file %s, because it is has invalid timestamp", name.c_str());
|
||||
else if (_localFilesTimestamps[name] > file.timestamp())
|
||||
debug(9, "- uploading file %s, because it is %d seconds newer than remote\n\tlocal = %d; \tremote = %d", name.c_str(), _localFilesTimestamps[name] - file.timestamp(), _localFilesTimestamps[name], file.timestamp());
|
||||
else
|
||||
debug(9, "- downloading file %s, because it is %d seconds older than remote\n\tlocal = %d; \tremote = %d", name.c_str(), file.timestamp() - _localFilesTimestamps[name], _localFilesTimestamps[name], file.timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,24 +129,41 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
|
|||
|
||||
//upload files which are unavailable in cloud
|
||||
for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) {
|
||||
if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME)
|
||||
if (i->_key == DefaultSaveFileManager::TIMESTAMPS_FILENAME || !CloudMan.canSyncFilename(i->_key))
|
||||
continue;
|
||||
if (i->_value)
|
||||
if (i->_value) {
|
||||
_filesToUpload.push_back(i->_key);
|
||||
debug(9, "- uploading file %s, because it is not present on remote", i->_key.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
debug(9, "\nSavesSyncRequest: download files:");
|
||||
for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
|
||||
debug(9, "%s", _filesToDownload[i].name().c_str());
|
||||
debug(9, "\nSavesSyncRequest: ");
|
||||
if (_filesToDownload.size() > 0) {
|
||||
debug(9, "download files:");
|
||||
for (uint32 i = 0; i < _filesToDownload.size(); ++i) {
|
||||
debug(9, " %s", _filesToDownload[i].name().c_str());
|
||||
}
|
||||
debug(9, "%s", "");
|
||||
} else {
|
||||
debug(9, "nothing to download");
|
||||
}
|
||||
debug(9, "\nSavesSyncRequest: upload files:");
|
||||
for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
|
||||
debug(9, "%s", _filesToUpload[i].c_str());
|
||||
debug(9, "SavesSyncRequest: ");
|
||||
if (_filesToUpload.size() > 0) {
|
||||
debug(9, "upload files:");
|
||||
for (uint32 i = 0; i < _filesToUpload.size(); ++i) {
|
||||
debug(9, " %s", _filesToUpload[i].c_str());
|
||||
}
|
||||
} else {
|
||||
debug(9, "nothing to upload");
|
||||
}
|
||||
_totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
|
||||
|
||||
//start downloading files
|
||||
downloadNextFile();
|
||||
if (!_filesToDownload.empty()) {
|
||||
downloadNextFile();
|
||||
} else {
|
||||
uploadNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
|
||||
|
@ -145,9 +171,22 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
|
|||
if (_ignoreCallback)
|
||||
return;
|
||||
|
||||
if (error.failed) debug(9, "%s", error.response.c_str());
|
||||
|
||||
bool irrecoverable = error.interrupted || error.failed;
|
||||
if (error.failed) {
|
||||
Common::JSONValue *value = Common::JSON::parse(error.response.c_str());
|
||||
|
||||
// somehow OneDrive returns JSON with '.' in unexpected places, try fixing it
|
||||
if (!value) {
|
||||
Common::String fixedResponse = error.response;
|
||||
for (uint32 i = 0; i < fixedResponse.size(); ++i) {
|
||||
if (fixedResponse[i] == '.')
|
||||
fixedResponse.replace(i, 1, " ");
|
||||
}
|
||||
value = Common::JSON::parse(fixedResponse.c_str());
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (value->isObject()) {
|
||||
Common::JSONObject object = value->asObject();
|
||||
|
@ -174,11 +213,13 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
|
|||
delete value;
|
||||
}
|
||||
|
||||
//Google Drive and Box-related ScummVM-based error
|
||||
//Google Drive, Box and OneDrive-related ScummVM-based error
|
||||
if (error.response.contains("subdirectory not found")) {
|
||||
irrecoverable = false; //base "/ScummVM/" folder not found
|
||||
} else if (error.response.contains("no such file found in its parent directory")) {
|
||||
irrecoverable = false; //"Saves" folder within "/ScummVM/" not found
|
||||
} else if (error.response.contains("itemNotFound") && error.response.contains("Item does not exist")) {
|
||||
irrecoverable = false; //"saves" folder within application folder is not found
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,14 +232,14 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
|
|||
Common::String dir = _storage->savesDirectoryPath();
|
||||
if (dir.lastChar() == '/')
|
||||
dir.deleteLastChar();
|
||||
debug(9, "SavesSyncRequest: creating %s", dir.c_str());
|
||||
debug(9, "\nSavesSyncRequest: creating %s", dir.c_str());
|
||||
_workingRequest = _storage->createDirectory(
|
||||
dir,
|
||||
new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
|
||||
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest)
|
||||
finishError(Networking::ErrorResponse(this));
|
||||
finishError(Networking::ErrorResponse(this, "SavesSyncRequest::directoryListedErrorCallback: Storage couldn't create Request to create remote directory"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
|
||||
|
@ -208,7 +249,7 @@ void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response)
|
|||
|
||||
//stop syncing if failed to create saves directory
|
||||
if (!response.value) {
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "SavesSyncRequest::directoryCreatedCallback: failed to create remote directory", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -239,7 +280,7 @@ void SavesSyncRequest::downloadNextFile() {
|
|||
|
||||
sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100));
|
||||
|
||||
debug(9, "SavesSyncRequest: downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
|
||||
debug(9, "\nSavesSyncRequest: downloading %s (%d %%)", _currentDownloadingFile.name().c_str(), (int)(getProgress() * 100));
|
||||
_workingRequest = _storage->downloadById(
|
||||
_currentDownloadingFile.id(),
|
||||
DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
|
||||
|
@ -247,7 +288,7 @@ void SavesSyncRequest::downloadNextFile() {
|
|||
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
|
||||
);
|
||||
if (!_workingRequest)
|
||||
finishError(Networking::ErrorResponse(this));
|
||||
finishError(Networking::ErrorResponse(this, "SavesSyncRequest::downloadNextFile: Storage couldn't create Request to download a file"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
|
||||
|
@ -259,7 +300,7 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
|
|||
if (!response.value) {
|
||||
//delete the incomplete file
|
||||
g_system->getSavefileManager()->removeSavefile(_currentDownloadingFile.name());
|
||||
finishError(Networking::ErrorResponse(this, false, true, "", -1));
|
||||
finishError(Networking::ErrorResponse(this, false, true, "SavesSyncRequest::fileDownloadedCallback: failed to download a file", -1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -290,7 +331,7 @@ void SavesSyncRequest::uploadNextFile() {
|
|||
_currentUploadingFile = _filesToUpload.back();
|
||||
_filesToUpload.pop_back();
|
||||
|
||||
debug(9, "SavesSyncRequest: uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
|
||||
debug(9, "\nSavesSyncRequest: uploading %s (%d %%)", _currentUploadingFile.c_str(), (int)(getProgress() * 100));
|
||||
if (_storage->uploadStreamSupported()) {
|
||||
_workingRequest = _storage->upload(
|
||||
_storage->savesDirectoryPath() + _currentUploadingFile,
|
||||
|
@ -306,7 +347,7 @@ void SavesSyncRequest::uploadNextFile() {
|
|||
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback)
|
||||
);
|
||||
}
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this));
|
||||
if (!_workingRequest) finishError(Networking::ErrorResponse(this, "SavesSyncRequest::uploadNextFile: Storage couldn't create Request to upload a file"));
|
||||
}
|
||||
|
||||
void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {
|
||||
|
|
|
@ -34,10 +34,18 @@ namespace Cloud {
|
|||
|
||||
Storage::Storage():
|
||||
_runningRequestsCount(0), _savesSyncRequest(nullptr), _syncRestartRequestsed(false),
|
||||
_downloadFolderRequest(nullptr) {}
|
||||
_downloadFolderRequest(nullptr), _isEnabled(false) {}
|
||||
|
||||
Storage::~Storage() {}
|
||||
|
||||
bool Storage::isEnabled() const {
|
||||
return _isEnabled;
|
||||
}
|
||||
|
||||
void Storage::enable() {
|
||||
_isEnabled = true;
|
||||
}
|
||||
|
||||
Networking::ErrorCallback Storage::getErrorPrintingCallback() {
|
||||
return new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::printErrorResponse);
|
||||
}
|
||||
|
@ -121,6 +129,12 @@ Networking::Request *Storage::downloadById(Common::String remoteId, Common::Stri
|
|||
}
|
||||
|
||||
Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::String localPath, FileArrayCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
|
||||
if (!_isEnabled) {
|
||||
warning("Storage::downloadFolder: cannot be run while Storage is disabled");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "Storage is disabled.", -1));
|
||||
return nullptr;
|
||||
}
|
||||
if (!errorCallback)
|
||||
errorCallback = getErrorPrintingCallback();
|
||||
return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive));
|
||||
|
@ -128,6 +142,13 @@ Networking::Request *Storage::downloadFolder(Common::String remotePath, Common::
|
|||
|
||||
SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
|
||||
_runningRequestsMutex.lock();
|
||||
if (!_isEnabled) {
|
||||
warning("Storage::syncSaves: cannot be run while Storage is disabled");
|
||||
if (errorCallback)
|
||||
(*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "Storage is disabled.", -1));
|
||||
_runningRequestsMutex.unlock();
|
||||
return nullptr;
|
||||
}
|
||||
if (_savesSyncRequest) {
|
||||
warning("Storage::syncSaves: there is a sync in progress already");
|
||||
_syncRestartRequestsed = true;
|
||||
|
@ -208,7 +229,6 @@ void Storage::savesSyncDefaultCallback(BoolResponse response) {
|
|||
|
||||
if (!response.value)
|
||||
warning("SavesSyncRequest called success callback with `false` argument");
|
||||
Common::OSDMessageQueue::instance().addMessage(_("Saved games sync complete."));
|
||||
}
|
||||
|
||||
void Storage::savesSyncDefaultErrorCallback(Networking::ErrorResponse error) {
|
||||
|
|
|
@ -70,6 +70,9 @@ protected:
|
|||
/** FolderDownloadRequest-related */
|
||||
FolderDownloadRequest *_downloadFolderRequest;
|
||||
|
||||
/** Whether user manually enabled the Storage. */
|
||||
bool _isEnabled;
|
||||
|
||||
/** Returns default error callback (printErrorResponse). */
|
||||
virtual Networking::ErrorCallback getErrorPrintingCallback();
|
||||
|
||||
|
@ -115,6 +118,16 @@ public:
|
|||
*/
|
||||
virtual Common::String name() const = 0;
|
||||
|
||||
/**
|
||||
* Return whether Storage has been manually enabled by user.
|
||||
*/
|
||||
bool isEnabled() const;
|
||||
|
||||
/**
|
||||
* Set _isEnabled to true.
|
||||
*/
|
||||
void enable();
|
||||
|
||||
/**
|
||||
* Public Cloud API comes down there.
|
||||
*
|
||||
|
|
|
@ -661,6 +661,10 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
|
|||
event.path = Common::String(ev.drop.file);
|
||||
SDL_free(ev.drop.file);
|
||||
return true;
|
||||
|
||||
case SDL_CLIPBOARDUPDATE:
|
||||
event.type = Common::EVENT_CLIPBOARD_UPDATE;
|
||||
return true;
|
||||
#else
|
||||
case SDL_VIDEOEXPOSE:
|
||||
if (_graphicsManager)
|
||||
|
|
|
@ -193,13 +193,11 @@ public:
|
|||
virtual Common::WriteStream *createWriteStream() = 0;
|
||||
|
||||
/**
|
||||
* Creates a file referred by this node.
|
||||
* Creates a directory referred by this node.
|
||||
*
|
||||
* @param isDirectoryFlag true if created file must be a directory
|
||||
*
|
||||
* @return true if file is created successfully
|
||||
* @return true if the directory is created successfully
|
||||
*/
|
||||
virtual bool create(bool isDirectoryFlag) = 0;
|
||||
virtual bool createDirectory() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -443,9 +443,9 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() {
|
|||
return StdioStream::makeFromPath(getPath(), true);
|
||||
}
|
||||
|
||||
bool AmigaOSFilesystemNode::create(bool isDirectoryFlag) {
|
||||
error("Not supported");
|
||||
return false;
|
||||
bool AmigaOSFilesystemNode::createDirectory() {
|
||||
warning("AmigaOSFilesystemNode::createDirectory(): Not supported");
|
||||
return _bIsValid && _bIsDirectory;
|
||||
}
|
||||
|
||||
#endif //defined(__amigaos4__)
|
||||
|
|
|
@ -116,7 +116,7 @@ public:
|
|||
|
||||
virtual Common::SeekableReadStream *createReadStream();
|
||||
virtual Common::WriteStream *createWriteStream();
|
||||
virtual bool create(bool isDirectoryFlag);
|
||||
virtual bool createDirectory();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -22,16 +22,6 @@
|
|||
|
||||
#if defined(POSIX)
|
||||
|
||||
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
|
||||
// Also with clock() in sys/time.h in some Mac OS X SDKs.
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_exit //Needed for IRIX's unistd.h
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_random
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
|
||||
|
||||
#include "backends/fs/chroot/chroot-fs.h"
|
||||
|
||||
ChRootFilesystemNode::ChRootFilesystemNode(const Common::String &root, POSIXFilesystemNode *node) {
|
||||
|
@ -110,9 +100,8 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() {
|
|||
return _realNode->createWriteStream();
|
||||
}
|
||||
|
||||
bool ChRootFilesystemNode::create(bool isDirectoryFlag) {
|
||||
error("Not supported");
|
||||
return false;
|
||||
bool ChRootFilesystemNode::createDirectory() {
|
||||
return _realNode->createDirectory();
|
||||
}
|
||||
|
||||
Common::String ChRootFilesystemNode::addPathComponent(const Common::String &path, const Common::String &component) {
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
|
||||
virtual Common::SeekableReadStream *createReadStream();
|
||||
virtual Common::WriteStream *createWriteStream();
|
||||
virtual bool create(bool isDirectoryFlag);
|
||||
virtual bool createDirectory();
|
||||
|
||||
private:
|
||||
static Common::String addPathComponent(const Common::String &path, const Common::String &component);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom
|
||||
|
||||
#include "backends/fs/posix/posix-fs.h"
|
||||
#include "backends/fs/stdiostream.h"
|
||||
#include "backends/fs/posix/posix-iostream.h"
|
||||
#include "common/algorithm.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
|
@ -91,13 +91,13 @@ POSIXFilesystemNode::POSIXFilesystemNode(const Common::String &p) {
|
|||
#endif
|
||||
|
||||
// Expand "~/" to the value of the HOME env variable
|
||||
if (p.hasPrefix("~/")) {
|
||||
if (p.hasPrefix("~/") || p == "~") {
|
||||
const char *home = getenv("HOME");
|
||||
if (home != NULL && strlen(home) < MAXPATHLEN) {
|
||||
_path = home;
|
||||
// Skip over the tilda. We know that p contains at least
|
||||
// two chars, so this is safe:
|
||||
_path += p.c_str() + 1;
|
||||
// Skip over the tilda.
|
||||
if (p.size() > 1)
|
||||
_path += p.c_str() + 1;
|
||||
}
|
||||
} else {
|
||||
_path = p;
|
||||
|
@ -292,40 +292,18 @@ AbstractFSNode *POSIXFilesystemNode::getParent() const {
|
|||
}
|
||||
|
||||
Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() {
|
||||
return StdioStream::makeFromPath(getPath(), false);
|
||||
return PosixIoStream::makeFromPath(getPath(), false);
|
||||
}
|
||||
|
||||
Common::WriteStream *POSIXFilesystemNode::createWriteStream() {
|
||||
return StdioStream::makeFromPath(getPath(), true);
|
||||
return PosixIoStream::makeFromPath(getPath(), true);
|
||||
}
|
||||
|
||||
bool POSIXFilesystemNode::create(bool isDirectoryFlag) {
|
||||
bool success;
|
||||
|
||||
if (isDirectoryFlag) {
|
||||
success = mkdir(_path.c_str(), 0755) == 0;
|
||||
} else {
|
||||
int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755);
|
||||
success = fd >= 0;
|
||||
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
bool POSIXFilesystemNode::createDirectory() {
|
||||
if (mkdir(_path.c_str(), 0755) == 0)
|
||||
setFlags();
|
||||
if (_isValid) {
|
||||
if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
|
||||
return _isDirectory == isDirectoryFlag;
|
||||
}
|
||||
|
||||
warning("POSIXFilesystemNode: %s() was a success, but stat indicates there is no such %s",
|
||||
isDirectoryFlag ? "mkdir" : "creat", isDirectoryFlag ? "directory" : "file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return _isValid && _isDirectory;
|
||||
}
|
||||
|
||||
namespace Posix {
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
|
||||
virtual Common::SeekableReadStream *createReadStream();
|
||||
virtual Common::WriteStream *createWriteStream();
|
||||
virtual bool create(bool isDirectoryFlag);
|
||||
virtual bool createDirectory();
|
||||
|
||||
private:
|
||||
/**
|
||||
|
|
56
backends/fs/posix/posix-iostream.cpp
Normal file
56
backends/fs/posix/posix-iostream.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||
|
||||
#include "backends/fs/posix/posix-iostream.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
PosixIoStream *PosixIoStream::makeFromPath(const Common::String &path, bool writeMode) {
|
||||
FILE *handle = fopen(path.c_str(), writeMode ? "wb" : "rb");
|
||||
|
||||
if (handle)
|
||||
return new PosixIoStream(handle);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PosixIoStream::PosixIoStream(void *handle) :
|
||||
StdioStream(handle) {
|
||||
}
|
||||
|
||||
int32 PosixIoStream::size() const {
|
||||
int fd = fileno((FILE *)_handle);
|
||||
if (fd == -1) {
|
||||
return StdioStream::size();
|
||||
}
|
||||
|
||||
// Using fstat to obtain the file size is generally faster than fseek / ftell
|
||||
// because it does not affect the IO buffer.
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1) {
|
||||
return StdioStream::size();
|
||||
}
|
||||
|
||||
return st.st_size;
|
||||
}
|
39
backends/fs/posix/posix-iostream.h
Normal file
39
backends/fs/posix/posix-iostream.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_FS_POSIX_POSIXIOSTREAM_H
|
||||
#define BACKENDS_FS_POSIX_POSIXIOSTREAM_H
|
||||
|
||||
#include "backends/fs/stdiostream.h"
|
||||
|
||||
/**
|
||||
* A file input / output stream using POSIX interfaces
|
||||
*/
|
||||
class PosixIoStream : public StdioStream {
|
||||
public:
|
||||
static PosixIoStream *makeFromPath(const Common::String &path, bool writeMode);
|
||||
PosixIoStream(void *handle);
|
||||
|
||||
int32 size() const override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,11 +20,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#if defined(RISCOS)
|
||||
|
||||
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(RISCOS)
|
||||
|
||||
#include "backends/fs/riscos/riscos-fs-factory.h"
|
||||
#include "backends/fs/riscos/riscos-fs.h"
|
||||
|
||||
|
|
|
@ -20,23 +20,21 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#if defined(RISCOS)
|
||||
|
||||
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(RISCOS)
|
||||
|
||||
#include "backends/platform/sdl/riscos/riscos-utils.h"
|
||||
#include "backends/fs/riscos/riscos-fs.h"
|
||||
#include "backends/fs/stdiostream.h"
|
||||
#include "common/algorithm.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
// TODO: Replace use of access()
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <unixlib/local.h>
|
||||
#include <kernel.h>
|
||||
#include <swis.h>
|
||||
|
||||
|
@ -52,23 +50,33 @@ bool RISCOSFilesystemNode::isWritable() const {
|
|||
return access(_path.c_str(), W_OK) == 0;
|
||||
}
|
||||
|
||||
RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) {
|
||||
_path = p;
|
||||
if (p == "/") {
|
||||
void RISCOSFilesystemNode::setFlags() {
|
||||
_kernel_swi_regs regs;
|
||||
regs.r[0] = 23;
|
||||
regs.r[1] = (int)_nativePath.c_str();
|
||||
_kernel_swi(OS_File, ®s, ®s);
|
||||
|
||||
if (regs.r[0] == 0) {
|
||||
_isDirectory = false;
|
||||
_isValid = false;
|
||||
} else if (regs.r[0] == 2) {
|
||||
_isDirectory = true;
|
||||
_isValid = true;
|
||||
} else {
|
||||
int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, RISCOS_Utils::toRISCOS(_path).c_str());
|
||||
if (type == 0) {
|
||||
_isDirectory = false;
|
||||
_isValid = false;
|
||||
} else if (type == 2) {
|
||||
_isDirectory = true;
|
||||
_isValid = true;
|
||||
} else {
|
||||
_isDirectory = false;
|
||||
_isValid = true;
|
||||
}
|
||||
_isDirectory = false;
|
||||
_isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) {
|
||||
_path = p;
|
||||
if (p == "/") {
|
||||
_nativePath = "";
|
||||
_isDirectory = true;
|
||||
_isValid = true;
|
||||
} else {
|
||||
_nativePath = RISCOS_Utils::toRISCOS(p);
|
||||
setFlags();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,47 +102,60 @@ bool RISCOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bo
|
|||
|
||||
if (_path == "/") {
|
||||
// Special case for the root dir: List all drives
|
||||
char fsname[PATH_MAX] = "";
|
||||
char fsname[MAXPATHLEN] = "";
|
||||
for (int fsNum = 0; fsNum < 256; fsNum += 1) {
|
||||
_swi(OS_FSControl, _INR(0,3), 33, fsNum, fsname, sizeof(fsname));
|
||||
if (strcmp(fsname, "") != 0) {
|
||||
if (!(fsNum == 46 || fsNum == 53 || fsNum == 99)) {
|
||||
int drives = 9;
|
||||
if (fsNum == 193)
|
||||
drives = 23;
|
||||
if (fsNum == 33 || fsNum == 46 || fsNum == 53 || fsNum == 99)
|
||||
continue;
|
||||
|
||||
for (int discnum = 0; discnum <= drives; discnum += 1) {
|
||||
const Common::String path = Common::String::format("%s::%d.$", fsname, discnum);
|
||||
char outpath[PATH_MAX] = "";
|
||||
if(_swix(OS_FSControl, _INR(0,2)|_IN(5), 37, path.c_str(), outpath, sizeof(outpath)) == NULL) {
|
||||
int exist;
|
||||
if (_swix(OS_File, _INR(0,1)|_OUT(0), 23, outpath, &exist) != NULL || exist != 2)
|
||||
continue;
|
||||
_kernel_swi_regs regs;
|
||||
regs.r[0] = 33;
|
||||
regs.r[1] = fsNum;
|
||||
regs.r[2] = (int)fsname;
|
||||
regs.r[3] = sizeof(fsname);
|
||||
_kernel_swi(OS_FSControl, ®s, ®s);
|
||||
if (fsname[0] == 0)
|
||||
continue;
|
||||
|
||||
RISCOSFilesystemNode *entry = new RISCOSFilesystemNode();
|
||||
entry->_isDirectory = true;
|
||||
entry->_isValid = true;
|
||||
entry->_path = Common::String::format("/%s", outpath);
|
||||
entry->_displayName = outpath;
|
||||
myList.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
int drives = (fsNum == 193) ? 23 : 9;
|
||||
|
||||
for (int discnum = 0; discnum <= drives; discnum += 1) {
|
||||
const Common::String path = Common::String::format("%s::%d.$", fsname, discnum);
|
||||
char outpath[MAXPATHLEN] = "";
|
||||
regs.r[0] = 37;
|
||||
regs.r[1] = (int)path.c_str();
|
||||
regs.r[2] = (int)outpath;
|
||||
regs.r[3] = 0;
|
||||
regs.r[4] = 0;
|
||||
regs.r[5] = sizeof(outpath);
|
||||
if (_kernel_swi(OS_FSControl, ®s, ®s) != NULL)
|
||||
continue;
|
||||
|
||||
RISCOSFilesystemNode *entry = new RISCOSFilesystemNode();
|
||||
entry->_nativePath = outpath;
|
||||
entry->_path = Common::String('/') + outpath;
|
||||
entry->_displayName = outpath;
|
||||
entry->setFlags();
|
||||
if (entry->_isDirectory)
|
||||
myList.push_back(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int read = 0;
|
||||
char file[PATH_MAX];
|
||||
Common::String dir = _path;
|
||||
char file[MAXPATHLEN];
|
||||
_kernel_swi_regs regs;
|
||||
regs.r[0] = 9;
|
||||
regs.r[1] = (int)_nativePath.c_str();
|
||||
regs.r[2] = (int)file;
|
||||
regs.r[3] = 1;
|
||||
regs.r[4] = 0;
|
||||
regs.r[5] = sizeof(file);
|
||||
regs.r[6] = 0;
|
||||
while (regs.r[4] != -1) {
|
||||
_kernel_swi(OS_GBPB, ®s, ®s);
|
||||
|
||||
while (count != -1) {
|
||||
_swix(OS_GBPB, _INR(0,5)|_OUTR(3,4), 9, RISCOS_Utils::toRISCOS(dir).c_str(), file, 1, count, sizeof(file), &read, &count);
|
||||
|
||||
if (count == -1)
|
||||
continue;
|
||||
if (regs.r[4] == -1)
|
||||
break;
|
||||
|
||||
// Start with a clone of this node, with the correct path set
|
||||
RISCOSFilesystemNode entry(*this);
|
||||
|
@ -143,15 +164,10 @@ bool RISCOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bo
|
|||
if (_path.lastChar() != '/')
|
||||
entry._path += '/';
|
||||
entry._path += entry._displayName;
|
||||
|
||||
int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, RISCOS_Utils::toRISCOS(entry._path).c_str());
|
||||
if (type == 0) {
|
||||
entry._nativePath = RISCOS_Utils::toRISCOS(entry._path);
|
||||
entry.setFlags();
|
||||
if (!entry._isValid)
|
||||
continue;
|
||||
} else if (type == 2) {
|
||||
entry._isDirectory = true;
|
||||
} else {
|
||||
entry._isDirectory = false;
|
||||
}
|
||||
|
||||
// Honor the chosen mode
|
||||
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
|
||||
|
@ -198,33 +214,14 @@ Common::WriteStream *RISCOSFilesystemNode::createWriteStream() {
|
|||
return StdioStream::makeFromPath(getPath(), true);
|
||||
}
|
||||
|
||||
bool RISCOSFilesystemNode::create(bool isDirectoryFlag) {
|
||||
bool success;
|
||||
bool RISCOSFilesystemNode::createDirectory() {
|
||||
_kernel_swi_regs regs;
|
||||
regs.r[0] = 8;
|
||||
regs.r[1] = (int)_nativePath.c_str();
|
||||
if (_kernel_swi(OS_File, ®s, ®s) == NULL)
|
||||
setFlags();
|
||||
|
||||
if (isDirectoryFlag) {
|
||||
success = _swix(OS_File, _INR(0,1), 8, RISCOS_Utils::toRISCOS(_path).c_str()) == NULL;
|
||||
} else {
|
||||
int fd = open(_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755);
|
||||
success = fd >= 0;
|
||||
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if (exists()) {
|
||||
_isDirectory = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, RISCOS_Utils::toRISCOS(_path).c_str()) == 2;
|
||||
if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
|
||||
return _isDirectory == isDirectoryFlag;
|
||||
}
|
||||
|
||||
warning("RISCOSFilesystemNode: Attempting to create a %s was a success, but access indicates there is no such %s",
|
||||
isDirectoryFlag ? "directory" : "file", isDirectoryFlag ? "directory" : "file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return _isValid && _isDirectory;
|
||||
}
|
||||
|
||||
namespace Riscos {
|
||||
|
@ -270,7 +267,7 @@ bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
|
|||
}
|
||||
|
||||
node = new RISCOSFilesystemNode(path);
|
||||
if (!node->create(true)) {
|
||||
if (!node->createDirectory()) {
|
||||
if (node->exists()) {
|
||||
if (!node->isDirectory()) {
|
||||
return false;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
class RISCOSFilesystemNode : public AbstractFSNode {
|
||||
protected:
|
||||
Common::String _displayName;
|
||||
Common::String _nativePath;
|
||||
Common::String _path;
|
||||
bool _isDirectory;
|
||||
bool _isValid;
|
||||
|
@ -67,7 +68,12 @@ public:
|
|||
|
||||
virtual Common::SeekableReadStream *createReadStream();
|
||||
virtual Common::WriteStream *createWriteStream();
|
||||
virtual bool create(bool isDirectoryFlag);
|
||||
virtual bool createDirectory();
|
||||
private:
|
||||
/**
|
||||
* Tests and sets the _isValid and _isDirectory flags, using OS_File 20.
|
||||
*/
|
||||
virtual void setFlags();
|
||||
};
|
||||
|
||||
namespace Riscos {
|
||||
|
|
|
@ -134,6 +134,12 @@ WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool
|
|||
|
||||
_displayName = lastPathComponent(_path, '\\');
|
||||
|
||||
setFlags();
|
||||
|
||||
_isPseudoRoot = false;
|
||||
}
|
||||
|
||||
void WindowsFilesystemNode::setFlags() {
|
||||
// Check whether it is a directory, and whether the file actually exists
|
||||
DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str()));
|
||||
|
||||
|
@ -148,7 +154,6 @@ WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool
|
|||
_path += '\\';
|
||||
}
|
||||
}
|
||||
_isPseudoRoot = false;
|
||||
}
|
||||
|
||||
AbstractFSNode *WindowsFilesystemNode::getChild(const Common::String &n) const {
|
||||
|
@ -244,36 +249,11 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() {
|
|||
return StdioStream::makeFromPath(getPath(), true);
|
||||
}
|
||||
|
||||
bool WindowsFilesystemNode::create(bool isDirectoryFlag) {
|
||||
bool success;
|
||||
bool WindowsFilesystemNode::createDirectory() {
|
||||
if (CreateDirectory(toUnicode(_path.c_str()), NULL) != 0)
|
||||
setFlags();
|
||||
|
||||
if (isDirectoryFlag) {
|
||||
success = CreateDirectory(toUnicode(_path.c_str()), NULL) != 0;
|
||||
} else {
|
||||
success = CreateFile(toUnicode(_path.c_str()), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
//this piece is copied from constructor, it checks that file exists and detects whether it's a directory
|
||||
DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str()));
|
||||
if (fileAttribs != INVALID_FILE_ATTRIBUTES) {
|
||||
_isDirectory = ((fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
||||
_isValid = true;
|
||||
// Add a trailing slash, if necessary.
|
||||
if (_isDirectory && _path.lastChar() != '\\') {
|
||||
_path += '\\';
|
||||
}
|
||||
|
||||
if (_isDirectory != isDirectoryFlag) warning("failed to create %s: got %s", isDirectoryFlag ? "directory" : "file", _isDirectory ? "directory" : "file");
|
||||
return _isDirectory == isDirectoryFlag;
|
||||
}
|
||||
|
||||
warning("WindowsFilesystemNode: Create%s() was a success, but GetFileAttributes() indicates there is no such %s",
|
||||
isDirectoryFlag ? "Directory" : "File", isDirectoryFlag ? "directory" : "file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return _isValid && _isDirectory;
|
||||
}
|
||||
|
||||
#endif //#ifdef WIN32
|
||||
|
|
|
@ -84,7 +84,7 @@ public:
|
|||
|
||||
virtual Common::SeekableReadStream *createReadStream();
|
||||
virtual Common::WriteStream *createWriteStream();
|
||||
virtual bool create(bool isDirectoryFlag);
|
||||
virtual bool createDirectory();
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -114,6 +114,11 @@ private:
|
|||
* @return str in Unicode format.
|
||||
*/
|
||||
static const TCHAR* toUnicode(const char *str);
|
||||
|
||||
/**
|
||||
* Tests and sets the _isValid and _isDirectory flags, using the GetFileAttributes() function.
|
||||
*/
|
||||
virtual void setFlags();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -81,7 +81,7 @@ void SdlMixerManager::init() {
|
|||
warning("Could not open audio device: %s", SDL_GetError());
|
||||
|
||||
// The mixer is not marked as ready
|
||||
_mixer = new Audio::MixerImpl(g_system, desired.freq);
|
||||
_mixer = new Audio::MixerImpl(desired.freq);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ void SdlMixerManager::init() {
|
|||
warning("Could not open audio device: %s", SDL_GetError());
|
||||
|
||||
// The mixer is not marked as ready
|
||||
_mixer = new Audio::MixerImpl(g_system, desired.freq);
|
||||
_mixer = new Audio::MixerImpl(desired.freq);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ void SdlMixerManager::init() {
|
|||
error("SDL mixer output requires stereo output device");
|
||||
#endif
|
||||
|
||||
_mixer = new Audio::MixerImpl(g_system, _obtained.freq);
|
||||
_mixer = new Audio::MixerImpl(_obtained.freq);
|
||||
assert(_mixer);
|
||||
_mixer->setReady(true);
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
|
||||
|
||||
#include "backends/modular-backend.h"
|
||||
|
||||
#include "backends/graphics/graphics.h"
|
||||
|
@ -302,7 +300,3 @@ void ModularBackend::displayMessageOnOSD(const char *msg) {
|
|||
void ModularBackend::displayActivityIconOnOSD(const Graphics::Surface *icon) {
|
||||
_graphicsManager->displayActivityIconOnOSD(icon);
|
||||
}
|
||||
|
||||
void ModularBackend::quit() {
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ class MutexManager;
|
|||
* OSystem::getMillis()
|
||||
* OSystem::delayMillis()
|
||||
* OSystem::getTimeAndDate()
|
||||
* OSystem::quit()
|
||||
*
|
||||
* And, it should also initialize all the managers variables
|
||||
* declared in this class, or override their related functions.
|
||||
|
@ -138,7 +139,6 @@ public:
|
|||
/** @name Miscellaneous */
|
||||
//@{
|
||||
|
||||
virtual void quit() override;
|
||||
virtual void displayMessageOnOSD(const char *msg) override;
|
||||
virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ ifdef USE_CLOUD
|
|||
|
||||
ifdef USE_LIBCURL
|
||||
MODULE_OBJS += \
|
||||
cloud/basestorage.o \
|
||||
cloud/cloudicon.o \
|
||||
cloud/cloudmanager.o \
|
||||
cloud/iso8601.o \
|
||||
|
@ -58,6 +59,7 @@ MODULE_OBJS += \
|
|||
networking/curl/networkreadstream.o \
|
||||
networking/curl/curlrequest.o \
|
||||
networking/curl/curljsonrequest.o \
|
||||
networking/curl/postrequest.o \
|
||||
networking/curl/request.o
|
||||
endif
|
||||
|
||||
|
@ -141,6 +143,7 @@ ifdef POSIX
|
|||
MODULE_OBJS += \
|
||||
fs/posix/posix-fs.o \
|
||||
fs/posix/posix-fs-factory.o \
|
||||
fs/posix/posix-iostream.o \
|
||||
fs/chroot/chroot-fs-factory.o \
|
||||
fs/chroot/chroot-fs.o \
|
||||
plugins/posix/posix-provider.o \
|
||||
|
@ -191,6 +194,7 @@ ifdef PLAYSTATION3
|
|||
MODULE_OBJS += \
|
||||
fs/posix/posix-fs.o \
|
||||
fs/posix/posix-fs-factory.o \
|
||||
fs/posix/posix-iostream.o \
|
||||
fs/ps3/ps3-fs-factory.o \
|
||||
events/ps3sdl/ps3sdl-events.o
|
||||
endif
|
||||
|
@ -269,6 +273,7 @@ endif
|
|||
ifeq ($(BACKEND),psp2)
|
||||
MODULE_OBJS += \
|
||||
fs/posix/posix-fs.o \
|
||||
fs/posix/posix-iostream.o \
|
||||
fs/psp2/psp2-fs-factory.o \
|
||||
fs/psp2/psp2-dirent.o \
|
||||
events/psp2sdl/psp2sdl-events.o \
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "backends/networking/curl/connectionmanager.h"
|
||||
#include "backends/networking/curl/networkreadstream.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
|
@ -98,6 +99,29 @@ uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
|
|||
return TIMER_INTERVAL * CLOUD_PERIOD;
|
||||
}
|
||||
|
||||
const char *ConnectionManager::getCaCertPath() {
|
||||
#if defined(DATA_PATH)
|
||||
static enum {
|
||||
kNotInitialized,
|
||||
kFileNotFound,
|
||||
kFileExists
|
||||
} state = kNotInitialized;
|
||||
|
||||
if (state == kNotInitialized) {
|
||||
Common::FSNode node(DATA_PATH"/cacert.pem");
|
||||
state = node.exists() ? kFileExists : kFileNotFound;
|
||||
}
|
||||
|
||||
if (state == kFileExists) {
|
||||
return DATA_PATH"/cacert.pem";
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
//private goes here:
|
||||
|
||||
void connectionsThread(void *ignored) {
|
||||
|
@ -151,7 +175,8 @@ void ConnectionManager::interateRequests() {
|
|||
_addedRequestsMutex.unlock();
|
||||
|
||||
//call handle() of all running requests (so they can do their work)
|
||||
debug(9, "handling %d request(s)", _requests.size());
|
||||
if (_frame % DEBUG_PRINT_PERIOD == 0)
|
||||
debug(9, "handling %d request(s)", _requests.size());
|
||||
for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
|
||||
Request *request = i->request;
|
||||
if (request) {
|
||||
|
@ -183,20 +208,19 @@ void ConnectionManager::processTransfers() {
|
|||
int messagesInQueue;
|
||||
CURLMsg *curlMsg;
|
||||
while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
|
||||
CURL *easyHandle = curlMsg->easy_handle;
|
||||
|
||||
NetworkReadStream *stream;
|
||||
curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
|
||||
if (stream)
|
||||
stream->finished();
|
||||
|
||||
if (curlMsg->msg == CURLMSG_DONE) {
|
||||
debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
|
||||
} else {
|
||||
warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
|
||||
}
|
||||
CURL *easyHandle = curlMsg->easy_handle;
|
||||
|
||||
curl_multi_remove_handle(_multi, easyHandle);
|
||||
NetworkReadStream *stream = nullptr;
|
||||
curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
|
||||
|
||||
if (stream)
|
||||
stream->finished(curlMsg->data.result);
|
||||
|
||||
curl_multi_remove_handle(_multi, easyHandle);
|
||||
} else {
|
||||
warning("Unknown libcurl message type %d", curlMsg->msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,11 @@ namespace Networking {
|
|||
class NetworkReadStream;
|
||||
|
||||
class ConnectionManager : public Common::Singleton<ConnectionManager> {
|
||||
static const uint32 FRAMES_PER_SECOND = 20;
|
||||
static const uint32 FRAMES_PER_SECOND = 25;
|
||||
static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
|
||||
static const uint32 CLOUD_PERIOD = 20; //every 20th frame
|
||||
static const uint32 CLOUD_PERIOD = 1; //every frame
|
||||
static const uint32 CURL_PERIOD = 1; //every frame
|
||||
static const uint32 DEBUG_PRINT_PERIOD = FRAMES_PER_SECOND; // once per second
|
||||
|
||||
friend void connectionsThread(void *); //calls handle()
|
||||
|
||||
|
@ -117,6 +118,9 @@ public:
|
|||
Common::String urlEncode(Common::String s) const;
|
||||
|
||||
static uint32 getCloudRequestsPeriodInMicroseconds();
|
||||
|
||||
/** Return the path to the CA certificates bundle. */
|
||||
static const char *getCaCertPath();
|
||||
};
|
||||
|
||||
/** Shortcut for accessing the connection manager. */
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace Networking {
|
|||
|
||||
typedef Response<Common::JSONValue *> JsonResponse;
|
||||
typedef Common::BaseCallback<JsonResponse> *JsonCallback;
|
||||
typedef Common::BaseCallback<Common::JSONValue *> *JSONValueCallback;
|
||||
|
||||
#define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ void CurlRequest::handle() {
|
|||
if (_stream && _stream->eos()) {
|
||||
if (_stream->httpResponseCode() != 200) {
|
||||
warning("CurlRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
|
||||
ErrorResponse error(this, false, true, "", _stream->httpResponseCode());
|
||||
ErrorResponse error(this, false, true, "HTTP response code is not 200 OK", _stream->httpResponseCode());
|
||||
finishError(error);
|
||||
return;
|
||||
}
|
||||
|
@ -71,20 +71,9 @@ void CurlRequest::restart() {
|
|||
|
||||
Common::String CurlRequest::date() const {
|
||||
if (_stream) {
|
||||
Common::String headers = _stream->responseHeaders();
|
||||
const char *cstr = headers.c_str();
|
||||
const char *position = strstr(cstr, "Date: ");
|
||||
|
||||
if (position) {
|
||||
Common::String result = "";
|
||||
char c;
|
||||
for (const char *i = position + 6; c = *i, c != 0; ++i) {
|
||||
if (c == '\n' || c == '\r')
|
||||
break;
|
||||
result += c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Common::HashMap<Common::String, Common::String> headers = _stream->responseHeadersMap();
|
||||
if (headers.contains("date"))
|
||||
return headers["date"];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "backends/networking/curl/networkreadstream.h"
|
||||
#include "backends/networking/curl/connectionmanager.h"
|
||||
#include "base/version.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Networking {
|
||||
|
||||
|
@ -64,6 +65,7 @@ int NetworkReadStream::curlProgressCallbackOlder(void *p, double dltotal, double
|
|||
|
||||
void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
|
||||
_eos = _requestComplete = false;
|
||||
_errorBuffer = (char *)calloc(CURL_ERROR_SIZE, 1);
|
||||
_sendingContentsBuffer = nullptr;
|
||||
_sendingContentsSize = _sendingContentsPos = 0;
|
||||
_progressDownloaded = _progressTotal = 0;
|
||||
|
@ -77,6 +79,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
|
|||
curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
|
||||
curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
|
||||
curl_easy_setopt(_easy, CURLOPT_URL, url);
|
||||
curl_easy_setopt(_easy, CURLOPT_ERRORBUFFER, _errorBuffer);
|
||||
curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
|
||||
curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
|
||||
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
|
||||
|
@ -84,6 +87,12 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
|
|||
curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
|
||||
curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
|
||||
|
||||
const char *caCertPath = ConnMan.getCaCertPath();
|
||||
if (caCertPath) {
|
||||
curl_easy_setopt(_easy, CURLOPT_CAINFO, caCertPath);
|
||||
}
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x072000
|
||||
// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
|
||||
// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
|
||||
|
@ -116,6 +125,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, const byt
|
|||
|
||||
void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
|
||||
_eos = _requestComplete = false;
|
||||
_errorBuffer = (char *)calloc(CURL_ERROR_SIZE, 1);
|
||||
_sendingContentsBuffer = nullptr;
|
||||
_sendingContentsSize = _sendingContentsPos = 0;
|
||||
_progressDownloaded = _progressTotal = 0;
|
||||
|
@ -129,6 +139,7 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
|
|||
curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
|
||||
curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
|
||||
curl_easy_setopt(_easy, CURLOPT_URL, url);
|
||||
curl_easy_setopt(_easy, CURLOPT_ERRORBUFFER, _errorBuffer);
|
||||
curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
|
||||
curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
|
||||
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
|
||||
|
@ -136,6 +147,12 @@ void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::H
|
|||
curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
|
||||
curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
|
||||
|
||||
const char *caCertPath = ConnMan.getCaCertPath();
|
||||
if (caCertPath) {
|
||||
curl_easy_setopt(_easy, CURLOPT_CAINFO, caCertPath);
|
||||
}
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x072000
|
||||
// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
|
||||
// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
|
||||
|
@ -197,6 +214,7 @@ NetworkReadStream::~NetworkReadStream() {
|
|||
if (_easy)
|
||||
curl_easy_cleanup(_easy);
|
||||
free(_bufferCopy);
|
||||
free(_errorBuffer);
|
||||
}
|
||||
|
||||
bool NetworkReadStream::eos() const {
|
||||
|
@ -215,8 +233,18 @@ uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
|
|||
return actuallyRead;
|
||||
}
|
||||
|
||||
void NetworkReadStream::finished() {
|
||||
void NetworkReadStream::finished(uint32 errorCode) {
|
||||
_requestComplete = true;
|
||||
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(_easy, CURLINFO_EFFECTIVE_URL, &url);
|
||||
|
||||
if (errorCode == CURLE_OK) {
|
||||
debug(9, "NetworkReadStream: %s - Request succeeded", url);
|
||||
} else {
|
||||
warning("NetworkReadStream: %s - Request failed (%d - %s)", url, errorCode,
|
||||
strlen(_errorBuffer) ? _errorBuffer : curl_easy_strerror((CURLcode)errorCode));
|
||||
}
|
||||
}
|
||||
|
||||
long NetworkReadStream::httpResponseCode() const {
|
||||
|
@ -240,6 +268,78 @@ Common::String NetworkReadStream::responseHeaders() const {
|
|||
return _responseHeaders;
|
||||
}
|
||||
|
||||
Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeadersMap() const {
|
||||
// HTTP headers are described at RFC 2616: https://tools.ietf.org/html/rfc2616#section-4.2
|
||||
// this implementation tries to follow it, but for simplicity it does not support multi-line header values
|
||||
|
||||
Common::HashMap<Common::String, Common::String> headers;
|
||||
Common::String headerName, headerValue, trailingWhitespace;
|
||||
char c;
|
||||
bool readingName = true;
|
||||
|
||||
for (uint i = 0; i < _responseHeaders.size(); ++i) {
|
||||
c = _responseHeaders[i];
|
||||
|
||||
if (readingName) {
|
||||
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
|
||||
// header names should not contain any whitespace, this is invalid
|
||||
// ignore what's been before
|
||||
headerName = "";
|
||||
continue;
|
||||
}
|
||||
if (c == ':') {
|
||||
if (!headerName.empty()) {
|
||||
readingName = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
headerName += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
// reading value:
|
||||
if (c == ' ' || c == '\t') {
|
||||
if (headerValue.empty()) {
|
||||
// skip leading whitespace
|
||||
continue;
|
||||
} else {
|
||||
// accumulate trailing whitespace
|
||||
trailingWhitespace += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\r' || c == '\n') {
|
||||
// not sure if RFC allows empty values, we'll ignore such
|
||||
if (!headerName.empty() && !headerValue.empty()) {
|
||||
// add header value
|
||||
// RFC allows header with the same name to be sent multiple times
|
||||
// and requires it to be equivalent of just listing all header values separated with comma
|
||||
// so if header already was met, we'll add new value to the old one
|
||||
headerName.toLowercase();
|
||||
if (headers.contains(headerName)) {
|
||||
headers[headerName] += "," + headerValue;
|
||||
} else {
|
||||
headers[headerName] = headerValue;
|
||||
}
|
||||
}
|
||||
|
||||
headerName = "";
|
||||
headerValue = "";
|
||||
trailingWhitespace = "";
|
||||
readingName = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we meet non-whitespace character, turns out those "trailing" whitespace characters were not so trailing
|
||||
headerValue += trailingWhitespace;
|
||||
trailingWhitespace = "";
|
||||
headerValue += c;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
|
||||
uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
|
||||
if (sendSize > maxSize)
|
||||
|
|
|
@ -38,6 +38,7 @@ class NetworkReadStream: public Common::ReadStream {
|
|||
CURL *_easy;
|
||||
Common::MemoryReadWriteStream _backingStream;
|
||||
bool _eos, _requestComplete;
|
||||
char *_errorBuffer;
|
||||
const byte *_sendingContentsBuffer;
|
||||
uint32 _sendingContentsSize;
|
||||
uint32 _sendingContentsPos;
|
||||
|
@ -111,7 +112,7 @@ public:
|
|||
*
|
||||
* @note It's called on failure too.
|
||||
*/
|
||||
void finished();
|
||||
void finished(uint32 errorCode);
|
||||
|
||||
/**
|
||||
* Returns HTTP response code from inner CURL handle.
|
||||
|
@ -136,6 +137,14 @@ public:
|
|||
*/
|
||||
Common::String responseHeaders() const;
|
||||
|
||||
/**
|
||||
* Return response headers as HashMap. All header names in
|
||||
* it are lowercase.
|
||||
*
|
||||
* @note This method should be called when eos() == true.
|
||||
*/
|
||||
Common::HashMap<Common::String, Common::String> responseHeadersMap() const;
|
||||
|
||||
/** Returns a number in range [0, 1], where 1 is "complete". */
|
||||
double getProgress() const;
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
namespace Networking {
|
||||
|
||||
ErrorResponse::ErrorResponse(Request *rq):
|
||||
request(rq), interrupted(false), failed(true), response(""), httpResponseCode(-1) {}
|
||||
ErrorResponse::ErrorResponse(Request *rq, Common::String resp):
|
||||
request(rq), interrupted(false), failed(true), response(resp), 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) {}
|
||||
|
@ -50,7 +50,7 @@ void Request::handleRetry() {
|
|||
void Request::pause() { _state = PAUSED; }
|
||||
|
||||
void Request::finish() {
|
||||
ErrorResponse error(this, true, false, "", -1);
|
||||
ErrorResponse error(this, true, false, "Request::finish() was called (i.e. interrupted)", -1);
|
||||
finishError(error);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,12 +78,12 @@ struct ErrorResponse {
|
|||
Common::String response;
|
||||
long httpResponseCode;
|
||||
|
||||
ErrorResponse(Request *rq);
|
||||
ErrorResponse(Request *rq, Common::String resp);
|
||||
ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode);
|
||||
};
|
||||
|
||||
typedef Response<void *> DataReponse;
|
||||
typedef Common::BaseCallback<DataReponse> *DataCallback;
|
||||
typedef Response<void *> DataResponse;
|
||||
typedef Common::BaseCallback<DataResponse> *DataCallback;
|
||||
typedef Common::BaseCallback<ErrorResponse> *ErrorCallback;
|
||||
|
||||
/**
|
||||
|
|
|
@ -107,7 +107,7 @@ const char *GetClientHandler::responseMessage(long responseCode) {
|
|||
|
||||
void GetClientHandler::prepareHeaders() {
|
||||
if (!_specialHeaders.contains("Content-Type"))
|
||||
setHeader("Content-Type", "text/html");
|
||||
setHeader("Content-Type", "text/html; charset=UTF-8");
|
||||
|
||||
if (!_specialHeaders.contains("Content-Length") && _stream)
|
||||
setHeader("Content-Length", Common::String::format("%u", _stream->size()));
|
||||
|
|
|
@ -57,35 +57,35 @@ void CreateDirectoryHandler::handle(Client &client) {
|
|||
|
||||
// check that <path> is not an absolute root
|
||||
if (path == "" || path == "/") {
|
||||
handleError(client, _("Can't create directory here!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Can't create directory here!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
handleError(client, _("Invalid path!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
|
||||
handleError(client, _("Invalid path!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
handleError(client, _("Invalid path!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
handleError(client, _("Parent directory doesn't exists!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Parent directory doesn't exists!")));
|
||||
return;
|
||||
}
|
||||
if (!node->isDirectory()) {
|
||||
handleError(client, _("Can't create a directory within a file!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("Can't create a directory within a file!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -95,20 +95,20 @@ void CreateDirectoryHandler::handle(Client &client) {
|
|||
node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
|
||||
if (node->exists()) {
|
||||
if (!node->isDirectory()) {
|
||||
handleError(client, _("There is a file with that name in the parent directory!"));
|
||||
handleError(client, HandlerUtils::toUtf8(_("There is a file with that name in the parent directory!")));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// create the <directory_name> in <path>
|
||||
if (!node->create(true)) {
|
||||
handleError(client, _("Failed to create the directory!"));
|
||||
if (!node->createDirectory()) {
|
||||
handleError(client, HandlerUtils::toUtf8(_("Failed to create the directory!")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if json requested, respond with it
|
||||
if (client.queryParameter("answer_json") == "true") {
|
||||
setJsonResponseHandler(client, "success", _("Directory created successfully!"));
|
||||
setJsonResponseHandler(client, "success", HandlerUtils::toUtf8(_("Directory created successfully!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -117,9 +117,9 @@ void CreateDirectoryHandler::handle(Client &client) {
|
|||
client,
|
||||
Common::String::format(
|
||||
"%s<br/><a href=\"files?path=%s\">%s</a>",
|
||||
_("Directory created successfully!"),
|
||||
HandlerUtils::toUtf8(_("Directory created successfully!")).c_str(),
|
||||
client.queryParameter("path").c_str(),
|
||||
_("Back to parent directory")
|
||||
HandlerUtils::toUtf8(_("Back to parent directory")).c_str()
|
||||
),
|
||||
(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
|
||||
LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
|
||||
|
|
|
@ -40,40 +40,40 @@ void DownloadFileHandler::handle(Client &client) {
|
|||
|
||||
// check that <path> is not an absolute root
|
||||
if (path == "" || path == "/") {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The file doesn't exist!")));
|
||||
return;
|
||||
}
|
||||
if (node->isDirectory()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't download a directory!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't download a directory!")));
|
||||
return;
|
||||
}
|
||||
Common::SeekableReadStream *stream = node->createReadStream();
|
||||
if (stream == nullptr) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to read the file!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Failed to read the file!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ void FilesAjaxPageHandler::handle(Client &client) {
|
|||
// load stylish response page from the archive
|
||||
Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
|
||||
if (stream == nullptr) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The page is not available without the resources."));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The page is not available without the resources. Make sure file wwwroot.zip from ResidualVM distribution is available in 'themepath'.")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -64,16 +64,16 @@ void FilesAjaxPageHandler::handle(Client &client) {
|
|||
Common::String path = client.queryParameter("path");
|
||||
|
||||
//these occur twice:
|
||||
replace(response, "{create_directory_button}", _("Create directory"));
|
||||
replace(response, "{create_directory_button}", _("Create directory"));
|
||||
replace(response, "{upload_files_button}", _("Upload files")); //tab
|
||||
replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
|
||||
replace(response, "{create_directory_desc}", _("Type new directory name:"));
|
||||
replace(response, "{upload_file_desc}", _("Select a file to upload:"));
|
||||
replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
|
||||
replace(response, "{index_of}", _("Index of "));
|
||||
replace(response, "{loading}", _("Loading..."));
|
||||
replace(response, "{error}", _("Error occurred"));
|
||||
replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
|
||||
replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
|
||||
replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
|
||||
replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
|
||||
replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
|
||||
replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
|
||||
replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("Or select a directory (works in Chrome only):")));
|
||||
replace(response, "{index_of}", HandlerUtils::toUtf8(_("Index of ")));
|
||||
replace(response, "{loading}", HandlerUtils::toUtf8(("Loading...")));
|
||||
replace(response, "{error}", HandlerUtils::toUtf8(_("Error occurred")));
|
||||
replace(response, "{start_path}", encodeDoubleQuotesAndSlashes(path));
|
||||
LocalWebserver::setClientGetHandler(client, response);
|
||||
}
|
||||
|
|
|
@ -78,8 +78,8 @@ Common::String getDisplayPath(Common::String s) {
|
|||
bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
|
||||
if (path == "" || path == "/") {
|
||||
if (ConfMan.hasKey("rootpath", "cloud"))
|
||||
addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root"));
|
||||
addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games"));
|
||||
addItem(content, itemTemplate, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
|
||||
addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
|
|||
filePath = "/";
|
||||
else
|
||||
filePath = parentPath(prefixToAdd + filePath);
|
||||
addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
|
||||
addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, HandlerUtils::toUtf8(_("Parent directory")));
|
||||
}
|
||||
|
||||
// fill the content
|
||||
|
@ -182,7 +182,7 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
|
|||
void FilesPageHandler::handle(Client &client) {
|
||||
Common::String response =
|
||||
"<html>" \
|
||||
"<head><title>ScummVM</title></head>" \
|
||||
"<head><title>ResidualVM</title><meta charset=\"utf-8\"/></head>" \
|
||||
"<body>" \
|
||||
"<p>{create_directory_desc}</p>" \
|
||||
"<form action=\"create\">" \
|
||||
|
@ -215,21 +215,21 @@ void FilesPageHandler::handle(Client &client) {
|
|||
|
||||
// show an error message if failed to list directory
|
||||
if (!listDirectory(path, content, itemTemplate)) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("ResidualVM couldn't list the directory you specified."));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("ResidualVM couldn't list the directory you specified.")));
|
||||
return;
|
||||
}
|
||||
|
||||
//these occur twice:
|
||||
replace(response, "{create_directory_button}", _("Create directory"));
|
||||
replace(response, "{create_directory_button}", _("Create directory"));
|
||||
replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
|
||||
replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
|
||||
replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
|
||||
replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
|
||||
replace(response, "{upload_files_button}", _("Upload files")); //tab
|
||||
replace(response, "{upload_file_button}", _("Upload files")); //button in the tab
|
||||
replace(response, "{create_directory_desc}", _("Type new directory name:"));
|
||||
replace(response, "{upload_file_desc}", _("Select a file to upload:"));
|
||||
replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):"));
|
||||
replace(response, "{index_of_directory}", Common::String::format(_("Index of %s"), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
|
||||
replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
|
||||
replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
|
||||
replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
|
||||
replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
|
||||
replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("Or select a directory (works in Chrome only):")));
|
||||
replace(response, "{index_of_directory}", Common::String::format("%s %s", HandlerUtils::toUtf8(_("Index of")).c_str(), encodeHtmlEntities(getDisplayPath(client.queryParameter("path"))).c_str()));
|
||||
replace(response, "{content}", content);
|
||||
LocalWebserver::setClientGetHandler(client, response);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "backends/networking/sdl_net/handlerutils.h"
|
||||
#include "backends/networking/sdl_net/localwebserver.h"
|
||||
#include "common/translation.h"
|
||||
#include "gui/storagewizarddialog.h"
|
||||
|
||||
namespace Networking {
|
||||
|
||||
|
@ -34,28 +33,17 @@ IndexPageHandler::~IndexPageHandler() {}
|
|||
|
||||
/// public
|
||||
|
||||
Common::String IndexPageHandler::code() const { return _code; }
|
||||
|
||||
void IndexPageHandler::handle(Client &client) {
|
||||
Common::String queryCode = client.queryParameter("code");
|
||||
|
||||
if (queryCode == "") {
|
||||
// redirect to "/filesAJAX"
|
||||
HandlerUtils::setMessageHandler(
|
||||
client,
|
||||
Common::String::format(
|
||||
"%s<br/><a href=\"files\">%s</a>",
|
||||
_("This is a local webserver index page."),
|
||||
_("Open Files manager")
|
||||
),
|
||||
"/filesAJAX"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_code = queryCode;
|
||||
sendCommand(GUI::kStorageCodePassedCmd, 0);
|
||||
HandlerUtils::setMessageHandler(client, _("ResidualVM got the code and already connects to your cloud storage!"));
|
||||
// redirect to "/filesAJAX"
|
||||
HandlerUtils::setMessageHandler(
|
||||
client,
|
||||
Common::String::format(
|
||||
"%s<br/><a href=\"files\">%s</a>",
|
||||
HandlerUtils::toUtf8(_("This is a local webserver index page.")).c_str(),
|
||||
HandlerUtils::toUtf8(_("Open Files manager")).c_str()
|
||||
),
|
||||
"/filesAJAX"
|
||||
);
|
||||
}
|
||||
|
||||
bool IndexPageHandler::minimalModeSupported() {
|
||||
|
|
|
@ -30,12 +30,10 @@ namespace Networking {
|
|||
class LocalWebserver;
|
||||
|
||||
class IndexPageHandler: public BaseHandler, public GUI::CommandSender {
|
||||
Common::String _code;
|
||||
public:
|
||||
IndexPageHandler();
|
||||
virtual ~IndexPageHandler();
|
||||
|
||||
Common::String code() const;
|
||||
virtual void handle(Client &client);
|
||||
virtual bool minimalModeSupported();
|
||||
};
|
||||
|
|
|
@ -42,8 +42,8 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
|
|||
|
||||
if (path == "" || path == "/") {
|
||||
if (ConfMan.hasKey("rootpath", "cloud"))
|
||||
addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root"));
|
||||
addItem(itemsList, IT_DIRECTORY, "/saves/", _("Saved games"));
|
||||
addItem(itemsList, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
|
||||
addItem(itemsList, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
|
||||
successResult.setVal("items", new Common::JSONValue(itemsList));
|
||||
return successResult;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
|
|||
filePath = "/";
|
||||
else
|
||||
filePath = parentPath(prefixToAdd + filePath);
|
||||
addItem(itemsList, IT_PARENT_DIRECTORY, filePath, _("Parent directory"));
|
||||
addItem(itemsList, IT_PARENT_DIRECTORY, filePath, HandlerUtils::toUtf8(_("Parent directory")));
|
||||
}
|
||||
|
||||
// fill the content
|
||||
|
|
|
@ -40,35 +40,35 @@ void UploadFileHandler::handle(Client &client) {
|
|||
|
||||
// check that <path> is not an absolute root
|
||||
if (path == "" || path == "/" || path == "\\") {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The parent directory doesn't exist!")));
|
||||
return;
|
||||
}
|
||||
if (!node->isDirectory()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!"));
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't upload into a file!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,8 +171,13 @@ bool HandlerUtils::permittedPath(const Common::String path) {
|
|||
return hasPermittedPrefix(path) && !isBlacklisted(path);
|
||||
}
|
||||
|
||||
Common::String HandlerUtils::toUtf8(const char *text) {
|
||||
// FIXME: Convert the GUI to use UTF8
|
||||
return Common::String(text);
|
||||
}
|
||||
|
||||
void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
|
||||
Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
|
||||
Common::String response = "<html><head><title>ResidualVM</title><meta charset=\"utf-8\"/></head><body>{message}</body></html>";
|
||||
|
||||
// load stylish response page from the archive
|
||||
Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
|
||||
|
@ -194,7 +199,7 @@ void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::St
|
|||
message.c_str(),
|
||||
client.queryParameter("ajax") == "true" ? "AJAX" : "",
|
||||
"%2F", //that's encoded "/"
|
||||
_("Back to the files manager")
|
||||
toUtf8(_("Back to the files manager")).c_str()
|
||||
),
|
||||
redirectTo
|
||||
);
|
||||
|
|
|
@ -41,6 +41,8 @@ public:
|
|||
static bool hasPermittedPrefix(const Common::String &path);
|
||||
static bool permittedPath(const Common::String path);
|
||||
|
||||
static Common::String toUtf8(const char*);
|
||||
|
||||
static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
|
||||
static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
|
||||
};
|
||||
|
|
|
@ -111,7 +111,7 @@ class Reader {
|
|||
uint32 bytesLeft() const;
|
||||
|
||||
public:
|
||||
static const uint32 SUSPICIOUS_HEADERS_SIZE = 1024 * 1024; // 1 MB is really a lot
|
||||
static const int32 SUSPICIOUS_HEADERS_SIZE = 1024 * 1024; // 1 MB is really a lot
|
||||
|
||||
Reader();
|
||||
~Reader();
|
||||
|
|
|
@ -66,7 +66,7 @@ void UploadFileClientHandler::handle(Client *client) {
|
|||
|
||||
// fail on suspicious headers
|
||||
if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
|
||||
setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid request: headers are too long!")));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -108,7 +108,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
|
|||
|
||||
// fail on suspicious headers
|
||||
if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) {
|
||||
setErrorMessageHandler(*client, _("Invalid request: headers are too long!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid request: headers are too long!")));
|
||||
}
|
||||
|
||||
// search for "upload_file" field
|
||||
|
@ -134,11 +134,11 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
|
|||
path += '/';
|
||||
AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
|
||||
if (!HandlerUtils::permittedPath(originalNode->getPath())) {
|
||||
setErrorMessageHandler(*client, _("Invalid path!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid path!")));
|
||||
return;
|
||||
}
|
||||
if (originalNode->exists()) {
|
||||
setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("There is a file with that name in the parent directory!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
|
|||
Common::DumpFile *f = new Common::DumpFile();
|
||||
if (!f->open(originalNode->getPath(), true)) {
|
||||
delete f;
|
||||
setErrorMessageHandler(*client, _("Failed to upload the file!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Failed to upload the file!")));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
|
|||
if (client->noMoreContent()) {
|
||||
// if no file field was found - failure
|
||||
if (_uploadedFiles == 0) {
|
||||
setErrorMessageHandler(*client, _("No file was passed!"));
|
||||
setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("No file was passed!")));
|
||||
} else {
|
||||
setSuccessHandler(*client);
|
||||
}
|
||||
|
@ -199,9 +199,9 @@ void UploadFileClientHandler::setSuccessHandler(Client &client) {
|
|||
client,
|
||||
Common::String::format(
|
||||
"%s<br/><a href=\"files?path=%s\">%s</a>",
|
||||
_("Uploaded successfully!"),
|
||||
HandlerUtils::toUtf8(_("Uploaded successfully!")).c_str(),
|
||||
client.queryParameter("path").c_str(),
|
||||
_("Back to parent directory")
|
||||
HandlerUtils::toUtf8(_("Back to parent directory")).c_str()
|
||||
),
|
||||
(client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
|
||||
LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))
|
||||
|
|
|
@ -374,7 +374,7 @@ void OSystem_Android::initBackend() {
|
|||
|
||||
gettimeofday(&_startTime, 0);
|
||||
|
||||
_mixer = new Audio::MixerImpl(this, _audio_sample_rate);
|
||||
_mixer = new Audio::MixerImpl(_audio_sample_rate);
|
||||
_mixer->setReady(true);
|
||||
|
||||
_timer_thread_exit = false;
|
||||
|
|
|
@ -10,15 +10,21 @@ amigaosdist: $(EXECUTABLE)
|
|||
ifdef DIST_FILES_ENGINEDATA
|
||||
cp $(DIST_FILES_ENGINEDATA) $(AMIGAOSPATH)/extras/
|
||||
endif
|
||||
cat ${srcdir}/README | sed -f ${srcdir}/dists/amiga/convertRM.sed > README.conv
|
||||
ifdef DIST_FILES_NETWORKING
|
||||
cp $(DIST_FILES_NETWORKING) $(AMIGAOSPATH)/extras/
|
||||
endif
|
||||
ifdef DIST_FILES_VKEYBD
|
||||
cp $(DIST_FILES_VKEYBD) $(AMIGAOSPATH)/extras/
|
||||
endif
|
||||
cat ${srcdir}/README.md | sed -f ${srcdir}/dists/amiga/convertRM.sed > README.conv
|
||||
# AmigaOS's shell is not happy with indented comments, thus don't do it.
|
||||
# AREXX seems to have problems when ${srcdir} is '.'. It will break with a
|
||||
# "Program not found" error. Therefore we copy the script to the cwd and
|
||||
# remove it again, once it has finished.
|
||||
cp ${srcdir}/dists/amiga/RM2AG.rx .
|
||||
rx RM2AG.rx README.conv
|
||||
cp ${srcdir}/dists/amiga/RM2AG.rexx .
|
||||
rx RM2AG.rexx README.conv
|
||||
cp README.guide $(AMIGAOSPATH)
|
||||
rm RM2AG.rx
|
||||
rm RM2AG.rexx
|
||||
rm README.conv
|
||||
rm README.guide
|
||||
cp $(DIST_FILES_DOCS) $(AMIGAOSPATH)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "backends/taskbar/macosx/macosx-taskbar.h"
|
||||
#include "backends/dialogs/macosx/macosx-dialogs.h"
|
||||
#include "backends/platform/sdl/macosx/macosx_wrapper.h"
|
||||
#include "backends/fs/posix/posix-fs.h"
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/config-manager.h"
|
||||
|
@ -199,6 +200,19 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
|
|||
#endif // USE_DETECTLANG
|
||||
}
|
||||
|
||||
Common::String OSystem_MacOSX::getDefaultLogFileName() {
|
||||
const char *prefix = getenv("HOME");
|
||||
if (prefix == nullptr) {
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
if (!Posix::assureDirectoryExists("Library/Logs", prefix)) {
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
return Common::String(prefix) + "/Library/Logs/scummvm.log";
|
||||
}
|
||||
|
||||
Common::String OSystem_MacOSX::getScreenshotsPath() {
|
||||
Common::String path = ConfMan.get("screenshotpath");
|
||||
if (path.empty())
|
||||
|
|
|
@ -50,6 +50,8 @@ public:
|
|||
virtual Common::String getScreenshotsPath();
|
||||
|
||||
protected:
|
||||
virtual Common::String getDefaultLogFileName();
|
||||
|
||||
// Override createAudioCDManager() to get our Mac-specific
|
||||
// version.
|
||||
virtual AudioCDManager *createAudioCDManager();
|
||||
|
|
|
@ -269,55 +269,29 @@ void OSystem_POSIX::addSysArchivesToSearchSet(Common::SearchSet &s, int priority
|
|||
OSystem_SDL::addSysArchivesToSearchSet(s, priority);
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_POSIX::createLogFile() {
|
||||
// Start out by resetting _logFilePath, so that in case
|
||||
// of a failure, we know that no log file is open.
|
||||
_logFilePath.clear();
|
||||
|
||||
const char *prefix = nullptr;
|
||||
Common::String OSystem_POSIX::getDefaultLogFileName() {
|
||||
Common::String logFile;
|
||||
#ifdef MACOSX
|
||||
prefix = getenv("HOME");
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
logFile = "Library/Logs";
|
||||
#elif SAMSUNGTV
|
||||
prefix = nullptr;
|
||||
logFile = "/mtd_ram";
|
||||
#else
|
||||
// On POSIX systems we follow the XDG Base Directory Specification for
|
||||
// where to store files. The version we based our code upon can be found
|
||||
// over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
|
||||
prefix = getenv("XDG_CACHE_HOME");
|
||||
const char *prefix = getenv("XDG_CACHE_HOME");
|
||||
if (prefix == nullptr || !*prefix) {
|
||||
prefix = getenv("HOME");
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
logFile = ".cache/";
|
||||
}
|
||||
|
||||
logFile += "residualvm/logs";
|
||||
#endif
|
||||
|
||||
if (!Posix::assureDirectoryExists(logFile, prefix)) {
|
||||
return 0;
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
logFile = Common::String::format("%s/%s", prefix, logFile.c_str());
|
||||
}
|
||||
|
||||
logFile += "/residualvm.log";
|
||||
|
||||
Common::FSNode file(logFile);
|
||||
Common::WriteStream *stream = file.createWriteStream();
|
||||
if (stream)
|
||||
_logFilePath = logFile;
|
||||
return stream;
|
||||
return Common::String::format("%s/%s/residualvm.log", prefix, logFile.c_str());
|
||||
}
|
||||
|
||||
bool OSystem_POSIX::displayLogFile() {
|
||||
|
|
|
@ -52,19 +52,8 @@ protected:
|
|||
*/
|
||||
Common::String _baseConfigName;
|
||||
|
||||
/**
|
||||
* The path of the currently open log file, if any.
|
||||
*
|
||||
* @note This is currently a string and not an FSNode for simplicity;
|
||||
* e.g. we don't need to include fs.h here, and currently the
|
||||
* only use of this value is to use it to open the log file in an
|
||||
* editor; for that, we need it only as a string anyway.
|
||||
*/
|
||||
Common::String _logFilePath;
|
||||
|
||||
virtual Common::String getDefaultConfigFileName();
|
||||
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
virtual Common::String getDefaultLogFileName();
|
||||
|
||||
Common::String getXdgUserDir(const char *name);
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ Common::String OSystem_PS3::getDefaultConfigFileName() {
|
|||
return PREFIX "/" + _baseConfigName;
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_PS3::createLogFile() {
|
||||
Common::FSNode file(PREFIX "/residualvm.log");
|
||||
return file.createWriteStream();
|
||||
Common::String OSystem_PS3::getDefaultLogFileName() {
|
||||
return PREFIX "/residualvm.log";
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ protected:
|
|||
Common::String _baseConfigName;
|
||||
|
||||
virtual Common::String getDefaultConfigFileName();
|
||||
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
virtual Common::String getDefaultLogFileName();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,11 +8,15 @@ ps3pkg: $(EXECUTABLE)
|
|||
cp $(DIST_FILES_THEMES) ps3pkg/USRDIR/data/
|
||||
ifdef DIST_FILES_ENGINEDATA
|
||||
cp $(DIST_FILES_ENGINEDATA) ps3pkg/USRDIR/data/
|
||||
endif
|
||||
ifdef DIST_FILES_NETWORKING
|
||||
cp $(DIST_FILES_NETWORKING) ps3pkg/USRDIR/data/
|
||||
endif
|
||||
ifdef DIST_FILES_VKEYBD
|
||||
cp $(DIST_FILES_VKEYBD) ps3pkg/USRDIR/data/
|
||||
endif
|
||||
cp $(DIST_FILES_DOCS) ps3pkg/USRDIR/doc/
|
||||
cp $(srcdir)/dists/ps3/readme-ps3.md ps3pkg/USRDIR/doc/
|
||||
cp $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip ps3pkg/USRDIR/data/
|
||||
cp $(srcdir)/backends/vkeybd/packs/vkeybd_small.zip ps3pkg/USRDIR/data/
|
||||
cp $(srcdir)/dists/ps3/ICON0.PNG ps3pkg/
|
||||
sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO
|
||||
pkg.py --contentid UP0001-RESI12000_00-0000000000000000 ps3pkg/ residualvm-ps3.pkg
|
||||
|
|
|
@ -119,24 +119,14 @@ Common::String OSystem_RISCOS::getDefaultConfigFileName() {
|
|||
return "/<Choices$Write>/ResidualVM/residualvm";
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_RISCOS::createLogFile() {
|
||||
// Start out by resetting _logFilePath, so that in case
|
||||
// of a failure, we know that no log file is open.
|
||||
_logFilePath.clear();
|
||||
|
||||
Common::String OSystem_RISCOS::getDefaultLogFileName() {
|
||||
Common::String logFile = "/<Choices$Write>/ResidualVM/Logs";
|
||||
|
||||
if (!Riscos::assureDirectoryExists(logFile)) {
|
||||
return 0;
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
logFile += "/residualvm";
|
||||
|
||||
Common::FSNode file(logFile);
|
||||
Common::WriteStream *stream = file.createWriteStream();
|
||||
if (stream)
|
||||
_logFilePath = logFile;
|
||||
return stream;
|
||||
return logFile + "/residualvm";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,18 +37,8 @@ public:
|
|||
virtual void logMessage(LogMessageType::Type type, const char *message);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The path of the currently open log file, if any.
|
||||
*
|
||||
* @note This is currently a string and not an FSNode for simplicity;
|
||||
* e.g. we don't need to include fs.h here, and currently the
|
||||
* only use of this value is to use it to open the log file in an
|
||||
* editor; for that, we need it only as a string anyway.
|
||||
*/
|
||||
Common::String _logFilePath;
|
||||
|
||||
virtual Common::String getDefaultConfigFileName();
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
virtual Common::String getDefaultLogFileName();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ifeq ($(shell echo a | iconv --to-code=RISCOS-LATIN1//TRANSLIT >/dev/null 2>&1; echo $$?),0)
|
||||
ENCODING=RISCOS-LATIN1//TRANSLIT
|
||||
ifeq ($(shell echo a | iconv --to-code=RISCOS-LATIN1//IGNORE//TRANSLIT >/dev/null 2>&1; echo $$?),0)
|
||||
ENCODING=RISCOS-LATIN1//IGNORE//TRANSLIT
|
||||
else
|
||||
ENCODING=ISO-8859-1//TRANSLIT
|
||||
ENCODING=ISO-8859-1//IGNORE//TRANSLIT
|
||||
endif
|
||||
|
||||
APP_NAME=!ResidualVM
|
||||
|
@ -27,6 +27,9 @@ ifdef USE_OPENGL_SHADERS
|
|||
mkdir -p $(APP_NAME)/data/shaders
|
||||
cp $(DIST_FILES_SHADERS) $(APP_NAME)/data/shaders/
|
||||
endif
|
||||
ifdef DIST_FILES_VKEYBD
|
||||
cp $(DIST_FILES_VKEYBD) $(APP_NAME)/data/
|
||||
endif
|
||||
ifdef DYNAMIC_MODULES
|
||||
mkdir -p $(APP_NAME)/plugins
|
||||
cp $(PLUGINS) $(APP_NAME)/plugins/
|
||||
|
|
|
@ -191,8 +191,6 @@ void OSystem_SDL::initBackend() {
|
|||
sdlDriverName[0] = '\0';
|
||||
SDL_VideoDriverName(sdlDriverName, maxNameLen);
|
||||
#endif
|
||||
// Using printf rather than debug() here as debug()/logging
|
||||
// is not active by this point.
|
||||
debug(1, "Using SDL Video Driver \"%s\"", sdlDriverName);
|
||||
|
||||
// ResidualVM specific code start
|
||||
|
@ -384,7 +382,7 @@ void OSystem_SDL::initSDL() {
|
|||
if (ConfMan.hasKey("disable_sdl_parachute"))
|
||||
sdlFlags |= SDL_INIT_NOPARACHUTE;
|
||||
|
||||
// Initialize SDL (SDL Subsystems are initiliazed in the corresponding sdl managers)
|
||||
// Initialize SDL (SDL Subsystems are initialized in the corresponding sdl managers)
|
||||
if (SDL_Init(sdlFlags) == -1)
|
||||
error("Could not initialize SDL: %s", SDL_GetError());
|
||||
|
||||
|
@ -504,6 +502,22 @@ void OSystem_SDL::logMessage(LogMessageType::Type type, const char *message) {
|
|||
_logger->print(message);
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_SDL::createLogFile() {
|
||||
// Start out by resetting _logFilePath, so that in case
|
||||
// of a failure, we know that no log file is open.
|
||||
_logFilePath.clear();
|
||||
|
||||
Common::String logFile = getDefaultLogFileName();
|
||||
if (logFile.empty())
|
||||
return nullptr;
|
||||
|
||||
Common::FSNode file(logFile);
|
||||
Common::WriteStream *stream = file.createWriteStream();
|
||||
if (stream)
|
||||
_logFilePath = logFile;
|
||||
return stream;
|
||||
}
|
||||
|
||||
Common::String OSystem_SDL::getSystemLanguage() const {
|
||||
#if defined(USE_DETECTLANG) && !defined(WIN32)
|
||||
// Activating current locale settings
|
||||
|
@ -538,18 +552,14 @@ Common::String OSystem_SDL::getSystemLanguage() const {
|
|||
#endif // USE_DETECTLANG
|
||||
}
|
||||
|
||||
bool OSystem_SDL::hasTextInClipboard() {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
bool OSystem_SDL::hasTextInClipboard() {
|
||||
return SDL_HasClipboardText() == SDL_TRUE;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Common::String OSystem_SDL::getTextFromClipboard() {
|
||||
if (!hasTextInClipboard()) return "";
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
char *text = SDL_GetClipboardText();
|
||||
// The string returned by SDL is in UTF-8. Convert to the
|
||||
// current TranslationManager encoding or ISO-8859-1.
|
||||
|
@ -566,13 +576,9 @@ Common::String OSystem_SDL::getTextFromClipboard() {
|
|||
SDL_free(text);
|
||||
|
||||
return strText;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OSystem_SDL::setTextInClipboard(const Common::String &text) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
// The encoding we need to use is UTF-8. Assume we currently have the
|
||||
// current TranslationManager encoding or ISO-8859-1.
|
||||
#ifdef USE_TRANSLATION
|
||||
|
@ -586,10 +592,8 @@ bool OSystem_SDL::setTextInClipboard(const Common::String &text) {
|
|||
return status == 0;
|
||||
}
|
||||
return SDL_SetClipboardText(text.c_str()) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32 OSystem_SDL::getMillis(bool skipRecord) {
|
||||
uint32 millis = SDL_GetTicks();
|
||||
|
|
|
@ -70,10 +70,12 @@ public:
|
|||
|
||||
virtual Common::String getSystemLanguage() const;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
// Clipboard
|
||||
virtual bool hasTextInClipboard();
|
||||
virtual Common::String getTextFromClipboard();
|
||||
virtual bool setTextInClipboard(const Common::String &text);
|
||||
#endif
|
||||
|
||||
virtual void setWindowCaption(const char *caption);
|
||||
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
|
||||
|
@ -101,6 +103,16 @@ protected:
|
|||
bool _initedSDLnet;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The path of the currently open log file, if any.
|
||||
*
|
||||
* @note This is currently a string and not an FSNode for simplicity;
|
||||
* e.g. we don't need to include fs.h here, and currently the
|
||||
* only use of this value is to use it to open the log file in an
|
||||
* editor; for that, we need it only as a string anyway.
|
||||
*/
|
||||
Common::String _logFilePath;
|
||||
|
||||
/**
|
||||
* Mixer manager that configures and setups SDL for
|
||||
* the wrapped Audio::Mixer, the true mixer.
|
||||
|
@ -138,7 +150,8 @@ protected:
|
|||
virtual AudioCDManager *createAudioCDManager();
|
||||
|
||||
// Logging
|
||||
virtual Common::WriteStream *createLogFile() { return 0; }
|
||||
virtual Common::String getDefaultLogFileName() { return Common::String(); }
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
Backends::Log::Log *_logger;
|
||||
};
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ void OSystem_Win32::initBackend() {
|
|||
|
||||
#if defined(USE_SPARKLE)
|
||||
// Initialize updates manager
|
||||
_updateManager = new Win32UpdateManager();
|
||||
_updateManager = new Win32UpdateManager((SdlWindow_Win32*)_window);
|
||||
#endif
|
||||
|
||||
// Invoke parent implementation of this method
|
||||
|
@ -278,31 +278,22 @@ Common::String OSystem_Win32::getDefaultConfigFileName() {
|
|||
return configFile;
|
||||
}
|
||||
|
||||
Common::WriteStream *OSystem_Win32::createLogFile() {
|
||||
// Start out by resetting _logFilePath, so that in case
|
||||
// of a failure, we know that no log file is open.
|
||||
_logFilePath.clear();
|
||||
|
||||
Common::String OSystem_Win32::getDefaultLogFileName() {
|
||||
char logFile[MAXPATHLEN];
|
||||
|
||||
// Use the Application Data directory of the user profile.
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, logFile) == S_OK) {
|
||||
strcat(logFile, "\\ResidualVM");
|
||||
CreateDirectory(logFile, NULL);
|
||||
strcat(logFile, "\\Logs");
|
||||
CreateDirectory(logFile, NULL);
|
||||
strcat(logFile, "\\residualvm.log");
|
||||
|
||||
Common::FSNode file(logFile);
|
||||
Common::WriteStream *stream = file.createWriteStream();
|
||||
if (stream)
|
||||
_logFilePath= logFile;
|
||||
|
||||
return stream;
|
||||
} else {
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, logFile) != S_OK) {
|
||||
warning("Unable to access application data directory");
|
||||
return 0;
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
strcat(logFile, "\\ResidualVM");
|
||||
CreateDirectory(logFile, NULL);
|
||||
strcat(logFile, "\\Logs");
|
||||
CreateDirectory(logFile, NULL);
|
||||
strcat(logFile, "\\residualvm.log");
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -46,20 +46,10 @@ public:
|
|||
virtual Common::String getScreenshotsPath();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The path of the currently open log file, if any.
|
||||
*
|
||||
* @note This is currently a string and not an FSNode for simplicity;
|
||||
* e.g. we don't need to include fs.h here, and currently the
|
||||
* only use of this value is to use it to open the log file in an
|
||||
* editor; for that, we need it only as a string anyway.
|
||||
*/
|
||||
Common::String _logFilePath;
|
||||
|
||||
virtual Common::String getDefaultConfigFileName();
|
||||
virtual Common::WriteStream *createLogFile();
|
||||
virtual Common::String getDefaultLogFileName();
|
||||
|
||||
// Override createAudioCDManager() to get our Mac-specific
|
||||
// Override createAudioCDManager() to get our Windows-specific
|
||||
// version.
|
||||
virtual AudioCDManager *createAudioCDManager();
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#
|
||||
|
||||
# ResidualVM: Added DIST_FILES_SHADERS
|
||||
residualvmwinres.o: $(srcdir)/icons/residualvm.ico $(DIST_FILES_THEMES) $(DIST_FILES_NETWORKING) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) $(srcdir)/dists/residualvm.rc
|
||||
$(QUIET_WINDRES)$(WINDRES) -DHAVE_CONFIG_H $(WINDRESFLAGS) $(DEFINES) -I. -I$(srcdir) $(srcdir)/dists/residualvm.rc residualvmwinres.o
|
||||
dists/residualvm.o: $(srcdir)/icons/residualvm.ico $(DIST_FILES_THEMES) $(DIST_FILES_NETWORKING) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) config.h $(srcdir)/base/internal_version.h
|
||||
|
||||
# Special target to create a win32 snapshot binary (for Inno Setup)
|
||||
win32dist: all
|
||||
mkdir -p $(WIN32PATH)
|
||||
mkdir -p $(WIN32PATH)/graphics
|
||||
mkdir -p $(WIN32PATH)/doc
|
||||
$(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE)
|
||||
cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#ifndef PLATFORM_SDL_WIN32_WRAPPER_H
|
||||
#define PLATFORM_SDL_WIN32_WRAPPER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
HRESULT SHGetFolderPathFunc(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
|
||||
|
||||
// Helper functions
|
||||
|
|
|
@ -63,7 +63,9 @@ DefaultSaveFileManager::DefaultSaveFileManager(const Common::String &defaultSave
|
|||
void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) {
|
||||
clearError();
|
||||
if (!dir.exists()) {
|
||||
setError(Common::kPathDoesNotExist, "The savepath '"+dir.getPath()+"' does not exist");
|
||||
if (!dir.createDirectory()) {
|
||||
setError(Common::kPathDoesNotExist, "Failed to create directory '"+dir.getPath()+"'");
|
||||
}
|
||||
} else if (!dir.isDirectory()) {
|
||||
setError(Common::kPathNotDirectory, "The savepath '"+dir.getPath()+"' is not a directory");
|
||||
}
|
||||
|
@ -169,6 +171,8 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
|
|||
|
||||
// Open the file for saving.
|
||||
Common::WriteStream *const sf = fileNode.createWriteStream();
|
||||
if (!sf)
|
||||
return nullptr;
|
||||
Common::OutSaveFile *const result = new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf);
|
||||
|
||||
// Add file to cache now that it exists.
|
||||
|
@ -358,7 +362,10 @@ void DefaultSaveFileManager::saveTimestamps(Common::HashMap<Common::String, uint
|
|||
}
|
||||
|
||||
for (Common::HashMap<Common::String, uint32>::iterator i = timestamps.begin(); i != timestamps.end(); ++i) {
|
||||
Common::String data = i->_key + Common::String::format(" %u\n", i->_value);
|
||||
uint32 v = i->_value;
|
||||
if (v < 1) v = 1; // 0 timestamp is treated as EOF up there, so we should never save zeros
|
||||
|
||||
Common::String data = i->_key + Common::String::format(" %u\n", v);
|
||||
if (f.write(data.c_str(), data.size()) != data.size()) {
|
||||
warning("DefaultSaveFileManager: failed to write timestamps data into '%s'", filename.c_str());
|
||||
return;
|
||||
|
|
|
@ -38,9 +38,6 @@
|
|||
#include "common/savefile.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
POSIXSaveFileManager::POSIXSaveFileManager() {
|
||||
|
@ -133,69 +130,4 @@ POSIXSaveFileManager::POSIXSaveFileManager() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void POSIXSaveFileManager::checkPath(const Common::FSNode &dir) {
|
||||
const Common::String path = dir.getPath();
|
||||
clearError();
|
||||
|
||||
struct stat sb;
|
||||
|
||||
// Check whether the dir exists
|
||||
if (stat(path.c_str(), &sb) == -1) {
|
||||
// The dir does not exist, or stat failed for some other reason.
|
||||
// If the problem was that the path pointed to nothing, try
|
||||
// to create the dir (ENOENT case).
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
setError(Common::kWritePermissionDenied, "Search or write permission denied: "+path);
|
||||
break;
|
||||
case ELOOP:
|
||||
setError(Common::kUnknownError, "Too many symbolic links encountered while traversing the path: "+path);
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
setError(Common::kUnknownError, "The path name is too long: "+path);
|
||||
break;
|
||||
case ENOENT:
|
||||
if (mkdir(path.c_str(), 0755) != 0) {
|
||||
// mkdir could fail for various reasons: The parent dir doesn't exist,
|
||||
// or is not writeable, the path could be completly bogus, etc.
|
||||
warning("mkdir for '%s' failed", path.c_str());
|
||||
perror("mkdir");
|
||||
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
setError(Common::kWritePermissionDenied, "Search or write permission denied: "+path);
|
||||
break;
|
||||
case EMLINK:
|
||||
setError(Common::kUnknownError, "The link count of the parent directory would exceed {LINK_MAX}: "+path);
|
||||
break;
|
||||
case ELOOP:
|
||||
setError(Common::kUnknownError, "Too many symbolic links encountered while traversing the path: "+path);
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
setError(Common::kUnknownError, "The path name is too long: "+path);
|
||||
break;
|
||||
case ENOENT:
|
||||
setError(Common::kPathDoesNotExist, "A component of the path does not exist, or the path is an empty string: "+path);
|
||||
break;
|
||||
case ENOTDIR:
|
||||
setError(Common::kPathDoesNotExist, "A component of the path prefix is not a directory: "+path);
|
||||
break;
|
||||
case EROFS:
|
||||
setError(Common::kWritePermissionDenied, "The parent directory resides on a read-only file system:"+path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ENOTDIR:
|
||||
setError(Common::kPathDoesNotExist, "A component of the path prefix is not a directory: "+path);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// So stat() succeeded. But is the path actually pointing to a directory?
|
||||
if (!S_ISDIR(sb.st_mode)) {
|
||||
setError(Common::kPathDoesNotExist, "The given savepath is not a directory: "+path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,14 +35,6 @@
|
|||
class POSIXSaveFileManager : public DefaultSaveFileManager {
|
||||
public:
|
||||
POSIXSaveFileManager();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Checks the given path for read access, existence, etc.
|
||||
* In addition, tries to create a missing savedir, if possible.
|
||||
* Sets the internal error and error message accordingly.
|
||||
*/
|
||||
virtual void checkPath(const Common::FSNode &dir);
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct TimerSlot {
|
|||
|
||||
TimerSlot *next;
|
||||
|
||||
TimerSlot() : refCon(0), interval(0), nextFireTime(0), nextFireTimeMicro(0), next(0) {}
|
||||
TimerSlot() : callback(nullptr), refCon(nullptr), interval(0), nextFireTime(0), nextFireTimeMicro(0), next(nullptr) {}
|
||||
};
|
||||
|
||||
void insertPrioQueue(TimerSlot *head, TimerSlot *newSlot) {
|
||||
|
|
|
@ -83,9 +83,6 @@ MacOSXUpdateManager::MacOSXUpdateManager() {
|
|||
// Set the target of the new menu item
|
||||
[updateMenuItem setTarget:sparkleUpdater];
|
||||
|
||||
// Finally give up our references to the objects
|
||||
[menuItem release];
|
||||
|
||||
if (!ConfMan.hasKey("updates_check")
|
||||
|| ConfMan.getInt("updates_check") == Common::UpdateManager::kUpdateIntervalNotSupported) {
|
||||
setAutomaticallyChecksForUpdates(kUpdateStateDisabled);
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
#include "backends/updates/win32/win32-updates.h"
|
||||
|
||||
#ifdef USE_SPARKLE
|
||||
#include "backends/platform/sdl/win32/win32-window.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <windows.h>
|
||||
#include <winsparkle.h>
|
||||
|
||||
/**
|
||||
|
@ -50,10 +52,15 @@
|
|||
* https://winsparkle.org/
|
||||
*
|
||||
*/
|
||||
Win32UpdateManager::Win32UpdateManager() {
|
||||
const char *appcastUrl = "https://www.residualvm.org/appcasts/macosx/release.xml";
|
||||
|
||||
win_sparkle_set_appcast_url(appcastUrl);
|
||||
static SdlWindow_Win32 *_window;
|
||||
|
||||
Win32UpdateManager::Win32UpdateManager(SdlWindow_Win32 *window) {
|
||||
_window = window;
|
||||
const char *appcastUrl = "https://www.scummvm.org/appcasts/macosx/release.xml";
|
||||
win_sparkle_set_appcast_url(appcastUrl);
|
||||
win_sparkle_set_can_shutdown_callback(canShutdownCallback);
|
||||
win_sparkle_set_shutdown_request_callback(shutdownRequestCallback);
|
||||
win_sparkle_init();
|
||||
|
||||
if (!ConfMan.hasKey("updates_check")
|
||||
|
@ -129,4 +136,20 @@ bool Win32UpdateManager::getLastUpdateCheckTimeAndDate(TimeDate &t) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// WinSparkle calls this to ask if we can shut down.
|
||||
// At this point the download has completed, the user has
|
||||
// selected Install Update, and the installer has started.
|
||||
// This callback runs on a non-main thread.
|
||||
int Win32UpdateManager::canShutdownCallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// WinSparkle calls this to request that we shut down.
|
||||
// This callback runs on a non-main thread so we post
|
||||
// a WM_CLOSE message to our window so that we exit
|
||||
// cleanly, as opposed to calling g_system->quit().
|
||||
void Win32UpdateManager::shutdownRequestCallback() {
|
||||
PostMessage(_window->getHwnd(), WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue