ALL: Sync with ScummVM rev. 55dba55056

This commit is contained in:
Bastien Bouclet 2019-12-07 15:31:33 +01:00
parent aaec28e12f
commit feaf9dc365
200 changed files with 13353 additions and 13638 deletions

View file

@ -126,7 +126,7 @@ endif
.PHONY: print-dists print-executables print-version print-distversion .PHONY: print-dists print-executables print-version print-distversion
print-dists: 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: print-executables:
@echo $(if $(DIST_EXECUTABLES),$(DIST_EXECUTABLES),$(EXECUTABLE) $(PLUGINS)) @echo $(if $(DIST_EXECUTABLES),$(DIST_EXECUTABLES),$(EXECUTABLE) $(PLUGINS))

View file

@ -138,6 +138,10 @@ ifdef CXX_UPDATE_DEP_FLAG
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR) $(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
$(QUIET_AS)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(ASFLAGS) -c $(<) -o $*.o $(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 else
# Dumb compile rule, for C++ compilers that don't allow dependency tracking or # Dumb compile rule, for C++ compilers that don't allow dependency tracking or
@ -151,6 +155,9 @@ else
$(QUIET)$(MKDIR) $(*D) $(QUIET)$(MKDIR) $(*D)
$(QUIET_AS)$(CXX) $(ASFLAGS) -c $(<) -o $*.o $(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 endif
# Build rule for assembler files # Build rule for assembler files
@ -158,6 +165,12 @@ endif
$(QUIET)$(MKDIR) $(*D) $(QUIET)$(MKDIR) $(*D)
$(QUIET_AS)$(AS) $(ASFLAGS) $(<) -o $*.o $(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 ifdef USE_NASM
# Build rule for NASM assembler files # Build rule for NASM assembler files
%.o: %.asm %.o: %.asm
@ -188,7 +201,7 @@ VER_EXTRA = $(shell echo $(VERSION) | cut -d. -f 3 | cut -c2-)
ifdef AMIGAOS ifdef AMIGAOS
# Amiga needs date in specific format for the version cookie # Amiga needs date in specific format for the version cookie
AMIGA_DATE = $(shell gdate '+%d.%m.%Y') AMIGA_DATE = $(shell gdate '+%d.%m.%Y')
base/version.o: CXXFLAGS:=$(CXXFLAGS) -DAMIGA_DATE=\"$(AMIGA_DATE)\" VERFLAGS += -DAMIGA_DATE=\"$(AMIGA_DATE)\"
endif endif
###################################################################### ######################################################################
@ -211,7 +224,7 @@ endif
# Define the Subversion revision if available, either autodetected or # Define the Subversion revision if available, either autodetected or
# specified by the user, but only for base/version.cpp. # specified by the user, but only for base/version.cpp.
ifneq ($(origin VER_REV), undefined) ifneq ($(origin VER_REV), undefined)
base/version.o: CXXFLAGS:=$(CXXFLAGS) -DSCUMMVM_REVISION=\"$(VER_REV)\" VERFLAGS += -DSCUMMVM_REVISION=\"$(VER_REV)\"
endif endif
###################################################################### ######################################################################

View file

@ -23,7 +23,6 @@
#include "gui/EventRecorder.h" #include "gui/EventRecorder.h"
#include "common/util.h" #include "common/util.h"
#include "common/system.h"
#include "common/textconsole.h" #include "common/textconsole.h"
#include "audio/mixer_intern.h" #include "audio/mixer_intern.h"
@ -173,8 +172,7 @@ private:
#pragma mark --- Mixer --- #pragma mark --- Mixer ---
#pragma mark - #pragma mark -
// TODO: parameter "system" is unused MixerImpl::MixerImpl(uint sampleRate)
MixerImpl::MixerImpl(OSystem *system, uint sampleRate)
: _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() { : _mutex(), _sampleRate(sampleRate), _mixerReady(false), _handleSeed(0), _soundTypeSettings() {
assert(sampleRate > 0); assert(sampleRate > 0);

View file

@ -73,7 +73,7 @@ private:
public: public:
MixerImpl(OSystem *system, uint sampleRate); MixerImpl(uint sampleRate);
~MixerImpl(); ~MixerImpl();
virtual bool isReady() const { return _mixerReady; } virtual bool isReady() const { return _mixerReady; }

View file

@ -57,7 +57,7 @@ void BaseBackend::initBackend() {
void BaseBackend::fillScreen(uint32 col) { void BaseBackend::fillScreen(uint32 col) {
Graphics::Surface *screen = lockScreen(); Graphics::Surface *screen = lockScreen();
if (screen && screen->getPixels()) if (screen)
memset(screen->getPixels(), col, screen->h * screen->pitch); screen->fillRect(Common::Rect(screen->w, screen->h), col);
unlockScreen(); unlockScreen();
} }

View file

@ -83,7 +83,7 @@ void BoxListDirectoryByIdRequest::responseCallback(Networking::JsonResponse resp
if (response.request) if (response.request)
_date = response.request->date(); _date = response.request->date();
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "BoxListDirectoryByIdRequest::responseCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -35,142 +35,34 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/json.h" #include "common/json.h"
#ifdef ENABLE_RELEASE
#include "dists/clouds/cloud_keys.h"
#endif
namespace Cloud { namespace Cloud {
namespace Box { 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_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_FILES_CONTENT "https://api.box.com/2.0/files/%s/content"
#define BOX_API_USERS_ME "https://api.box.com/2.0/users/me" #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 BoxStorage::BoxStorage(Common::String token, Common::String refreshToken, bool enabled):
char *BoxStorage::SECRET = nullptr; IdStorage(token, refreshToken, enabled) {}
void BoxStorage::loadKeyAndSecret() { BoxStorage::BoxStorage(Common::String code, Networking::ErrorCallback cb) {
#ifdef ENABLE_RELEASE getAccessToken(code, cb);
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() {} BoxStorage::~BoxStorage() {}
void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) { Common::String BoxStorage::cloudProvider() { return "box"; }
if (!KEY || !SECRET)
loadKeyAndSecret();
bool codeFlow = (code != "");
if (!codeFlow && _refreshToken == "") { uint32 BoxStorage::storageIndex() { return kStorageBoxId; }
warning("BoxStorage: no refresh token available to get new access token.");
if (callback) (*callback)(BoolResponse(nullptr, false));
return;
}
Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback); bool BoxStorage::needsRefreshToken() { return true; }
if (errorCallback == nullptr)
errorCallback = getErrorPrintingCallback();
Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, BOX_OAUTH2_TOKEN); bool BoxStorage::canReuseRefreshToken() { return false; }
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);
}
void BoxStorage::saveConfig(Common::String keyPrefix) { void BoxStorage::saveConfig(Common::String keyPrefix) {
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
saveIsEnabledFlag(keyPrefix);
} }
Common::String BoxStorage::name() const { Common::String BoxStorage::name() const {
@ -321,8 +213,6 @@ Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::
Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; } Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; }
BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) { BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
loadKeyAndSecret();
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) { if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
warning("BoxStorage: no access_token found"); warning("BoxStorage: no access_token found");
return nullptr; return nullptr;
@ -335,7 +225,13 @@ BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain); Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_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() { Common::String BoxStorage::getRootDirectoryId() {

View file

@ -30,26 +30,32 @@ namespace Cloud {
namespace Box { namespace Box {
class BoxStorage: public Id::IdStorage { class BoxStorage: public Id::IdStorage {
static char *KEY, *SECRET;
static void loadKeyAndSecret();
Common::String _token, _refreshToken;
/** This private constructor is called from loadFromConfig(). */ /** This private constructor is called from loadFromConfig(). */
BoxStorage(Common::String token, Common::String refreshToken); BoxStorage(Common::String token, Common::String refreshToken, bool enabled);
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
void codeFlowComplete(BoolResponse response);
void codeFlowFailed(Networking::ErrorResponse error);
/** Constructs StorageInfo based on JSON response from cloud. */ /** Constructs StorageInfo based on JSON response from cloud. */
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json); void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response); 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: public:
/** This constructor uses OAuth code flow to get tokens. */ /** This constructor uses OAuth code flow to get tokens. */
BoxStorage(Common::String code); BoxStorage(Common::String code, Networking::ErrorCallback cb);
virtual ~BoxStorage(); virtual ~BoxStorage();
/** /**
@ -98,14 +104,12 @@ public:
*/ */
static BoxStorage *loadFromConfig(Common::String keyPrefix); static BoxStorage *loadFromConfig(Common::String keyPrefix);
virtual Common::String getRootDirectoryId();
/** /**
* Gets new access_token. If <code> passed is "", refresh_token is used. * Remove all BoxStorage-related data from config.
* Use "" in order to refresh token and pass a callback, so you could
* continue your work when new token is available.
*/ */
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; } Common::String accessToken() const { return _token; }
}; };

View file

@ -41,7 +41,7 @@ void BoxTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
if (!response.value) { if (!response.value) {
//failed to refresh token, notify user with NULL in original callback //failed to refresh token, notify user with NULL in original callback
warning("BoxTokenRefresher: failed to refresh token"); 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; return;
} }
@ -99,7 +99,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
pause(); pause();
delete json; 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; return;
} }
} }
@ -111,7 +111,7 @@ void BoxTokenRefresher::finishJson(Common::JSONValue *json) {
void BoxTokenRefresher::finishError(Networking::ErrorResponse error) { void BoxTokenRefresher::finishError(Networking::ErrorResponse error) {
if (error.httpResponseCode == 401) { // invalid_token if (error.httpResponseCode == 401) { // invalid_token
pause(); pause();
_parentStorage->getAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed)); _parentStorage->refreshAccessToken(new Common::Callback<BoxTokenRefresher, Storage::BoolResponse>(this, &BoxTokenRefresher::tokenRefreshed));
return; return;
} }

View file

@ -148,12 +148,17 @@ void CloudManager::replaceStorage(Storage *storage, uint32 index) {
} }
_activeStorage = storage; _activeStorage = storage;
_currentStorageIndex = index; _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(); save();
//do what should be done on first Storage connect //do what should be done on first Storage connect
if (_activeStorage) { if (_activeStorage) {
_activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername() _activeStorage->info(nullptr, nullptr); //automatically calls setStorageUsername()
_activeStorage->syncSaves(nullptr, nullptr);
} }
} }
@ -250,21 +255,21 @@ void CloudManager::setStorageLastSync(uint32 index, Common::String date) {
save(); save();
} }
void CloudManager::connectStorage(uint32 index, Common::String code) { void CloudManager::connectStorage(uint32 index, Common::String code, Networking::ErrorCallback cb) {
freeStorages(); freeStorages();
switch (index) { switch (index) {
case kStorageDropboxId: case kStorageDropboxId:
new Dropbox::DropboxStorage(code); new Dropbox::DropboxStorage(code, cb);
break; break;
case kStorageOneDriveId: case kStorageOneDriveId:
new OneDrive::OneDriveStorage(code); new OneDrive::OneDriveStorage(code, cb);
break; break;
case kStorageGoogleDriveId: case kStorageGoogleDriveId:
new GoogleDrive::GoogleDriveStorage(code); new GoogleDrive::GoogleDriveStorage(code, cb);
break; break;
case kStorageBoxId: case kStorageBoxId:
new Box::BoxStorage(code); new Box::BoxStorage(code, cb);
break; break;
} }
// in these constructors Storages request token using the passed code // 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 // 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) { Networking::Request *CloudManager::listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
Storage *storage = getCurrentStorage(); Storage *storage = getCurrentStorage();
if (storage) { if (storage) {
@ -316,6 +357,28 @@ Common::String CloudManager::savesDirectoryPath() {
return ""; 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) { SavesSyncRequest *CloudManager::syncSaves(Storage::BoolCallback callback, Networking::ErrorCallback errorCallback) {
Storage *storage = getCurrentStorage(); Storage *storage = getCurrentStorage();
if (storage) { if (storage) {
@ -336,14 +399,6 @@ bool CloudManager::isWorking() const {
return false; return false;
} }
bool CloudManager::couldUseLocalServer() {
#ifdef USE_SDL_NET
return Networking::LocalWebserver::getPort() == Networking::LocalWebserver::DEFAULT_SERVER_PORT;
#else
return false;
#endif
}
///// SavesSyncRequest-related ///// ///// SavesSyncRequest-related /////
bool CloudManager::isSyncing() const { bool CloudManager::isSyncing() const {

View file

@ -204,8 +204,16 @@ public:
* *
* @param index Storage's index * @param index Storage's index
* @param code OAuth2 code received from user * @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. */ /** Returns ListDirectoryResponse with list of files. */
Networking::Request *listDirectory(Common::String path, Storage::ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive = false); 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. */ /** Returns storage's saves directory path with the trailing slash. */
Common::String savesDirectoryPath(); 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. * Starts saves syncing process in currently active storage if there is any.
*/ */
@ -227,9 +244,6 @@ public:
/** Returns whether there are any requests running. */ /** Returns whether there are any requests running. */
bool isWorking() const; bool isWorking() const;
/** Returns whether LocalWebserver is available to use for auth. */
static bool couldUseLocalServer();
///// SavesSyncRequest-related ///// ///// SavesSyncRequest-related /////
/** Returns whether there is a SavesSyncRequest running. */ /** Returns whether there is a SavesSyncRequest running. */

View file

@ -73,13 +73,13 @@ void DownloadRequest::streamErrorCallback(Networking::ErrorResponse error) {
void DownloadRequest::handle() { void DownloadRequest::handle() {
if (!_localFile) { if (!_localFile) {
warning("DownloadRequest: no file to write"); 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; return;
} }
if (!_localFile->isOpen()) { if (!_localFile->isOpen()) {
warning("DownloadRequest: failed to open file to write"); 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; return;
} }
@ -93,7 +93,7 @@ void DownloadRequest::handle() {
if (readBytes != 0) if (readBytes != 0)
if (_localFile->write(_buffer, readBytes) != readBytes) { if (_localFile->write(_buffer, readBytes) != readBytes) {
warning("DownloadRequest: unable to write all received bytes into output file"); 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; return;
} }
@ -113,7 +113,7 @@ void DownloadRequest::handle() {
void DownloadRequest::restart() { void DownloadRequest::restart() {
warning("DownloadRequest: can't restart as there are no means to reopen DumpFile"); 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(); //start();
} }

View file

@ -74,7 +74,7 @@ void DropboxCreateDirectoryRequest::responseCallback(Networking::JsonResponse re
} }
if (response.request) _date = response.request->date(); 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; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -71,7 +71,7 @@ void DropboxInfoRequest::userResponseCallback(Networking::JsonResponse response)
return; return;
} }
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "DropboxInfoRequest::userResponseCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
@ -125,7 +125,7 @@ void DropboxInfoRequest::quotaResponseCallback(Networking::JsonResponse response
return; return;
} }
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "DropboxInfoRequest::quotaResponseCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -84,7 +84,7 @@ void DropboxListDirectoryRequest::responseCallback(Networking::JsonResponse resp
if (response.request) if (response.request)
_date = response.request->date(); _date = response.request->date();
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "DropboxListDirectoryRequest::responseCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -35,103 +35,30 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/json.h" #include "common/json.h"
#ifdef ENABLE_RELEASE
#include "dists/clouds/cloud_keys.h"
#endif
namespace Cloud { namespace Cloud {
namespace Dropbox { namespace Dropbox {
#define DROPBOX_OAUTH2_TOKEN "https://api.dropboxapi.com/oauth2/token"
#define DROPBOX_API_FILES_DOWNLOAD "https://content.dropboxapi.com/2/files/download" #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 DropboxStorage::DropboxStorage(Common::String accessToken, bool enabled): BaseStorage(accessToken, "", enabled) {}
char *DropboxStorage::SECRET = nullptr;
void DropboxStorage::loadKeyAndSecret() { DropboxStorage::DropboxStorage(Common::String code, Networking::ErrorCallback cb): BaseStorage() {
#ifdef ENABLE_RELEASE getAccessToken(code, cb);
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() {} DropboxStorage::~DropboxStorage() {}
void DropboxStorage::getAccessToken(Common::String code) { Common::String DropboxStorage::cloudProvider() { return "dropbox"; }
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);
}
void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) { uint32 DropboxStorage::storageIndex() { return kStorageDropboxId; }
Common::JSONValue *json = (Common::JSONValue *)response.value;
if (json == nullptr) {
debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
CloudMan.removeStorage(this);
return;
}
if (!json->isObject()) { bool DropboxStorage::needsRefreshToken() { return false; }
debug(9, "DropboxStorage::codeFlowComplete: Passed JSON is not an object!");
CloudMan.removeStorage(this);
delete json;
return;
}
Common::JSONObject result = json->asObject(); bool DropboxStorage::canReuseRefreshToken() { return false; }
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);
}
void DropboxStorage::saveConfig(Common::String keyPrefix) { void DropboxStorage::saveConfig(Common::String keyPrefix) {
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain); saveIsEnabledFlag(keyPrefix);
} }
Common::String DropboxStorage::name() const { Common::String DropboxStorage::name() const {
@ -177,22 +104,18 @@ Networking::Request *DropboxStorage::info(StorageInfoCallback callback, Networki
Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; } Common::String DropboxStorage::savesDirectoryPath() { return "/saves/"; }
DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) { DropboxStorage *DropboxStorage::loadFromConfig(Common::String keyPrefix) {
loadKeyAndSecret();
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) { if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
warning("DropboxStorage: no access_token found"); warning("DropboxStorage: no access_token found");
return nullptr; return nullptr;
} }
if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) { Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
warning("DropboxStorage: no user_id found"); return new DropboxStorage(accessToken, loadIsEnabledFlag(keyPrefix));
return nullptr; }
}
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain); void DropboxStorage::removeFromConfig(Common::String keyPrefix) {
Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain); ConfMan.removeKey(keyPrefix + "access_token", ConfMan.kCloudDomain);
removeIsEnabledFlag(keyPrefix);
return new DropboxStorage(accessToken, userId);
} }
} // End of namespace Dropbox } // End of namespace Dropbox

View file

@ -23,30 +23,35 @@
#ifndef BACKENDS_CLOUD_DROPBOX_STORAGE_H #ifndef BACKENDS_CLOUD_DROPBOX_STORAGE_H
#define 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 "common/callback.h"
#include "backends/networking/curl/curljsonrequest.h" #include "backends/networking/curl/curljsonrequest.h"
namespace Cloud { namespace Cloud {
namespace Dropbox { namespace Dropbox {
class DropboxStorage: public Cloud::Storage { class DropboxStorage: public Cloud::BaseStorage {
static char *KEY, *SECRET;
static void loadKeyAndSecret();
Common::String _token, _uid;
/** This private constructor is called from loadFromConfig(). */ /** 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); protected:
void codeFlowComplete(Networking::JsonResponse response); /**
void codeFlowFailed(Networking::ErrorResponse error); * @return "dropbox"
*/
virtual Common::String cloudProvider();
/**
* @return kStorageDropboxId
*/
virtual uint32 storageIndex();
virtual bool needsRefreshToken();
virtual bool canReuseRefreshToken();
public: public:
/** This constructor uses OAuth code flow to get tokens. */ /** This constructor uses OAuth code flow to get tokens. */
DropboxStorage(Common::String code); DropboxStorage(Common::String code, Networking::ErrorCallback cb);
virtual ~DropboxStorage(); virtual ~DropboxStorage();
/** /**
@ -93,6 +98,11 @@ public:
* @return pointer to the newly created DropboxStorage or 0 if some problem occured. * @return pointer to the newly created DropboxStorage or 0 if some problem occured.
*/ */
static DropboxStorage *loadFromConfig(Common::String keyPrefix); static DropboxStorage *loadFromConfig(Common::String keyPrefix);
/**
* Remove all DropboxStorage-related data from config.
*/
static void removeFromConfig(Common::String keyPrefix);
}; };
} // End of namespace Dropbox } // End of namespace Dropbox

View file

@ -54,12 +54,12 @@ void DropboxUploadRequest::start() {
_workingRequest->finish(); _workingRequest->finish();
if (!_contentsStream) { if (!_contentsStream) {
warning("DropboxUploadRequest: cannot start because stream is invalid"); 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; return;
} }
if (!_contentsStream->seek(0)) { if (!_contentsStream->seek(0)) {
warning("DropboxUploadRequest: cannot restart because stream couldn't 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; return;
} }
_ignoreCallback = false; _ignoreCallback = false;

View file

@ -26,6 +26,7 @@
#include "common/debug.h" #include "common/debug.h"
#include "gui/downloaddialog.h" #include "gui/downloaddialog.h"
#include <backends/networking/curl/connectionmanager.h> #include <backends/networking/curl/connectionmanager.h>
#include "cloudmanager.h"
namespace Cloud { namespace Cloud {
@ -73,8 +74,9 @@ void FolderDownloadRequest::directoryListedCallback(Storage::ListDirectoryRespon
// remove all directories // remove all directories
// non-empty directories would be created by DumpFile, and empty ones are just ignored // 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();) for (Common::Array<StorageFile>::iterator i = _pendingFiles.begin(); i != _pendingFiles.end();)
if (i->isDirectory()) if (i->isDirectory() || !CloudMan.canSyncFilename(i->name()))
_pendingFiles.erase(i); _pendingFiles.erase(i);
else { else {
_totalBytes += i->size(); _totalBytes += i->size();

View file

@ -81,7 +81,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(Networking::JsonRespo
if (response.request) if (response.request)
_date = response.request->date(); _date = response.request->date();
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "GoogleDriveListDirectoryByIdRequest::responseCallback");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -36,142 +36,34 @@
#include "common/json.h" #include "common/json.h"
#include "common/debug.h" #include "common/debug.h"
#ifdef ENABLE_RELEASE
#include "dists/clouds/cloud_keys.h"
#endif
namespace Cloud { namespace Cloud {
namespace GoogleDrive { 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_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_FILES "https://www.googleapis.com/drive/v3/files"
#define GOOGLEDRIVE_API_ABOUT "https://www.googleapis.com/drive/v3/about?fields=storageQuota,user" #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 GoogleDriveStorage::GoogleDriveStorage(Common::String token, Common::String refreshToken, bool enabled):
char *GoogleDriveStorage::SECRET = nullptr; IdStorage(token, refreshToken, enabled) {}
void GoogleDriveStorage::loadKeyAndSecret() { GoogleDriveStorage::GoogleDriveStorage(Common::String code, Networking::ErrorCallback cb) {
#ifdef ENABLE_RELEASE getAccessToken(code, cb);
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() {} GoogleDriveStorage::~GoogleDriveStorage() {}
void GoogleDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) { Common::String GoogleDriveStorage::cloudProvider() { return "gdrive"; }
if (!KEY || !SECRET) loadKeyAndSecret();
bool codeFlow = (code != "");
if (!codeFlow && _refreshToken == "") { uint32 GoogleDriveStorage::storageIndex() { return kStorageGoogleDriveId; }
warning("GoogleDriveStorage: no refresh token available to get new access token.");
if (callback)
(*callback)(BoolResponse(nullptr, false));
return;
}
Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::tokenRefreshed, callback); bool GoogleDriveStorage::needsRefreshToken() { return true; }
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);
}
void GoogleDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) { bool GoogleDriveStorage::canReuseRefreshToken() { return true; }
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);
}
void GoogleDriveStorage::saveConfig(Common::String keyPrefix) { void GoogleDriveStorage::saveConfig(Common::String keyPrefix) {
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
saveIsEnabledFlag(keyPrefix);
} }
Common::String GoogleDriveStorage::name() const { Common::String GoogleDriveStorage::name() const {
@ -325,8 +217,6 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; } Common::String GoogleDriveStorage::savesDirectoryPath() { return "scummvm/saves/"; }
GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) { GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix) {
loadKeyAndSecret();
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) { if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
warning("GoogleDriveStorage: no access_token found"); warning("GoogleDriveStorage: no access_token found");
return nullptr; return nullptr;
@ -339,7 +229,13 @@ GoogleDriveStorage *GoogleDriveStorage::loadFromConfig(Common::String keyPrefix)
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain); Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_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() { Common::String GoogleDriveStorage::getRootDirectoryId() {

View file

@ -30,18 +30,8 @@ namespace Cloud {
namespace GoogleDrive { namespace GoogleDrive {
class GoogleDriveStorage: public Id::IdStorage { class GoogleDriveStorage: public Id::IdStorage {
static char *KEY, *SECRET;
static void loadKeyAndSecret();
Common::String _token, _refreshToken;
/** This private constructor is called from loadFromConfig(). */ /** This private constructor is called from loadFromConfig(). */
GoogleDriveStorage(Common::String token, Common::String refreshToken); GoogleDriveStorage(Common::String token, Common::String refreshToken, bool enabled);
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
void codeFlowComplete(BoolResponse response);
void codeFlowFailed(Networking::ErrorResponse error);
/** Constructs StorageInfo based on JSON response from cloud. */ /** Constructs StorageInfo based on JSON response from cloud. */
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json); void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
@ -50,9 +40,25 @@ class GoogleDriveStorage: public Id::IdStorage {
void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json); void createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse json);
void printInfo(StorageInfoResponse response); void printInfo(StorageInfoResponse response);
protected:
/**
* @return "gdrive"
*/
virtual Common::String cloudProvider();
/**
* @return kStorageGoogleDriveId
*/
virtual uint32 storageIndex();
virtual bool needsRefreshToken();
virtual bool canReuseRefreshToken();
public: public:
/** This constructor uses OAuth code flow to get tokens. */ /** This constructor uses OAuth code flow to get tokens. */
GoogleDriveStorage(Common::String code); GoogleDriveStorage(Common::String code, Networking::ErrorCallback cb);
virtual ~GoogleDriveStorage(); virtual ~GoogleDriveStorage();
/** /**
@ -100,14 +106,12 @@ public:
*/ */
static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix); static GoogleDriveStorage *loadFromConfig(Common::String keyPrefix);
virtual Common::String getRootDirectoryId();
/** /**
* Gets new access_token. If <code> passed is "", refresh_token is used. * Remove all GoogleDriveStorage-related data from config.
* Use "" in order to refresh token and pass a callback, so you could
* continue your work when new token is available.
*/ */
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; } Common::String accessToken() const { return _token; }
}; };

View file

@ -41,7 +41,7 @@ void GoogleDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
if (!response.value) { if (!response.value) {
//failed to refresh token, notify user with NULL in original callback //failed to refresh token, notify user with NULL in original callback
warning("GoogleDriveTokenRefresher: failed to refresh token"); 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; return;
} }
@ -77,7 +77,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
Common::JSONObject error = result.getVal("error")->asObject(); Common::JSONObject error = result.getVal("error")->asObject();
bool irrecoverable = true; bool irrecoverable = true;
uint32 code = -1; uint32 code = 0xFFFFFFFF; // Invalid
Common::String message; Common::String message;
if (jsonContainsIntegerNumber(error, "code", "GoogleDriveTokenRefresher")) { if (jsonContainsIntegerNumber(error, "code", "GoogleDriveTokenRefresher")) {
code = error.getVal("code")->asIntegerNumber(); code = error.getVal("code")->asIntegerNumber();
@ -100,7 +100,7 @@ void GoogleDriveTokenRefresher::finishJson(Common::JSONValue *json) {
pause(); pause();
delete json; 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; return;
} }
} }

View file

@ -55,7 +55,7 @@ void GoogleDriveUploadRequest::start() {
_workingRequest->finish(); _workingRequest->finish();
if (_contentsStream == nullptr || !_contentsStream->seek(0)) { if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
warning("GoogleDriveUploadRequest: cannot restart because stream couldn't 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; return;
} }
_resolvedId = ""; //used to update file contents _resolvedId = ""; //used to update file contents
@ -146,33 +146,31 @@ void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse resp
if (_ignoreCallback) if (_ignoreCallback)
return; return;
Networking::ErrorResponse error(this, false, true, "", -1); Networking::ErrorResponse error(this, false, true, "GoogleDriveUploadRequest::startUploadCallback", -1);
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq) { if (rq) {
const Networking::NetworkReadStream *stream = rq->getNetworkReadStream(); const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
if (stream) { if (stream) {
long code = stream->httpResponseCode(); long code = stream->httpResponseCode();
Common::String headers = stream->responseHeaders();
if (code == 200) { if (code == 200) {
const char *cstr = headers.c_str(); Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
const char *position = strstr(cstr, "Location: "); if (headers.contains("location")) {
_uploadUrl = headers["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;
uploadNextPart(); uploadNextPart();
return; 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; error.httpResponseCode = code;
} else {
error.response += ": missing response stream [improbable]";
} }
} else {
error.response += ": missing request object [improbable]";
} }
Common::JSONValue *json = response.value; Common::JSONValue *json = response.value;
@ -202,7 +200,7 @@ void GoogleDriveUploadRequest::uploadNextPart() {
if (oldPos != _serverReceivedBytes) { if (oldPos != _serverReceivedBytes) {
if (!_contentsStream->seek(_serverReceivedBytes)) { if (!_contentsStream->seek(_serverReceivedBytes)) {
warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%lu)", _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; return;
} }
oldPos = _serverReceivedBytes; oldPos = _serverReceivedBytes;
@ -230,25 +228,19 @@ bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream
if (stream->httpResponseCode() != 308) if (stream->httpResponseCode() != 308)
return false; //seriously return false; //seriously
Common::String headers = stream->responseHeaders(); Common::HashMap<Common::String, Common::String> headers = stream->responseHeadersMap();
const char *cstr = headers.c_str(); if (headers.contains("range")) {
for (int rangeTry = 0; rangeTry < 2; ++rangeTry) { Common::String range = headers["range"];
const char *needle = (rangeTry == 0 ? "Range: 0-" : "Range: bytes=0-"); for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
uint32 needleLength = (rangeTry == 0 ? 9 : 15); 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 (range.hasPrefix(needle)) {
range.erase(0, needleLength);
if (position) { _serverReceivedBytes = range.asUint64() + 1;
Common::String result = ""; uploadNextPart();
char c; return true;
for (const char *i = position + needleLength; c = *i, c != 0; ++i) {
if (c == '\n' || c == '\r')
break;
result += c;
} }
_serverReceivedBytes = result.asUint64() + 1;
uploadNextPart();
return true;
} }
} }

View file

@ -33,6 +33,11 @@
namespace Cloud { namespace Cloud {
namespace Id { namespace Id {
IdStorage::IdStorage() {}
IdStorage::IdStorage(Common::String token, Common::String refreshToken, bool enabled):
BaseStorage(token, refreshToken, enabled) {}
IdStorage::~IdStorage() {} IdStorage::~IdStorage() {}
void IdStorage::printFiles(FileArrayResponse response) { void IdStorage::printFiles(FileArrayResponse response) {

View file

@ -23,7 +23,7 @@
#ifndef BACKENDS_CLOUD_ID_IDSTORAGE_H #ifndef BACKENDS_CLOUD_ID_IDSTORAGE_H
#define 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" #include "backends/networking/curl/curljsonrequest.h"
/* /*
@ -43,7 +43,7 @@
namespace Cloud { namespace Cloud {
namespace Id { namespace Id {
class IdStorage: public Cloud::Storage { class IdStorage: public Cloud::BaseStorage {
protected: protected:
void printFiles(FileArrayResponse response); void printFiles(FileArrayResponse response);
void printBool(BoolResponse response); void printBool(BoolResponse response);
@ -52,6 +52,8 @@ protected:
ListDirectoryCallback getPrintFilesCallback(); ListDirectoryCallback getPrintFilesCallback();
public: public:
IdStorage();
IdStorage(Common::String token, Common::String refreshToken, bool enabled);
virtual ~IdStorage(); virtual ~IdStorage();
/** Public Cloud API comes down there. */ /** Public Cloud API comes down there. */

View file

@ -31,7 +31,7 @@
namespace Cloud { namespace Cloud {
namespace OneDrive { 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): OneDriveCreateDirectoryRequest::OneDriveCreateDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::BoolCallback cb, Networking::ErrorCallback ecb):
Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb), Networking::Request(nullptr, ecb), _storage(storage), _path(path), _boolCallback(cb),
@ -96,7 +96,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(Networking::JsonResponse r
if (response.request) if (response.request)
_date = response.request->date(); _date = response.request->date();
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "OneDriveCreateDirectoryRequest::responseCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -31,7 +31,8 @@
namespace Cloud { namespace Cloud {
namespace OneDrive { 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): OneDriveListDirectoryRequest::OneDriveListDirectoryRequest(OneDriveStorage *storage, Common::String path, Storage::ListDirectoryCallback cb, Networking::ErrorCallback ecb, bool recursive):
Networking::Request(nullptr, ecb), Networking::Request(nullptr, ecb),
@ -77,6 +78,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
Common::String dir = _currentDirectory; Common::String dir = _currentDirectory;
dir.deleteLastChar(); dir.deleteLastChar();
Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, ConnMan.urlEncode(dir).c_str()); 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); 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::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, Networking::JsonResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback); Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, Networking::ErrorResponse>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str()); 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); _workingRequest = ConnMan.addRequest(request);
} }
@ -100,7 +102,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonRespo
if (response.request) if (response.request)
_date = response.request->date(); _date = response.request->date();
Networking::ErrorResponse error(this); Networking::ErrorResponse error(this, "OneDriveListDirectoryRequest::listedDirectoryCallback: unknown error");
Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request; Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
if (rq && rq->getNetworkReadStream()) if (rq && rq->getNetworkReadStream())
error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode(); error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

View file

@ -36,143 +36,33 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/json.h" #include "common/json.h"
#ifdef ENABLE_RELEASE
#include "dists/clouds/cloud_keys.h"
#endif
namespace Cloud { namespace Cloud {
namespace OneDrive { namespace OneDrive {
#define ONEDRIVE_OAUTH2_TOKEN "https://login.live.com/oauth20_token.srf" #define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://graph.microsoft.com/v1.0/drive/special/approot:/"
#define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://api.onedrive.com/v1.0/drive/special/approot:/" #define ONEDRIVE_API_SPECIAL_APPROOT "https://graph.microsoft.com/v1.0/drive/special/approot"
#define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth OneDriveStorage::OneDriveStorage(Common::String token, Common::String refreshToken, bool enabled):
char *OneDriveStorage::SECRET = nullptr; BaseStorage(token, refreshToken, enabled) {}
void OneDriveStorage::loadKeyAndSecret() { OneDriveStorage::OneDriveStorage(Common::String code, Networking::ErrorCallback cb) {
#ifdef ENABLE_RELEASE getAccessToken(code, cb);
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() {} OneDriveStorage::~OneDriveStorage() {}
void OneDriveStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) { Common::String OneDriveStorage::cloudProvider() { return "onedrive"; }
if (!KEY || !SECRET)
loadKeyAndSecret();
bool codeFlow = (code != "");
if (!codeFlow && _refreshToken == "") { uint32 OneDriveStorage::storageIndex() { return kStorageOneDriveId; }
warning("OneDriveStorage: no refresh token available to get new access token.");
if (callback)
(*callback)(BoolResponse(nullptr, false));
return;
}
Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback); bool OneDriveStorage::needsRefreshToken() { return true; }
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);
}
void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) { bool OneDriveStorage::canReuseRefreshToken() { return false; }
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);
}
void OneDriveStorage::saveConfig(Common::String keyPrefix) { void OneDriveStorage::saveConfig(Common::String keyPrefix) {
ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain); ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
saveIsEnabledFlag(keyPrefix);
} }
Common::String OneDriveStorage::name() const { Common::String OneDriveStorage::name() const {
@ -247,7 +137,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
} }
Common::JSONObject result = response.value->asObject(); 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"); warning("OneDriveStorage: downloadUrl not found in passed JSON");
debug(9, "%s", response.value->stringify().c_str()); debug(9, "%s", response.value->stringify().c_str());
if (outerCallback) if (outerCallback)
@ -257,7 +147,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
return; return;
} }
const char *url = result.getVal("@content.downloadUrl")->asString().c_str(); const char *url = result.getVal("@microsoft.graph.downloadUrl")->asString().c_str();
if (outerCallback) if (outerCallback)
(*outerCallback)(Networking::NetworkReadStreamResponse( (*outerCallback)(Networking::NetworkReadStreamResponse(
response.request, 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) { 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)); return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
} }
Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) { 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)); return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
} }
Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback 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); 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::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()); Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
request->addHeader("Authorization: Bearer " + _token); request->addHeader("Authorization: bearer " + _token);
return addRequest(request); return addRequest(request);
} }
Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) { Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
debug(9, "OneDrive: `mkdir \"%s\"`", path.c_str());
if (!errorCallback) if (!errorCallback)
errorCallback = getErrorPrintingCallback(); errorCallback = getErrorPrintingCallback();
return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback)); return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
} }
Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback 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::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); Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
request->addHeader("Authorization: bearer " + _token); request->addHeader("Authorization: bearer " + _token);
@ -300,27 +195,25 @@ Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Network
Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; } Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) { OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
loadKeyAndSecret();
if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) { if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
warning("OneDriveStorage: no access_token found"); warning("OneDriveStorage: no access_token found");
return nullptr; 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)) { if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
warning("OneDriveStorage: no refresh_token found"); warning("OneDriveStorage: no refresh_token found");
return nullptr; return nullptr;
} }
Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain); 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); 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 } // End of namespace OneDrive

View file

@ -23,33 +23,39 @@
#ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H #ifndef BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
#define 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" #include "backends/networking/curl/curljsonrequest.h"
namespace Cloud { namespace Cloud {
namespace OneDrive { namespace OneDrive {
class OneDriveStorage: public Cloud::Storage { class OneDriveStorage: public Cloud::BaseStorage {
static char *KEY, *SECRET;
static void loadKeyAndSecret();
Common::String _token, _uid, _refreshToken;
/** This private constructor is called from loadFromConfig(). */ /** This private constructor is called from loadFromConfig(). */
OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken); OneDriveStorage(Common::String token, Common::String refreshToken, bool enabled);
void tokenRefreshed(BoolCallback callback, Networking::JsonResponse response);
void codeFlowComplete(BoolResponse response);
void codeFlowFailed(Networking::ErrorResponse error);
/** Constructs StorageInfo based on JSON response from cloud. */ /** Constructs StorageInfo based on JSON response from cloud. */
void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json); void infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse json);
void fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response); 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: public:
/** This constructor uses OAuth code flow to get tokens. */ /** This constructor uses OAuth code flow to get tokens. */
OneDriveStorage(Common::String code); OneDriveStorage(Common::String code, Networking::ErrorCallback cb);
virtual ~OneDriveStorage(); virtual ~OneDriveStorage();
/** /**
@ -98,11 +104,9 @@ public:
static OneDriveStorage *loadFromConfig(Common::String keyPrefix); static OneDriveStorage *loadFromConfig(Common::String keyPrefix);
/** /**
* Gets new access_token. If <code> passed is "", refresh_token is used. * Remove all OneDriveStorage-related data from config.
* Use "" in order to refresh token and pass a callback, so you could
* continue your work when new token is available.
*/ */
void getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback = nullptr, Common::String code = ""); static void removeFromConfig(Common::String keyPrefix);
Common::String accessToken() const { return _token; } Common::String accessToken() const { return _token; }
}; };

View file

@ -41,7 +41,7 @@ void OneDriveTokenRefresher::tokenRefreshed(Storage::BoolResponse response) {
if (!response.value) { if (!response.value) {
//failed to refresh token, notify user with NULL in original callback //failed to refresh token, notify user with NULL in original callback
warning("OneDriveTokenRefresher: failed to refresh token"); 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; return;
} }
@ -94,18 +94,19 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
irrecoverable = false; irrecoverable = false;
} }
if (code == "unauthenticated") if (code == "unauthenticated" || code == "InvalidAuthenticationToken")
irrecoverable = false; irrecoverable = false;
if (irrecoverable) { 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; delete json;
return; return;
} }
pause(); pause();
delete json; 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; return;
} }
} }
@ -114,6 +115,29 @@ void OneDriveTokenRefresher::finishJson(Common::JSONValue *json) {
CurlJsonRequest::finishJson(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) { void OneDriveTokenRefresher::setHeaders(Common::Array<Common::String> &headers) {
_headers = headers; _headers = headers;
curl_slist_free_all(_headersList); curl_slist_free_all(_headersList);

View file

@ -38,6 +38,7 @@ class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
void tokenRefreshed(Storage::BoolResponse response); void tokenRefreshed(Storage::BoolResponse response);
virtual void finishJson(Common::JSONValue *json); virtual void finishJson(Common::JSONValue *json);
virtual void finishError(Networking::ErrorResponse error);
public: public:
OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url); OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
virtual ~OneDriveTokenRefresher(); virtual ~OneDriveTokenRefresher();

View file

@ -33,8 +33,8 @@
namespace Cloud { namespace Cloud {
namespace OneDrive { 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_UPLOAD "https://graph.microsoft.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_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): 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), Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
@ -56,12 +56,12 @@ void OneDriveUploadRequest::start() {
_workingRequest->finish(); _workingRequest->finish();
if (_contentsStream == nullptr) { if (_contentsStream == nullptr) {
warning("OneDriveUploadRequest: cannot restart because no stream given"); 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; return;
} }
if (!_contentsStream->seek(0)) { if (!_contentsStream->seek(0)) {
warning("OneDriveUploadRequest: cannot restart because stream couldn't 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; return;
} }
_ignoreCallback = false; _ignoreCallback = false;

View file

@ -72,7 +72,7 @@ void SavesSyncRequest::start() {
new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback), new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback),
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback) 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) { 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 //determine which files to download and which files to upload
Common::Array<StorageFile> &remoteFiles = response.value; Common::Array<StorageFile> &remoteFiles = response.value;
uint64 totalSize = 0; uint64 totalSize = 0;
debug(9, "SavesSyncRequest decisions:");
for (uint32 i = 0; i < remoteFiles.size(); ++i) { for (uint32 i = 0; i < remoteFiles.size(); ++i) {
StorageFile &file = remoteFiles[i]; StorageFile &file = remoteFiles[i];
if (file.isDirectory()) if (file.isDirectory())
continue; continue;
totalSize += file.size(); totalSize += file.size();
if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME) if (file.name() == DefaultSaveFileManager::TIMESTAMPS_FILENAME || !CloudMan.canSyncFilename(file.name()))
continue; continue;
Common::String name = file.name(); Common::String name = file.name();
if (!_localFilesTimestamps.contains(name)) { if (!_localFilesTimestamps.contains(name)) {
_filesToDownload.push_back(file); _filesToDownload.push_back(file);
debug(9, "- downloading file %s, because it is not present on local", name.c_str());
} else { } else {
localFileNotAvailableInCloud[name] = false; localFileNotAvailableInCloud[name] = false;
@ -113,6 +115,13 @@ void SavesSyncRequest::directoryListedCallback(Storage::ListDirectoryResponse re
_filesToUpload.push_back(file.name()); _filesToUpload.push_back(file.name());
else else
_filesToDownload.push_back(file); _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 //upload files which are unavailable in cloud
for (Common::HashMap<Common::String, bool>::iterator i = localFileNotAvailableInCloud.begin(); i != localFileNotAvailableInCloud.end(); ++i) { 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; continue;
if (i->_value) if (i->_value) {
_filesToUpload.push_back(i->_key); _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:"); debug(9, "\nSavesSyncRequest: ");
for (uint32 i = 0; i < _filesToDownload.size(); ++i) { if (_filesToDownload.size() > 0) {
debug(9, "%s", _filesToDownload[i].name().c_str()); 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:"); debug(9, "SavesSyncRequest: ");
for (uint32 i = 0; i < _filesToUpload.size(); ++i) { if (_filesToUpload.size() > 0) {
debug(9, "%s", _filesToUpload[i].c_str()); 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(); _totalFilesToHandle = _filesToDownload.size() + _filesToUpload.size();
//start downloading files //start downloading files
downloadNextFile(); if (!_filesToDownload.empty()) {
downloadNextFile();
} else {
uploadNextFile();
}
} }
void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) { void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
@ -145,9 +171,22 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
if (_ignoreCallback) if (_ignoreCallback)
return; return;
if (error.failed) debug(9, "%s", error.response.c_str());
bool irrecoverable = error.interrupted || error.failed; bool irrecoverable = error.interrupted || error.failed;
if (error.failed) { if (error.failed) {
Common::JSONValue *value = Common::JSON::parse(error.response.c_str()); 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) {
if (value->isObject()) { if (value->isObject()) {
Common::JSONObject object = value->asObject(); Common::JSONObject object = value->asObject();
@ -174,11 +213,13 @@ void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse er
delete value; 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")) { if (error.response.contains("subdirectory not found")) {
irrecoverable = false; //base "/ScummVM/" folder not found irrecoverable = false; //base "/ScummVM/" folder not found
} else if (error.response.contains("no such file found in its parent directory")) { } else if (error.response.contains("no such file found in its parent directory")) {
irrecoverable = false; //"Saves" folder within "/ScummVM/" not found 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(); Common::String dir = _storage->savesDirectoryPath();
if (dir.lastChar() == '/') if (dir.lastChar() == '/')
dir.deleteLastChar(); dir.deleteLastChar();
debug(9, "SavesSyncRequest: creating %s", dir.c_str()); debug(9, "\nSavesSyncRequest: creating %s", dir.c_str());
_workingRequest = _storage->createDirectory( _workingRequest = _storage->createDirectory(
dir, dir,
new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback), new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback) new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
); );
if (!_workingRequest) 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) { void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response) {
@ -208,7 +249,7 @@ void SavesSyncRequest::directoryCreatedCallback(Storage::BoolResponse response)
//stop syncing if failed to create saves directory //stop syncing if failed to create saves directory
if (!response.value) { 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; return;
} }
@ -239,7 +280,7 @@ void SavesSyncRequest::downloadNextFile() {
sendCommand(GUI::kSavesSyncProgressCmd, (int)(getDownloadingProgress() * 100)); 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( _workingRequest = _storage->downloadById(
_currentDownloadingFile.id(), _currentDownloadingFile.id(),
DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()), DefaultSaveFileManager::concatWithSavesPath(_currentDownloadingFile.name()),
@ -247,7 +288,7 @@ void SavesSyncRequest::downloadNextFile() {
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback) new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileDownloadedErrorCallback)
); );
if (!_workingRequest) 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) { void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
@ -259,7 +300,7 @@ void SavesSyncRequest::fileDownloadedCallback(Storage::BoolResponse response) {
if (!response.value) { if (!response.value) {
//delete the incomplete file //delete the incomplete file
g_system->getSavefileManager()->removeSavefile(_currentDownloadingFile.name()); 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; return;
} }
@ -290,7 +331,7 @@ void SavesSyncRequest::uploadNextFile() {
_currentUploadingFile = _filesToUpload.back(); _currentUploadingFile = _filesToUpload.back();
_filesToUpload.pop_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()) { if (_storage->uploadStreamSupported()) {
_workingRequest = _storage->upload( _workingRequest = _storage->upload(
_storage->savesDirectoryPath() + _currentUploadingFile, _storage->savesDirectoryPath() + _currentUploadingFile,
@ -306,7 +347,7 @@ void SavesSyncRequest::uploadNextFile() {
new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::fileUploadedErrorCallback) 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) { void SavesSyncRequest::fileUploadedCallback(Storage::UploadResponse response) {

View file

@ -34,10 +34,18 @@ namespace Cloud {
Storage::Storage(): Storage::Storage():
_runningRequestsCount(0), _savesSyncRequest(nullptr), _syncRestartRequestsed(false), _runningRequestsCount(0), _savesSyncRequest(nullptr), _syncRestartRequestsed(false),
_downloadFolderRequest(nullptr) {} _downloadFolderRequest(nullptr), _isEnabled(false) {}
Storage::~Storage() {} Storage::~Storage() {}
bool Storage::isEnabled() const {
return _isEnabled;
}
void Storage::enable() {
_isEnabled = true;
}
Networking::ErrorCallback Storage::getErrorPrintingCallback() { Networking::ErrorCallback Storage::getErrorPrintingCallback() {
return new Common::Callback<Storage, Networking::ErrorResponse>(this, &Storage::printErrorResponse); 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) { 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) if (!errorCallback)
errorCallback = getErrorPrintingCallback(); errorCallback = getErrorPrintingCallback();
return addRequest(new FolderDownloadRequest(this, callback, errorCallback, remotePath, localPath, recursive)); 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) { SavesSyncRequest *Storage::syncSaves(BoolCallback callback, Networking::ErrorCallback errorCallback) {
_runningRequestsMutex.lock(); _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) { if (_savesSyncRequest) {
warning("Storage::syncSaves: there is a sync in progress already"); warning("Storage::syncSaves: there is a sync in progress already");
_syncRestartRequestsed = true; _syncRestartRequestsed = true;
@ -208,7 +229,6 @@ void Storage::savesSyncDefaultCallback(BoolResponse response) {
if (!response.value) if (!response.value)
warning("SavesSyncRequest called success callback with `false` argument"); warning("SavesSyncRequest called success callback with `false` argument");
Common::OSDMessageQueue::instance().addMessage(_("Saved games sync complete."));
} }
void Storage::savesSyncDefaultErrorCallback(Networking::ErrorResponse error) { void Storage::savesSyncDefaultErrorCallback(Networking::ErrorResponse error) {

View file

@ -70,6 +70,9 @@ protected:
/** FolderDownloadRequest-related */ /** FolderDownloadRequest-related */
FolderDownloadRequest *_downloadFolderRequest; FolderDownloadRequest *_downloadFolderRequest;
/** Whether user manually enabled the Storage. */
bool _isEnabled;
/** Returns default error callback (printErrorResponse). */ /** Returns default error callback (printErrorResponse). */
virtual Networking::ErrorCallback getErrorPrintingCallback(); virtual Networking::ErrorCallback getErrorPrintingCallback();
@ -115,6 +118,16 @@ public:
*/ */
virtual Common::String name() const = 0; 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. * Public Cloud API comes down there.
* *

View file

@ -661,6 +661,10 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
event.path = Common::String(ev.drop.file); event.path = Common::String(ev.drop.file);
SDL_free(ev.drop.file); SDL_free(ev.drop.file);
return true; return true;
case SDL_CLIPBOARDUPDATE:
event.type = Common::EVENT_CLIPBOARD_UPDATE;
return true;
#else #else
case SDL_VIDEOEXPOSE: case SDL_VIDEOEXPOSE:
if (_graphicsManager) if (_graphicsManager)

View file

@ -193,13 +193,11 @@ public:
virtual Common::WriteStream *createWriteStream() = 0; 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 the directory is created successfully
*
* @return true if file is created successfully
*/ */
virtual bool create(bool isDirectoryFlag) = 0; virtual bool createDirectory() = 0;
}; };

View file

@ -443,9 +443,9 @@ Common::WriteStream *AmigaOSFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool AmigaOSFilesystemNode::create(bool isDirectoryFlag) { bool AmigaOSFilesystemNode::createDirectory() {
error("Not supported"); warning("AmigaOSFilesystemNode::createDirectory(): Not supported");
return false; return _bIsValid && _bIsDirectory;
} }
#endif //defined(__amigaos4__) #endif //defined(__amigaos4__)

View file

@ -116,7 +116,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectoryFlag); virtual bool createDirectory();
}; };

View file

@ -22,16 +22,6 @@
#if defined(POSIX) #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" #include "backends/fs/chroot/chroot-fs.h"
ChRootFilesystemNode::ChRootFilesystemNode(const Common::String &root, POSIXFilesystemNode *node) { ChRootFilesystemNode::ChRootFilesystemNode(const Common::String &root, POSIXFilesystemNode *node) {
@ -110,9 +100,8 @@ Common::WriteStream *ChRootFilesystemNode::createWriteStream() {
return _realNode->createWriteStream(); return _realNode->createWriteStream();
} }
bool ChRootFilesystemNode::create(bool isDirectoryFlag) { bool ChRootFilesystemNode::createDirectory() {
error("Not supported"); return _realNode->createDirectory();
return false;
} }
Common::String ChRootFilesystemNode::addPathComponent(const Common::String &path, const Common::String &component) { Common::String ChRootFilesystemNode::addPathComponent(const Common::String &path, const Common::String &component) {

View file

@ -49,7 +49,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectoryFlag); virtual bool createDirectory();
private: private:
static Common::String addPathComponent(const Common::String &path, const Common::String &component); static Common::String addPathComponent(const Common::String &path, const Common::String &component);

View file

@ -33,7 +33,7 @@
#define FORBIDDEN_SYMBOL_EXCEPTION_srandom #define FORBIDDEN_SYMBOL_EXCEPTION_srandom
#include "backends/fs/posix/posix-fs.h" #include "backends/fs/posix/posix-fs.h"
#include "backends/fs/stdiostream.h" #include "backends/fs/posix/posix-iostream.h"
#include "common/algorithm.h" #include "common/algorithm.h"
#include <sys/param.h> #include <sys/param.h>
@ -91,13 +91,13 @@ POSIXFilesystemNode::POSIXFilesystemNode(const Common::String &p) {
#endif #endif
// Expand "~/" to the value of the HOME env variable // Expand "~/" to the value of the HOME env variable
if (p.hasPrefix("~/")) { if (p.hasPrefix("~/") || p == "~") {
const char *home = getenv("HOME"); const char *home = getenv("HOME");
if (home != NULL && strlen(home) < MAXPATHLEN) { if (home != NULL && strlen(home) < MAXPATHLEN) {
_path = home; _path = home;
// Skip over the tilda. We know that p contains at least // Skip over the tilda.
// two chars, so this is safe: if (p.size() > 1)
_path += p.c_str() + 1; _path += p.c_str() + 1;
} }
} else { } else {
_path = p; _path = p;
@ -292,40 +292,18 @@ AbstractFSNode *POSIXFilesystemNode::getParent() const {
} }
Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() { Common::SeekableReadStream *POSIXFilesystemNode::createReadStream() {
return StdioStream::makeFromPath(getPath(), false); return PosixIoStream::makeFromPath(getPath(), false);
} }
Common::WriteStream *POSIXFilesystemNode::createWriteStream() { Common::WriteStream *POSIXFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return PosixIoStream::makeFromPath(getPath(), true);
} }
bool POSIXFilesystemNode::create(bool isDirectoryFlag) { bool POSIXFilesystemNode::createDirectory() {
bool success; if (mkdir(_path.c_str(), 0755) == 0)
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) {
setFlags(); 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", return _isValid && _isDirectory;
isDirectoryFlag ? "mkdir" : "creat", isDirectoryFlag ? "directory" : "file");
return false;
}
return false;
} }
namespace Posix { namespace Posix {

View file

@ -68,7 +68,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectoryFlag); virtual bool createDirectory();
private: private:
/** /**

View 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;
}

View 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

View file

@ -20,11 +20,13 @@
* *
*/ */
#if defined(RISCOS)
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
#define FORBIDDEN_SYMBOL_EXCEPTION_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-factory.h"
#include "backends/fs/riscos/riscos-fs.h" #include "backends/fs/riscos/riscos-fs.h"

View file

@ -20,23 +20,21 @@
* *
*/ */
#if defined(RISCOS)
// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h. // Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
#define FORBIDDEN_SYMBOL_EXCEPTION_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/platform/sdl/riscos/riscos-utils.h"
#include "backends/fs/riscos/riscos-fs.h" #include "backends/fs/riscos/riscos-fs.h"
#include "backends/fs/stdiostream.h" #include "backends/fs/stdiostream.h"
#include "common/algorithm.h" #include "common/algorithm.h"
#include <limits.h> // TODO: Replace use of access()
#include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <unixlib/local.h>
#include <kernel.h> #include <kernel.h>
#include <swis.h> #include <swis.h>
@ -52,23 +50,33 @@ bool RISCOSFilesystemNode::isWritable() const {
return access(_path.c_str(), W_OK) == 0; return access(_path.c_str(), W_OK) == 0;
} }
RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) { void RISCOSFilesystemNode::setFlags() {
_path = p; _kernel_swi_regs regs;
if (p == "/") { regs.r[0] = 23;
regs.r[1] = (int)_nativePath.c_str();
_kernel_swi(OS_File, &regs, &regs);
if (regs.r[0] == 0) {
_isDirectory = false;
_isValid = false;
} else if (regs.r[0] == 2) {
_isDirectory = true; _isDirectory = true;
_isValid = true; _isValid = true;
} else { } else {
int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, RISCOS_Utils::toRISCOS(_path).c_str()); _isDirectory = false;
if (type == 0) { _isValid = true;
_isDirectory = false; }
_isValid = false; }
} else if (type == 2) {
_isDirectory = true; RISCOSFilesystemNode::RISCOSFilesystemNode(const Common::String &p) {
_isValid = true; _path = p;
} else { if (p == "/") {
_isDirectory = false; _nativePath = "";
_isValid = true; _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 == "/") { if (_path == "/") {
// Special case for the root dir: List all drives // Special case for the root dir: List all drives
char fsname[PATH_MAX] = ""; char fsname[MAXPATHLEN] = "";
for (int fsNum = 0; fsNum < 256; fsNum += 1) { for (int fsNum = 0; fsNum < 256; fsNum += 1) {
_swi(OS_FSControl, _INR(0,3), 33, fsNum, fsname, sizeof(fsname)); if (fsNum == 33 || fsNum == 46 || fsNum == 53 || fsNum == 99)
if (strcmp(fsname, "") != 0) { continue;
if (!(fsNum == 46 || fsNum == 53 || fsNum == 99)) {
int drives = 9;
if (fsNum == 193)
drives = 23;
for (int discnum = 0; discnum <= drives; discnum += 1) { _kernel_swi_regs regs;
const Common::String path = Common::String::format("%s::%d.$", fsname, discnum); regs.r[0] = 33;
char outpath[PATH_MAX] = ""; regs.r[1] = fsNum;
if(_swix(OS_FSControl, _INR(0,2)|_IN(5), 37, path.c_str(), outpath, sizeof(outpath)) == NULL) { regs.r[2] = (int)fsname;
int exist; regs.r[3] = sizeof(fsname);
if (_swix(OS_File, _INR(0,1)|_OUT(0), 23, outpath, &exist) != NULL || exist != 2) _kernel_swi(OS_FSControl, &regs, &regs);
continue; if (fsname[0] == 0)
continue;
RISCOSFilesystemNode *entry = new RISCOSFilesystemNode(); int drives = (fsNum == 193) ? 23 : 9;
entry->_isDirectory = true;
entry->_isValid = true; for (int discnum = 0; discnum <= drives; discnum += 1) {
entry->_path = Common::String::format("/%s", outpath); const Common::String path = Common::String::format("%s::%d.$", fsname, discnum);
entry->_displayName = outpath; char outpath[MAXPATHLEN] = "";
myList.push_back(entry); 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, &regs, &regs) != 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; return true;
} }
int count = 0; char file[MAXPATHLEN];
int read = 0; _kernel_swi_regs regs;
char file[PATH_MAX]; regs.r[0] = 9;
Common::String dir = _path; 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, &regs, &regs);
while (count != -1) { if (regs.r[4] == -1)
_swix(OS_GBPB, _INR(0,5)|_OUTR(3,4), 9, RISCOS_Utils::toRISCOS(dir).c_str(), file, 1, count, sizeof(file), &read, &count); break;
if (count == -1)
continue;
// Start with a clone of this node, with the correct path set // Start with a clone of this node, with the correct path set
RISCOSFilesystemNode entry(*this); RISCOSFilesystemNode entry(*this);
@ -143,15 +164,10 @@ bool RISCOSFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bo
if (_path.lastChar() != '/') if (_path.lastChar() != '/')
entry._path += '/'; entry._path += '/';
entry._path += entry._displayName; entry._path += entry._displayName;
entry._nativePath = RISCOS_Utils::toRISCOS(entry._path);
int type = _swi(OS_File, _INR(0,1)|_RETURN(0), 20, RISCOS_Utils::toRISCOS(entry._path).c_str()); entry.setFlags();
if (type == 0) { if (!entry._isValid)
continue; continue;
} else if (type == 2) {
entry._isDirectory = true;
} else {
entry._isDirectory = false;
}
// Honor the chosen mode // Honor the chosen mode
if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) || if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) ||
@ -198,33 +214,14 @@ Common::WriteStream *RISCOSFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool RISCOSFilesystemNode::create(bool isDirectoryFlag) { bool RISCOSFilesystemNode::createDirectory() {
bool success; _kernel_swi_regs regs;
regs.r[0] = 8;
regs.r[1] = (int)_nativePath.c_str();
if (_kernel_swi(OS_File, &regs, &regs) == NULL)
setFlags();
if (isDirectoryFlag) { return _isValid && _isDirectory;
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;
} }
namespace Riscos { namespace Riscos {
@ -270,7 +267,7 @@ bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
} }
node = new RISCOSFilesystemNode(path); node = new RISCOSFilesystemNode(path);
if (!node->create(true)) { if (!node->createDirectory()) {
if (node->exists()) { if (node->exists()) {
if (!node->isDirectory()) { if (!node->isDirectory()) {
return false; return false;

View file

@ -33,6 +33,7 @@
class RISCOSFilesystemNode : public AbstractFSNode { class RISCOSFilesystemNode : public AbstractFSNode {
protected: protected:
Common::String _displayName; Common::String _displayName;
Common::String _nativePath;
Common::String _path; Common::String _path;
bool _isDirectory; bool _isDirectory;
bool _isValid; bool _isValid;
@ -67,7 +68,12 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); 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 { namespace Riscos {

View file

@ -134,6 +134,12 @@ WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool
_displayName = lastPathComponent(_path, '\\'); _displayName = lastPathComponent(_path, '\\');
setFlags();
_isPseudoRoot = false;
}
void WindowsFilesystemNode::setFlags() {
// Check whether it is a directory, and whether the file actually exists // Check whether it is a directory, and whether the file actually exists
DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str())); DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str()));
@ -148,7 +154,6 @@ WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool
_path += '\\'; _path += '\\';
} }
} }
_isPseudoRoot = false;
} }
AbstractFSNode *WindowsFilesystemNode::getChild(const Common::String &n) const { AbstractFSNode *WindowsFilesystemNode::getChild(const Common::String &n) const {
@ -244,36 +249,11 @@ Common::WriteStream *WindowsFilesystemNode::createWriteStream() {
return StdioStream::makeFromPath(getPath(), true); return StdioStream::makeFromPath(getPath(), true);
} }
bool WindowsFilesystemNode::create(bool isDirectoryFlag) { bool WindowsFilesystemNode::createDirectory() {
bool success; if (CreateDirectory(toUnicode(_path.c_str()), NULL) != 0)
setFlags();
if (isDirectoryFlag) { return _isValid && _isDirectory;
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;
} }
#endif //#ifdef WIN32 #endif //#ifdef WIN32

View file

@ -84,7 +84,7 @@ public:
virtual Common::SeekableReadStream *createReadStream(); virtual Common::SeekableReadStream *createReadStream();
virtual Common::WriteStream *createWriteStream(); virtual Common::WriteStream *createWriteStream();
virtual bool create(bool isDirectoryFlag); virtual bool createDirectory();
private: private:
/** /**
@ -114,6 +114,11 @@ private:
* @return str in Unicode format. * @return str in Unicode format.
*/ */
static const TCHAR* toUnicode(const char *str); static const TCHAR* toUnicode(const char *str);
/**
* Tests and sets the _isValid and _isDirectory flags, using the GetFileAttributes() function.
*/
virtual void setFlags();
}; };
#endif #endif

View file

@ -81,7 +81,7 @@ void SdlMixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError()); warning("Could not open audio device: %s", SDL_GetError());
// The mixer is not marked as ready // The mixer is not marked as ready
_mixer = new Audio::MixerImpl(g_system, desired.freq); _mixer = new Audio::MixerImpl(desired.freq);
return; return;
} }
@ -96,7 +96,7 @@ void SdlMixerManager::init() {
warning("Could not open audio device: %s", SDL_GetError()); warning("Could not open audio device: %s", SDL_GetError());
// The mixer is not marked as ready // The mixer is not marked as ready
_mixer = new Audio::MixerImpl(g_system, desired.freq); _mixer = new Audio::MixerImpl(desired.freq);
return; return;
} }
@ -118,7 +118,7 @@ void SdlMixerManager::init() {
error("SDL mixer output requires stereo output device"); error("SDL mixer output requires stereo output device");
#endif #endif
_mixer = new Audio::MixerImpl(g_system, _obtained.freq); _mixer = new Audio::MixerImpl(_obtained.freq);
assert(_mixer); assert(_mixer);
_mixer->setReady(true); _mixer->setReady(true);

View file

@ -20,8 +20,6 @@
* *
*/ */
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#include "backends/modular-backend.h" #include "backends/modular-backend.h"
#include "backends/graphics/graphics.h" #include "backends/graphics/graphics.h"
@ -302,7 +300,3 @@ void ModularBackend::displayMessageOnOSD(const char *msg) {
void ModularBackend::displayActivityIconOnOSD(const Graphics::Surface *icon) { void ModularBackend::displayActivityIconOnOSD(const Graphics::Surface *icon) {
_graphicsManager->displayActivityIconOnOSD(icon); _graphicsManager->displayActivityIconOnOSD(icon);
} }
void ModularBackend::quit() {
exit(0);
}

View file

@ -40,6 +40,7 @@ class MutexManager;
* OSystem::getMillis() * OSystem::getMillis()
* OSystem::delayMillis() * OSystem::delayMillis()
* OSystem::getTimeAndDate() * OSystem::getTimeAndDate()
* OSystem::quit()
* *
* And, it should also initialize all the managers variables * And, it should also initialize all the managers variables
* declared in this class, or override their related functions. * declared in this class, or override their related functions.
@ -138,7 +139,6 @@ public:
/** @name Miscellaneous */ /** @name Miscellaneous */
//@{ //@{
virtual void quit() override;
virtual void displayMessageOnOSD(const char *msg) override; virtual void displayMessageOnOSD(const char *msg) override;
virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) override; virtual void displayActivityIconOnOSD(const Graphics::Surface *icon) override;

View file

@ -17,6 +17,7 @@ ifdef USE_CLOUD
ifdef USE_LIBCURL ifdef USE_LIBCURL
MODULE_OBJS += \ MODULE_OBJS += \
cloud/basestorage.o \
cloud/cloudicon.o \ cloud/cloudicon.o \
cloud/cloudmanager.o \ cloud/cloudmanager.o \
cloud/iso8601.o \ cloud/iso8601.o \
@ -58,6 +59,7 @@ MODULE_OBJS += \
networking/curl/networkreadstream.o \ networking/curl/networkreadstream.o \
networking/curl/curlrequest.o \ networking/curl/curlrequest.o \
networking/curl/curljsonrequest.o \ networking/curl/curljsonrequest.o \
networking/curl/postrequest.o \
networking/curl/request.o networking/curl/request.o
endif endif
@ -141,6 +143,7 @@ ifdef POSIX
MODULE_OBJS += \ MODULE_OBJS += \
fs/posix/posix-fs.o \ fs/posix/posix-fs.o \
fs/posix/posix-fs-factory.o \ fs/posix/posix-fs-factory.o \
fs/posix/posix-iostream.o \
fs/chroot/chroot-fs-factory.o \ fs/chroot/chroot-fs-factory.o \
fs/chroot/chroot-fs.o \ fs/chroot/chroot-fs.o \
plugins/posix/posix-provider.o \ plugins/posix/posix-provider.o \
@ -191,6 +194,7 @@ ifdef PLAYSTATION3
MODULE_OBJS += \ MODULE_OBJS += \
fs/posix/posix-fs.o \ fs/posix/posix-fs.o \
fs/posix/posix-fs-factory.o \ fs/posix/posix-fs-factory.o \
fs/posix/posix-iostream.o \
fs/ps3/ps3-fs-factory.o \ fs/ps3/ps3-fs-factory.o \
events/ps3sdl/ps3sdl-events.o events/ps3sdl/ps3sdl-events.o
endif endif
@ -269,6 +273,7 @@ endif
ifeq ($(BACKEND),psp2) ifeq ($(BACKEND),psp2)
MODULE_OBJS += \ MODULE_OBJS += \
fs/posix/posix-fs.o \ fs/posix/posix-fs.o \
fs/posix/posix-iostream.o \
fs/psp2/psp2-fs-factory.o \ fs/psp2/psp2-fs-factory.o \
fs/psp2/psp2-dirent.o \ fs/psp2/psp2-dirent.o \
events/psp2sdl/psp2sdl-events.o \ events/psp2sdl/psp2sdl-events.o \

View file

@ -26,6 +26,7 @@
#include "backends/networking/curl/connectionmanager.h" #include "backends/networking/curl/connectionmanager.h"
#include "backends/networking/curl/networkreadstream.h" #include "backends/networking/curl/networkreadstream.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/fs.h"
#include "common/system.h" #include "common/system.h"
#include "common/timer.h" #include "common/timer.h"
@ -98,6 +99,29 @@ uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
return TIMER_INTERVAL * CLOUD_PERIOD; 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: //private goes here:
void connectionsThread(void *ignored) { void connectionsThread(void *ignored) {
@ -151,7 +175,8 @@ void ConnectionManager::interateRequests() {
_addedRequestsMutex.unlock(); _addedRequestsMutex.unlock();
//call handle() of all running requests (so they can do their work) //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();) { for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
Request *request = i->request; Request *request = i->request;
if (request) { if (request) {
@ -183,20 +208,19 @@ void ConnectionManager::processTransfers() {
int messagesInQueue; int messagesInQueue;
CURLMsg *curlMsg; CURLMsg *curlMsg;
while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) { 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) { if (curlMsg->msg == CURLMSG_DONE) {
debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result)); CURL *easyHandle = curlMsg->easy_handle;
} else {
warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
}
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);
}
} }
} }

View file

@ -38,10 +38,11 @@ namespace Networking {
class NetworkReadStream; class NetworkReadStream;
class ConnectionManager : public Common::Singleton<ConnectionManager> { 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 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 CURL_PERIOD = 1; //every frame
static const uint32 DEBUG_PRINT_PERIOD = FRAMES_PER_SECOND; // once per second
friend void connectionsThread(void *); //calls handle() friend void connectionsThread(void *); //calls handle()
@ -117,6 +118,9 @@ public:
Common::String urlEncode(Common::String s) const; Common::String urlEncode(Common::String s) const;
static uint32 getCloudRequestsPeriodInMicroseconds(); static uint32 getCloudRequestsPeriodInMicroseconds();
/** Return the path to the CA certificates bundle. */
static const char *getCaCertPath();
}; };
/** Shortcut for accessing the connection manager. */ /** Shortcut for accessing the connection manager. */

View file

@ -31,6 +31,7 @@ namespace Networking {
typedef Response<Common::JSONValue *> JsonResponse; typedef Response<Common::JSONValue *> JsonResponse;
typedef Common::BaseCallback<JsonResponse> *JsonCallback; typedef Common::BaseCallback<JsonResponse> *JsonCallback;
typedef Common::BaseCallback<Common::JSONValue *> *JSONValueCallback;
#define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024 #define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024

View file

@ -53,7 +53,7 @@ void CurlRequest::handle() {
if (_stream && _stream->eos()) { if (_stream && _stream->eos()) {
if (_stream->httpResponseCode() != 200) { if (_stream->httpResponseCode() != 200) {
warning("CurlRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode()); 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); finishError(error);
return; return;
} }
@ -71,20 +71,9 @@ void CurlRequest::restart() {
Common::String CurlRequest::date() const { Common::String CurlRequest::date() const {
if (_stream) { if (_stream) {
Common::String headers = _stream->responseHeaders(); Common::HashMap<Common::String, Common::String> headers = _stream->responseHeadersMap();
const char *cstr = headers.c_str(); if (headers.contains("date"))
const char *position = strstr(cstr, "Date: "); return headers["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;
}
} }
return ""; return "";
} }

View file

@ -26,6 +26,7 @@
#include "backends/networking/curl/networkreadstream.h" #include "backends/networking/curl/networkreadstream.h"
#include "backends/networking/curl/connectionmanager.h" #include "backends/networking/curl/connectionmanager.h"
#include "base/version.h" #include "base/version.h"
#include "common/debug.h"
namespace Networking { 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) { void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
_eos = _requestComplete = false; _eos = _requestComplete = false;
_errorBuffer = (char *)calloc(CURL_ERROR_SIZE, 1);
_sendingContentsBuffer = nullptr; _sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0; _sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 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_HEADERDATA, this);
curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback); curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
curl_easy_setopt(_easy, CURLOPT_URL, url); 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_VERBOSE, 0L);
curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList); 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_NOPROGRESS, 0L);
curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder); curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this); 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 #if LIBCURL_VERSION_NUM >= 0x072000
// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0 // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used // 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) { 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; _eos = _requestComplete = false;
_errorBuffer = (char *)calloc(CURL_ERROR_SIZE, 1);
_sendingContentsBuffer = nullptr; _sendingContentsBuffer = nullptr;
_sendingContentsSize = _sendingContentsPos = 0; _sendingContentsSize = _sendingContentsPos = 0;
_progressDownloaded = _progressTotal = 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_HEADERDATA, this);
curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback); curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
curl_easy_setopt(_easy, CURLOPT_URL, url); 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_VERBOSE, 0L);
curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList); 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_NOPROGRESS, 0L);
curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder); curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this); 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 #if LIBCURL_VERSION_NUM >= 0x072000
// CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0 // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
// CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used // CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
@ -197,6 +214,7 @@ NetworkReadStream::~NetworkReadStream() {
if (_easy) if (_easy)
curl_easy_cleanup(_easy); curl_easy_cleanup(_easy);
free(_bufferCopy); free(_bufferCopy);
free(_errorBuffer);
} }
bool NetworkReadStream::eos() const { bool NetworkReadStream::eos() const {
@ -215,8 +233,18 @@ uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
return actuallyRead; return actuallyRead;
} }
void NetworkReadStream::finished() { void NetworkReadStream::finished(uint32 errorCode) {
_requestComplete = true; _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 { long NetworkReadStream::httpResponseCode() const {
@ -240,6 +268,78 @@ Common::String NetworkReadStream::responseHeaders() const {
return _responseHeaders; 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 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
uint32 sendSize = _sendingContentsSize - _sendingContentsPos; uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
if (sendSize > maxSize) if (sendSize > maxSize)

View file

@ -38,6 +38,7 @@ class NetworkReadStream: public Common::ReadStream {
CURL *_easy; CURL *_easy;
Common::MemoryReadWriteStream _backingStream; Common::MemoryReadWriteStream _backingStream;
bool _eos, _requestComplete; bool _eos, _requestComplete;
char *_errorBuffer;
const byte *_sendingContentsBuffer; const byte *_sendingContentsBuffer;
uint32 _sendingContentsSize; uint32 _sendingContentsSize;
uint32 _sendingContentsPos; uint32 _sendingContentsPos;
@ -111,7 +112,7 @@ public:
* *
* @note It's called on failure too. * @note It's called on failure too.
*/ */
void finished(); void finished(uint32 errorCode);
/** /**
* Returns HTTP response code from inner CURL handle. * Returns HTTP response code from inner CURL handle.
@ -136,6 +137,14 @@ public:
*/ */
Common::String responseHeaders() const; 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". */ /** Returns a number in range [0, 1], where 1 is "complete". */
double getProgress() const; double getProgress() const;

View file

@ -24,8 +24,8 @@
namespace Networking { namespace Networking {
ErrorResponse::ErrorResponse(Request *rq): ErrorResponse::ErrorResponse(Request *rq, Common::String resp):
request(rq), interrupted(false), failed(true), response(""), httpResponseCode(-1) {} request(rq), interrupted(false), failed(true), response(resp), httpResponseCode(-1) {}
ErrorResponse::ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode): ErrorResponse::ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode):
request(rq), interrupted(interrupt), failed(failure), response(resp), httpResponseCode(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::pause() { _state = PAUSED; }
void Request::finish() { void Request::finish() {
ErrorResponse error(this, true, false, "", -1); ErrorResponse error(this, true, false, "Request::finish() was called (i.e. interrupted)", -1);
finishError(error); finishError(error);
} }

View file

@ -78,12 +78,12 @@ struct ErrorResponse {
Common::String response; Common::String response;
long httpResponseCode; long httpResponseCode;
ErrorResponse(Request *rq); ErrorResponse(Request *rq, Common::String resp);
ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode); ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode);
}; };
typedef Response<void *> DataReponse; typedef Response<void *> DataResponse;
typedef Common::BaseCallback<DataReponse> *DataCallback; typedef Common::BaseCallback<DataResponse> *DataCallback;
typedef Common::BaseCallback<ErrorResponse> *ErrorCallback; typedef Common::BaseCallback<ErrorResponse> *ErrorCallback;
/** /**

View file

@ -107,7 +107,7 @@ const char *GetClientHandler::responseMessage(long responseCode) {
void GetClientHandler::prepareHeaders() { void GetClientHandler::prepareHeaders() {
if (!_specialHeaders.contains("Content-Type")) if (!_specialHeaders.contains("Content-Type"))
setHeader("Content-Type", "text/html"); setHeader("Content-Type", "text/html; charset=UTF-8");
if (!_specialHeaders.contains("Content-Length") && _stream) if (!_specialHeaders.contains("Content-Length") && _stream)
setHeader("Content-Length", Common::String::format("%u", _stream->size())); setHeader("Content-Length", Common::String::format("%u", _stream->size()));

View file

@ -57,35 +57,35 @@ void CreateDirectoryHandler::handle(Client &client) {
// check that <path> is not an absolute root // check that <path> is not an absolute root
if (path == "" || path == "/") { if (path == "" || path == "/") {
handleError(client, _("Can't create directory here!")); handleError(client, HandlerUtils::toUtf8(_("Can't create directory here!")));
return; return;
} }
// check that <path> contains no '../' // check that <path> contains no '../'
if (HandlerUtils::hasForbiddenCombinations(path)) { if (HandlerUtils::hasForbiddenCombinations(path)) {
handleError(client, _("Invalid path!")); handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// transform virtual path to actual file system one // transform virtual path to actual file system one
Common::String prefixToRemove = "", prefixToAdd = ""; Common::String prefixToRemove = "", prefixToAdd = "";
if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) { if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
handleError(client, _("Invalid path!")); handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// check that <path> exists, is directory and isn't forbidden // check that <path> exists, is directory and isn't forbidden
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path); AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
if (!HandlerUtils::permittedPath(node->getPath())) { if (!HandlerUtils::permittedPath(node->getPath())) {
handleError(client, _("Invalid path!")); handleError(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
if (!node->exists()) { if (!node->exists()) {
handleError(client, _("Parent directory doesn't exists!")); handleError(client, HandlerUtils::toUtf8(_("Parent directory doesn't exists!")));
return; return;
} }
if (!node->isDirectory()) { 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; return;
} }
@ -95,20 +95,20 @@ void CreateDirectoryHandler::handle(Client &client) {
node = g_system->getFilesystemFactory()->makeFileNodePath(path + name); node = g_system->getFilesystemFactory()->makeFileNodePath(path + name);
if (node->exists()) { if (node->exists()) {
if (!node->isDirectory()) { 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; return;
} }
} else { } else {
// create the <directory_name> in <path> // create the <directory_name> in <path>
if (!node->create(true)) { if (!node->createDirectory()) {
handleError(client, _("Failed to create the directory!")); handleError(client, HandlerUtils::toUtf8(_("Failed to create the directory!")));
return; return;
} }
} }
// if json requested, respond with it // if json requested, respond with it
if (client.queryParameter("answer_json") == "true") { if (client.queryParameter("answer_json") == "true") {
setJsonResponseHandler(client, "success", _("Directory created successfully!")); setJsonResponseHandler(client, "success", HandlerUtils::toUtf8(_("Directory created successfully!")));
return; return;
} }
@ -117,9 +117,9 @@ void CreateDirectoryHandler::handle(Client &client) {
client, client,
Common::String::format( Common::String::format(
"%s<br/><a href=\"files?path=%s\">%s</a>", "%s<br/><a href=\"files?path=%s\">%s</a>",
_("Directory created successfully!"), HandlerUtils::toUtf8(_("Directory created successfully!")).c_str(),
client.queryParameter("path").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=") + (client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path")) LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))

View file

@ -40,40 +40,40 @@ void DownloadFileHandler::handle(Client &client) {
// check that <path> is not an absolute root // check that <path> is not an absolute root
if (path == "" || path == "/") { if (path == "" || path == "/") {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// check that <path> contains no '../' // check that <path> contains no '../'
if (HandlerUtils::hasForbiddenCombinations(path)) { if (HandlerUtils::hasForbiddenCombinations(path)) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// transform virtual path to actual file system one // transform virtual path to actual file system one
Common::String prefixToRemove = "", prefixToAdd = ""; Common::String prefixToRemove = "", prefixToAdd = "";
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) { if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// check that <path> exists, is directory and isn't forbidden // check that <path> exists, is directory and isn't forbidden
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path); AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
if (!HandlerUtils::permittedPath(node->getPath())) { if (!HandlerUtils::permittedPath(node->getPath())) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
if (!node->exists()) { if (!node->exists()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The file doesn't exist!")));
return; return;
} }
if (node->isDirectory()) { if (node->isDirectory()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't download a directory!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't download a directory!")));
return; return;
} }
Common::SeekableReadStream *stream = node->createReadStream(); Common::SeekableReadStream *stream = node->createReadStream();
if (stream == nullptr) { if (stream == nullptr) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Failed to read the file!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Failed to read the file!")));
return; return;
} }

View file

@ -56,7 +56,7 @@ void FilesAjaxPageHandler::handle(Client &client) {
// load stylish response page from the archive // load stylish response page from the archive
Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME); Common::SeekableReadStream *const stream = HandlerUtils::getArchiveFile(FILES_PAGE_NAME);
if (stream == nullptr) { 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; return;
} }
@ -64,16 +64,16 @@ void FilesAjaxPageHandler::handle(Client &client) {
Common::String path = client.queryParameter("path"); Common::String path = client.queryParameter("path");
//these occur twice: //these occur twice:
replace(response, "{create_directory_button}", _("Create directory")); replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
replace(response, "{create_directory_button}", _("Create directory")); replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
replace(response, "{upload_files_button}", _("Upload files")); //tab replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
replace(response, "{upload_file_button}", _("Upload files")); //button in the tab replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
replace(response, "{create_directory_desc}", _("Type new directory name:")); replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
replace(response, "{upload_file_desc}", _("Select a file to upload:")); replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):")); replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("Or select a directory (works in Chrome only):")));
replace(response, "{index_of}", _("Index of ")); replace(response, "{index_of}", HandlerUtils::toUtf8(_("Index of ")));
replace(response, "{loading}", _("Loading...")); replace(response, "{loading}", HandlerUtils::toUtf8(("Loading...")));
replace(response, "{error}", _("Error occurred")); replace(response, "{error}", HandlerUtils::toUtf8(_("Error occurred")));
replace(response, "{start_path}", encodeDoubleQuotesAndSlashes(path)); replace(response, "{start_path}", encodeDoubleQuotesAndSlashes(path));
LocalWebserver::setClientGetHandler(client, response); LocalWebserver::setClientGetHandler(client, response);
} }

View file

@ -78,8 +78,8 @@ Common::String getDisplayPath(Common::String s) {
bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) { bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) {
if (path == "" || path == "/") { if (path == "" || path == "/") {
if (ConfMan.hasKey("rootpath", "cloud")) if (ConfMan.hasKey("rootpath", "cloud"))
addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root")); addItem(content, itemTemplate, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games")); addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
return true; return true;
} }
@ -116,7 +116,7 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
filePath = "/"; filePath = "/";
else else
filePath = parentPath(prefixToAdd + filePath); 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 // fill the content
@ -182,7 +182,7 @@ void FilesPageHandler::addItem(Common::String &content, const Common::String &it
void FilesPageHandler::handle(Client &client) { void FilesPageHandler::handle(Client &client) {
Common::String response = Common::String response =
"<html>" \ "<html>" \
"<head><title>ScummVM</title></head>" \ "<head><title>ResidualVM</title><meta charset=\"utf-8\"/></head>" \
"<body>" \ "<body>" \
"<p>{create_directory_desc}</p>" \ "<p>{create_directory_desc}</p>" \
"<form action=\"create\">" \ "<form action=\"create\">" \
@ -215,21 +215,21 @@ void FilesPageHandler::handle(Client &client) {
// show an error message if failed to list directory // show an error message if failed to list directory
if (!listDirectory(path, content, itemTemplate)) { 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; return;
} }
//these occur twice: //these occur twice:
replace(response, "{create_directory_button}", _("Create directory")); replace(response, "{create_directory_button}", HandlerUtils::toUtf8(_("Create directory")));
replace(response, "{create_directory_button}", _("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, "{path}", encodeDoubleQuotes(client.queryParameter("path"))); replace(response, "{path}", encodeDoubleQuotes(client.queryParameter("path")));
replace(response, "{upload_files_button}", _("Upload files")); //tab replace(response, "{upload_files_button}", HandlerUtils::toUtf8(_("Upload files"))); //tab
replace(response, "{upload_file_button}", _("Upload files")); //button in the tab replace(response, "{upload_file_button}", HandlerUtils::toUtf8(_("Upload files"))); //button in the tab
replace(response, "{create_directory_desc}", _("Type new directory name:")); replace(response, "{create_directory_desc}", HandlerUtils::toUtf8(_("Type new directory name:")));
replace(response, "{upload_file_desc}", _("Select a file to upload:")); replace(response, "{upload_file_desc}", HandlerUtils::toUtf8(_("Select a file to upload:")));
replace(response, "{or_upload_directory_desc}", _("Or select a directory (works in Chrome only):")); replace(response, "{or_upload_directory_desc}", HandlerUtils::toUtf8(_("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, "{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); replace(response, "{content}", content);
LocalWebserver::setClientGetHandler(client, response); LocalWebserver::setClientGetHandler(client, response);
} }

View file

@ -24,7 +24,6 @@
#include "backends/networking/sdl_net/handlerutils.h" #include "backends/networking/sdl_net/handlerutils.h"
#include "backends/networking/sdl_net/localwebserver.h" #include "backends/networking/sdl_net/localwebserver.h"
#include "common/translation.h" #include "common/translation.h"
#include "gui/storagewizarddialog.h"
namespace Networking { namespace Networking {
@ -34,28 +33,17 @@ IndexPageHandler::~IndexPageHandler() {}
/// public /// public
Common::String IndexPageHandler::code() const { return _code; }
void IndexPageHandler::handle(Client &client) { void IndexPageHandler::handle(Client &client) {
Common::String queryCode = client.queryParameter("code"); // redirect to "/filesAJAX"
HandlerUtils::setMessageHandler(
if (queryCode == "") { client,
// redirect to "/filesAJAX" Common::String::format(
HandlerUtils::setMessageHandler( "%s<br/><a href=\"files\">%s</a>",
client, HandlerUtils::toUtf8(_("This is a local webserver index page.")).c_str(),
Common::String::format( HandlerUtils::toUtf8(_("Open Files manager")).c_str()
"%s<br/><a href=\"files\">%s</a>", ),
_("This is a local webserver index page."), "/filesAJAX"
_("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!"));
} }
bool IndexPageHandler::minimalModeSupported() { bool IndexPageHandler::minimalModeSupported() {

View file

@ -30,12 +30,10 @@ namespace Networking {
class LocalWebserver; class LocalWebserver;
class IndexPageHandler: public BaseHandler, public GUI::CommandSender { class IndexPageHandler: public BaseHandler, public GUI::CommandSender {
Common::String _code;
public: public:
IndexPageHandler(); IndexPageHandler();
virtual ~IndexPageHandler(); virtual ~IndexPageHandler();
Common::String code() const;
virtual void handle(Client &client); virtual void handle(Client &client);
virtual bool minimalModeSupported(); virtual bool minimalModeSupported();
}; };

View file

@ -42,8 +42,8 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
if (path == "" || path == "/") { if (path == "" || path == "/") {
if (ConfMan.hasKey("rootpath", "cloud")) if (ConfMan.hasKey("rootpath", "cloud"))
addItem(itemsList, IT_DIRECTORY, "/root/", _("File system root")); addItem(itemsList, IT_DIRECTORY, "/root/", HandlerUtils::toUtf8(_("File system root")));
addItem(itemsList, IT_DIRECTORY, "/saves/", _("Saved games")); addItem(itemsList, IT_DIRECTORY, "/saves/", HandlerUtils::toUtf8(_("Saved games")));
successResult.setVal("items", new Common::JSONValue(itemsList)); successResult.setVal("items", new Common::JSONValue(itemsList));
return successResult; return successResult;
} }
@ -81,7 +81,7 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
filePath = "/"; filePath = "/";
else else
filePath = parentPath(prefixToAdd + filePath); 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 // fill the content

View file

@ -40,35 +40,35 @@ void UploadFileHandler::handle(Client &client) {
// check that <path> is not an absolute root // check that <path> is not an absolute root
if (path == "" || path == "/" || path == "\\") { if (path == "" || path == "/" || path == "\\") {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// check that <path> contains no '../' // check that <path> contains no '../'
if (HandlerUtils::hasForbiddenCombinations(path)) { if (HandlerUtils::hasForbiddenCombinations(path)) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// transform virtual path to actual file system one // transform virtual path to actual file system one
Common::String prefixToRemove = "", prefixToAdd = ""; Common::String prefixToRemove = "", prefixToAdd = "";
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) { if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
// check that <path> exists, is directory and isn't forbidden // check that <path> exists, is directory and isn't forbidden
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path); AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
if (!HandlerUtils::permittedPath(node->getPath())) { if (!HandlerUtils::permittedPath(node->getPath())) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
if (!node->exists()) { if (!node->exists()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("The parent directory doesn't exist!")));
return; return;
} }
if (!node->isDirectory()) { if (!node->isDirectory()) {
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Can't upload into a file!")); HandlerUtils::setFilesManagerErrorMessageHandler(client, HandlerUtils::toUtf8(_("Can't upload into a file!")));
return; return;
} }

View file

@ -171,8 +171,13 @@ bool HandlerUtils::permittedPath(const Common::String path) {
return hasPermittedPrefix(path) && !isBlacklisted(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) { 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 // load stylish response page from the archive
Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME); Common::SeekableReadStream *const stream = getArchiveFile(INDEX_PAGE_NAME);
@ -194,7 +199,7 @@ void HandlerUtils::setFilesManagerErrorMessageHandler(Client &client, Common::St
message.c_str(), message.c_str(),
client.queryParameter("ajax") == "true" ? "AJAX" : "", client.queryParameter("ajax") == "true" ? "AJAX" : "",
"%2F", //that's encoded "/" "%2F", //that's encoded "/"
_("Back to the files manager") toUtf8(_("Back to the files manager")).c_str()
), ),
redirectTo redirectTo
); );

View file

@ -41,6 +41,8 @@ public:
static bool hasPermittedPrefix(const Common::String &path); static bool hasPermittedPrefix(const Common::String &path);
static bool permittedPath(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 setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = ""); static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
}; };

View file

@ -111,7 +111,7 @@ class Reader {
uint32 bytesLeft() const; uint32 bytesLeft() const;
public: 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();
~Reader(); ~Reader();

View file

@ -66,7 +66,7 @@ void UploadFileClientHandler::handle(Client *client) {
// fail on suspicious headers // fail on suspicious headers
if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) { 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; break;
@ -108,7 +108,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
// fail on suspicious headers // fail on suspicious headers
if (_headersStream->size() > Reader::SUSPICIOUS_HEADERS_SIZE) { 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 // search for "upload_file" field
@ -134,11 +134,11 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
path += '/'; path += '/';
AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename); AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
if (!HandlerUtils::permittedPath(originalNode->getPath())) { if (!HandlerUtils::permittedPath(originalNode->getPath())) {
setErrorMessageHandler(*client, _("Invalid path!")); setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Invalid path!")));
return; return;
} }
if (originalNode->exists()) { 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; return;
} }
@ -152,7 +152,7 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
Common::DumpFile *f = new Common::DumpFile(); Common::DumpFile *f = new Common::DumpFile();
if (!f->open(originalNode->getPath(), true)) { if (!f->open(originalNode->getPath(), true)) {
delete f; delete f;
setErrorMessageHandler(*client, _("Failed to upload the file!")); setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("Failed to upload the file!")));
return; return;
} }
@ -181,7 +181,7 @@ void UploadFileClientHandler::handleBlockContent(Client *client) {
if (client->noMoreContent()) { if (client->noMoreContent()) {
// if no file field was found - failure // if no file field was found - failure
if (_uploadedFiles == 0) { if (_uploadedFiles == 0) {
setErrorMessageHandler(*client, _("No file was passed!")); setErrorMessageHandler(*client, HandlerUtils::toUtf8(_("No file was passed!")));
} else { } else {
setSuccessHandler(*client); setSuccessHandler(*client);
} }
@ -199,9 +199,9 @@ void UploadFileClientHandler::setSuccessHandler(Client &client) {
client, client,
Common::String::format( Common::String::format(
"%s<br/><a href=\"files?path=%s\">%s</a>", "%s<br/><a href=\"files?path=%s\">%s</a>",
_("Uploaded successfully!"), HandlerUtils::toUtf8(_("Uploaded successfully!")).c_str(),
client.queryParameter("path").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=") + (client.queryParameter("ajax") == "true" ? "/filesAJAX?path=" : "/files?path=") +
LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path")) LocalWebserver::urlEncodeQueryParameterValue(client.queryParameter("path"))

View file

@ -374,7 +374,7 @@ void OSystem_Android::initBackend() {
gettimeofday(&_startTime, 0); gettimeofday(&_startTime, 0);
_mixer = new Audio::MixerImpl(this, _audio_sample_rate); _mixer = new Audio::MixerImpl(_audio_sample_rate);
_mixer->setReady(true); _mixer->setReady(true);
_timer_thread_exit = false; _timer_thread_exit = false;

View file

@ -10,15 +10,21 @@ amigaosdist: $(EXECUTABLE)
ifdef DIST_FILES_ENGINEDATA ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(AMIGAOSPATH)/extras/ cp $(DIST_FILES_ENGINEDATA) $(AMIGAOSPATH)/extras/
endif 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. # 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 # 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 # "Program not found" error. Therefore we copy the script to the cwd and
# remove it again, once it has finished. # remove it again, once it has finished.
cp ${srcdir}/dists/amiga/RM2AG.rx . cp ${srcdir}/dists/amiga/RM2AG.rexx .
rx RM2AG.rx README.conv rx RM2AG.rexx README.conv
cp README.guide $(AMIGAOSPATH) cp README.guide $(AMIGAOSPATH)
rm RM2AG.rx rm RM2AG.rexx
rm README.conv rm README.conv
rm README.guide rm README.guide
cp $(DIST_FILES_DOCS) $(AMIGAOSPATH) cp $(DIST_FILES_DOCS) $(AMIGAOSPATH)

View file

@ -34,6 +34,7 @@
#include "backends/taskbar/macosx/macosx-taskbar.h" #include "backends/taskbar/macosx/macosx-taskbar.h"
#include "backends/dialogs/macosx/macosx-dialogs.h" #include "backends/dialogs/macosx/macosx-dialogs.h"
#include "backends/platform/sdl/macosx/macosx_wrapper.h" #include "backends/platform/sdl/macosx/macosx_wrapper.h"
#include "backends/fs/posix/posix-fs.h"
#include "common/archive.h" #include "common/archive.h"
#include "common/config-manager.h" #include "common/config-manager.h"
@ -199,6 +200,19 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
#endif // USE_DETECTLANG #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 OSystem_MacOSX::getScreenshotsPath() {
Common::String path = ConfMan.get("screenshotpath"); Common::String path = ConfMan.get("screenshotpath");
if (path.empty()) if (path.empty())

View file

@ -50,6 +50,8 @@ public:
virtual Common::String getScreenshotsPath(); virtual Common::String getScreenshotsPath();
protected: protected:
virtual Common::String getDefaultLogFileName();
// Override createAudioCDManager() to get our Mac-specific // Override createAudioCDManager() to get our Mac-specific
// version. // version.
virtual AudioCDManager *createAudioCDManager(); virtual AudioCDManager *createAudioCDManager();

View file

@ -269,55 +269,29 @@ void OSystem_POSIX::addSysArchivesToSearchSet(Common::SearchSet &s, int priority
OSystem_SDL::addSysArchivesToSearchSet(s, priority); OSystem_SDL::addSysArchivesToSearchSet(s, priority);
} }
Common::WriteStream *OSystem_POSIX::createLogFile() { Common::String OSystem_POSIX::getDefaultLogFileName() {
// 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 logFile; 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 // 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 // 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 // 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) { if (prefix == nullptr || !*prefix) {
prefix = getenv("HOME"); prefix = getenv("HOME");
if (prefix == nullptr) { if (prefix == nullptr) {
return 0; return Common::String();
} }
logFile = ".cache/"; logFile = ".cache/";
} }
logFile += "residualvm/logs"; logFile += "residualvm/logs";
#endif
if (!Posix::assureDirectoryExists(logFile, prefix)) { if (!Posix::assureDirectoryExists(logFile, prefix)) {
return 0; return Common::String();
} }
if (prefix) { return Common::String::format("%s/%s/residualvm.log", prefix, logFile.c_str());
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;
} }
bool OSystem_POSIX::displayLogFile() { bool OSystem_POSIX::displayLogFile() {

View file

@ -52,19 +52,8 @@ protected:
*/ */
Common::String _baseConfigName; 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::String getDefaultConfigFileName();
virtual Common::String getDefaultLogFileName();
virtual Common::WriteStream *createLogFile();
Common::String getXdgUserDir(const char *name); Common::String getXdgUserDir(const char *name);

View file

@ -78,7 +78,6 @@ Common::String OSystem_PS3::getDefaultConfigFileName() {
return PREFIX "/" + _baseConfigName; return PREFIX "/" + _baseConfigName;
} }
Common::WriteStream *OSystem_PS3::createLogFile() { Common::String OSystem_PS3::getDefaultLogFileName() {
Common::FSNode file(PREFIX "/residualvm.log"); return PREFIX "/residualvm.log";
return file.createWriteStream();
} }

View file

@ -40,8 +40,7 @@ protected:
Common::String _baseConfigName; Common::String _baseConfigName;
virtual Common::String getDefaultConfigFileName(); virtual Common::String getDefaultConfigFileName();
virtual Common::String getDefaultLogFileName();
virtual Common::WriteStream *createLogFile();
}; };
#endif #endif

View file

@ -8,11 +8,15 @@ ps3pkg: $(EXECUTABLE)
cp $(DIST_FILES_THEMES) ps3pkg/USRDIR/data/ cp $(DIST_FILES_THEMES) ps3pkg/USRDIR/data/
ifdef DIST_FILES_ENGINEDATA ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) ps3pkg/USRDIR/data/ 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 endif
cp $(DIST_FILES_DOCS) ps3pkg/USRDIR/doc/ cp $(DIST_FILES_DOCS) ps3pkg/USRDIR/doc/
cp $(srcdir)/dists/ps3/readme-ps3.md 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/ cp $(srcdir)/dists/ps3/ICON0.PNG ps3pkg/
sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO sfo.py -f $(srcdir)/dists/ps3/sfo.xml ps3pkg/PARAM.SFO
pkg.py --contentid UP0001-RESI12000_00-0000000000000000 ps3pkg/ residualvm-ps3.pkg pkg.py --contentid UP0001-RESI12000_00-0000000000000000 ps3pkg/ residualvm-ps3.pkg

View file

@ -119,24 +119,14 @@ Common::String OSystem_RISCOS::getDefaultConfigFileName() {
return "/<Choices$Write>/ResidualVM/residualvm"; return "/<Choices$Write>/ResidualVM/residualvm";
} }
Common::WriteStream *OSystem_RISCOS::createLogFile() { Common::String OSystem_RISCOS::getDefaultLogFileName() {
// 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 = "/<Choices$Write>/ResidualVM/Logs"; Common::String logFile = "/<Choices$Write>/ResidualVM/Logs";
if (!Riscos::assureDirectoryExists(logFile)) { if (!Riscos::assureDirectoryExists(logFile)) {
return 0; return Common::String();
} }
logFile += "/residualvm"; return logFile + "/residualvm";
Common::FSNode file(logFile);
Common::WriteStream *stream = file.createWriteStream();
if (stream)
_logFilePath = logFile;
return stream;
} }
#endif #endif

View file

@ -37,18 +37,8 @@ public:
virtual void logMessage(LogMessageType::Type type, const char *message); virtual void logMessage(LogMessageType::Type type, const char *message);
protected: 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::String getDefaultConfigFileName();
virtual Common::WriteStream *createLogFile(); virtual Common::String getDefaultLogFileName();
}; };
#endif #endif

View file

@ -1,7 +1,7 @@
ifeq ($(shell echo a | iconv --to-code=RISCOS-LATIN1//TRANSLIT >/dev/null 2>&1; echo $$?),0) ifeq ($(shell echo a | iconv --to-code=RISCOS-LATIN1//IGNORE//TRANSLIT >/dev/null 2>&1; echo $$?),0)
ENCODING=RISCOS-LATIN1//TRANSLIT ENCODING=RISCOS-LATIN1//IGNORE//TRANSLIT
else else
ENCODING=ISO-8859-1//TRANSLIT ENCODING=ISO-8859-1//IGNORE//TRANSLIT
endif endif
APP_NAME=!ResidualVM APP_NAME=!ResidualVM
@ -27,6 +27,9 @@ ifdef USE_OPENGL_SHADERS
mkdir -p $(APP_NAME)/data/shaders mkdir -p $(APP_NAME)/data/shaders
cp $(DIST_FILES_SHADERS) $(APP_NAME)/data/shaders/ cp $(DIST_FILES_SHADERS) $(APP_NAME)/data/shaders/
endif endif
ifdef DIST_FILES_VKEYBD
cp $(DIST_FILES_VKEYBD) $(APP_NAME)/data/
endif
ifdef DYNAMIC_MODULES ifdef DYNAMIC_MODULES
mkdir -p $(APP_NAME)/plugins mkdir -p $(APP_NAME)/plugins
cp $(PLUGINS) $(APP_NAME)/plugins/ cp $(PLUGINS) $(APP_NAME)/plugins/

View file

@ -191,8 +191,6 @@ void OSystem_SDL::initBackend() {
sdlDriverName[0] = '\0'; sdlDriverName[0] = '\0';
SDL_VideoDriverName(sdlDriverName, maxNameLen); SDL_VideoDriverName(sdlDriverName, maxNameLen);
#endif #endif
// Using printf rather than debug() here as debug()/logging
// is not active by this point.
debug(1, "Using SDL Video Driver \"%s\"", sdlDriverName); debug(1, "Using SDL Video Driver \"%s\"", sdlDriverName);
// ResidualVM specific code start // ResidualVM specific code start
@ -384,7 +382,7 @@ void OSystem_SDL::initSDL() {
if (ConfMan.hasKey("disable_sdl_parachute")) if (ConfMan.hasKey("disable_sdl_parachute"))
sdlFlags |= SDL_INIT_NOPARACHUTE; 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) if (SDL_Init(sdlFlags) == -1)
error("Could not initialize SDL: %s", SDL_GetError()); 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); _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 { Common::String OSystem_SDL::getSystemLanguage() const {
#if defined(USE_DETECTLANG) && !defined(WIN32) #if defined(USE_DETECTLANG) && !defined(WIN32)
// Activating current locale settings // Activating current locale settings
@ -538,18 +552,14 @@ Common::String OSystem_SDL::getSystemLanguage() const {
#endif // USE_DETECTLANG #endif // USE_DETECTLANG
} }
bool OSystem_SDL::hasTextInClipboard() {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
bool OSystem_SDL::hasTextInClipboard() {
return SDL_HasClipboardText() == SDL_TRUE; return SDL_HasClipboardText() == SDL_TRUE;
#else
return false;
#endif
} }
Common::String OSystem_SDL::getTextFromClipboard() { Common::String OSystem_SDL::getTextFromClipboard() {
if (!hasTextInClipboard()) return ""; if (!hasTextInClipboard()) return "";
#if SDL_VERSION_ATLEAST(2, 0, 0)
char *text = SDL_GetClipboardText(); char *text = SDL_GetClipboardText();
// The string returned by SDL is in UTF-8. Convert to the // The string returned by SDL is in UTF-8. Convert to the
// current TranslationManager encoding or ISO-8859-1. // current TranslationManager encoding or ISO-8859-1.
@ -566,13 +576,9 @@ Common::String OSystem_SDL::getTextFromClipboard() {
SDL_free(text); SDL_free(text);
return strText; return strText;
#else
return "";
#endif
} }
bool OSystem_SDL::setTextInClipboard(const Common::String &text) { 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 // The encoding we need to use is UTF-8. Assume we currently have the
// current TranslationManager encoding or ISO-8859-1. // current TranslationManager encoding or ISO-8859-1.
#ifdef USE_TRANSLATION #ifdef USE_TRANSLATION
@ -586,10 +592,8 @@ bool OSystem_SDL::setTextInClipboard(const Common::String &text) {
return status == 0; return status == 0;
} }
return SDL_SetClipboardText(text.c_str()) == 0; return SDL_SetClipboardText(text.c_str()) == 0;
#else
return false;
#endif
} }
#endif
uint32 OSystem_SDL::getMillis(bool skipRecord) { uint32 OSystem_SDL::getMillis(bool skipRecord) {
uint32 millis = SDL_GetTicks(); uint32 millis = SDL_GetTicks();

View file

@ -70,10 +70,12 @@ public:
virtual Common::String getSystemLanguage() const; virtual Common::String getSystemLanguage() const;
#if SDL_VERSION_ATLEAST(2, 0, 0)
// Clipboard // Clipboard
virtual bool hasTextInClipboard(); virtual bool hasTextInClipboard();
virtual Common::String getTextFromClipboard(); virtual Common::String getTextFromClipboard();
virtual bool setTextInClipboard(const Common::String &text); virtual bool setTextInClipboard(const Common::String &text);
#endif
virtual void setWindowCaption(const char *caption); virtual void setWindowCaption(const char *caption);
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
@ -101,6 +103,16 @@ protected:
bool _initedSDLnet; bool _initedSDLnet;
#endif #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 * Mixer manager that configures and setups SDL for
* the wrapped Audio::Mixer, the true mixer. * the wrapped Audio::Mixer, the true mixer.
@ -138,7 +150,8 @@ protected:
virtual AudioCDManager *createAudioCDManager(); virtual AudioCDManager *createAudioCDManager();
// Logging // Logging
virtual Common::WriteStream *createLogFile() { return 0; } virtual Common::String getDefaultLogFileName() { return Common::String(); }
virtual Common::WriteStream *createLogFile();
Backends::Log::Log *_logger; Backends::Log::Log *_logger;
}; };

View file

@ -110,7 +110,7 @@ void OSystem_Win32::initBackend() {
#if defined(USE_SPARKLE) #if defined(USE_SPARKLE)
// Initialize updates manager // Initialize updates manager
_updateManager = new Win32UpdateManager(); _updateManager = new Win32UpdateManager((SdlWindow_Win32*)_window);
#endif #endif
// Invoke parent implementation of this method // Invoke parent implementation of this method
@ -278,31 +278,22 @@ Common::String OSystem_Win32::getDefaultConfigFileName() {
return configFile; return configFile;
} }
Common::WriteStream *OSystem_Win32::createLogFile() { Common::String OSystem_Win32::getDefaultLogFileName() {
// Start out by resetting _logFilePath, so that in case
// of a failure, we know that no log file is open.
_logFilePath.clear();
char logFile[MAXPATHLEN]; char logFile[MAXPATHLEN];
// Use the Application Data directory of the user profile. // Use the Application Data directory of the user profile.
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, logFile) == S_OK) { 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 {
warning("Unable to access application data directory"); 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 { namespace {

View file

@ -46,20 +46,10 @@ public:
virtual Common::String getScreenshotsPath(); virtual Common::String getScreenshotsPath();
protected: 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::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. // version.
virtual AudioCDManager *createAudioCDManager(); virtual AudioCDManager *createAudioCDManager();

View file

@ -3,12 +3,12 @@
# #
# ResidualVM: Added DIST_FILES_SHADERS # 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 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
$(QUIET_WINDRES)$(WINDRES) -DHAVE_CONFIG_H $(WINDRESFLAGS) $(DEFINES) -I. -I$(srcdir) $(srcdir)/dists/residualvm.rc residualvmwinres.o
# Special target to create a win32 snapshot binary (for Inno Setup) # Special target to create a win32 snapshot binary (for Inno Setup)
win32dist: all win32dist: all
mkdir -p $(WIN32PATH) mkdir -p $(WIN32PATH)
mkdir -p $(WIN32PATH)/graphics
mkdir -p $(WIN32PATH)/doc mkdir -p $(WIN32PATH)/doc
$(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE)
cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt

View file

@ -23,6 +23,8 @@
#ifndef PLATFORM_SDL_WIN32_WRAPPER_H #ifndef PLATFORM_SDL_WIN32_WRAPPER_H
#define 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); HRESULT SHGetFolderPathFunc(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
// Helper functions // Helper functions

View file

@ -63,7 +63,9 @@ DefaultSaveFileManager::DefaultSaveFileManager(const Common::String &defaultSave
void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) { void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) {
clearError(); clearError();
if (!dir.exists()) { 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()) { } else if (!dir.isDirectory()) {
setError(Common::kPathNotDirectory, "The savepath '"+dir.getPath()+"' is not a directory"); 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. // Open the file for saving.
Common::WriteStream *const sf = fileNode.createWriteStream(); Common::WriteStream *const sf = fileNode.createWriteStream();
if (!sf)
return nullptr;
Common::OutSaveFile *const result = new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf); Common::OutSaveFile *const result = new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf);
// Add file to cache now that it exists. // 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) { 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()) { if (f.write(data.c_str(), data.size()) != data.size()) {
warning("DefaultSaveFileManager: failed to write timestamps data into '%s'", filename.c_str()); warning("DefaultSaveFileManager: failed to write timestamps data into '%s'", filename.c_str());
return; return;

View file

@ -38,9 +38,6 @@
#include "common/savefile.h" #include "common/savefile.h"
#include "common/textconsole.h" #include "common/textconsole.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
POSIXSaveFileManager::POSIXSaveFileManager() { POSIXSaveFileManager::POSIXSaveFileManager() {
@ -133,69 +130,4 @@ POSIXSaveFileManager::POSIXSaveFileManager() {
#endif #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 #endif

View file

@ -35,14 +35,6 @@
class POSIXSaveFileManager : public DefaultSaveFileManager { class POSIXSaveFileManager : public DefaultSaveFileManager {
public: public:
POSIXSaveFileManager(); 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 #endif

View file

@ -36,7 +36,7 @@ struct TimerSlot {
TimerSlot *next; 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) { void insertPrioQueue(TimerSlot *head, TimerSlot *newSlot) {

View file

@ -83,9 +83,6 @@ MacOSXUpdateManager::MacOSXUpdateManager() {
// Set the target of the new menu item // Set the target of the new menu item
[updateMenuItem setTarget:sparkleUpdater]; [updateMenuItem setTarget:sparkleUpdater];
// Finally give up our references to the objects
[menuItem release];
if (!ConfMan.hasKey("updates_check") if (!ConfMan.hasKey("updates_check")
|| ConfMan.getInt("updates_check") == Common::UpdateManager::kUpdateIntervalNotSupported) { || ConfMan.getInt("updates_check") == Common::UpdateManager::kUpdateIntervalNotSupported) {
setAutomaticallyChecksForUpdates(kUpdateStateDisabled); setAutomaticallyChecksForUpdates(kUpdateStateDisabled);

View file

@ -27,10 +27,12 @@
#include "backends/updates/win32/win32-updates.h" #include "backends/updates/win32/win32-updates.h"
#ifdef USE_SPARKLE #ifdef USE_SPARKLE
#include "backends/platform/sdl/win32/win32-window.h"
#include "common/translation.h" #include "common/translation.h"
#include "common/config-manager.h" #include "common/config-manager.h"
#include <time.h> #include <time.h>
#include <windows.h>
#include <winsparkle.h> #include <winsparkle.h>
/** /**
@ -50,10 +52,15 @@
* https://winsparkle.org/ * 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(); win_sparkle_init();
if (!ConfMan.hasKey("updates_check") if (!ConfMan.hasKey("updates_check")
@ -129,4 +136,20 @@ bool Win32UpdateManager::getLastUpdateCheckTimeAndDate(TimeDate &t) {
return true; 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 #endif

Some files were not shown because too many files have changed in this diff Show more