mirror of
https://github.com/TomHarte/CLK.git
synced 2025-11-23 21:17:42 +00:00
Compare commits
5 Commits
master
...
macOSPermi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74736d9723 | ||
|
|
5c20fcefc4 | ||
|
|
e9ba1c4ede | ||
|
|
6767ddde4c | ||
|
|
56a326d7db |
@@ -68,6 +68,7 @@ class MachineDocument:
|
|||||||
var fileObserver: CSFileContentChangeObserver?
|
var fileObserver: CSFileContentChangeObserver?
|
||||||
override func read(from url: URL, ofType typeName: String) throws {
|
override func read(from url: URL, ofType typeName: String) throws {
|
||||||
if let analyser = CSStaticAnalyser(fileAt: url) {
|
if let analyser = CSStaticAnalyser(fileAt: url) {
|
||||||
|
checkPermisions(analyser.mediaSet)
|
||||||
self.displayName = analyser.displayName
|
self.displayName = analyser.displayName
|
||||||
self.configureAs(analyser)
|
self.configureAs(analyser)
|
||||||
self.fileObserver = CSFileContentChangeObserver.init(url: url, handler: {
|
self.fileObserver = CSFileContentChangeObserver.init(url: url, handler: {
|
||||||
@@ -332,6 +333,7 @@ class MachineDocument:
|
|||||||
private func insertFile(_ URL: URL) {
|
private func insertFile(_ URL: URL) {
|
||||||
// Try to insert media.
|
// Try to insert media.
|
||||||
let mediaSet = CSMediaSet(fileAt: URL)
|
let mediaSet = CSMediaSet(fileAt: URL)
|
||||||
|
checkPermisions(mediaSet)
|
||||||
if !mediaSet.empty {
|
if !mediaSet.empty {
|
||||||
mediaSet.apply(to: self.machine)
|
mediaSet.apply(to: self.machine)
|
||||||
return
|
return
|
||||||
@@ -347,6 +349,10 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func checkPermisions(_ mediaSet: CSMediaSet) {
|
||||||
|
mediaSet.addPermissionHandler()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Input Management.
|
// MARK: - Input Management.
|
||||||
|
|
||||||
/// Upon a resign key, immediately releases all ongoing input mechanisms — any currently pressed keys,
|
/// Upon a resign key, immediately releases all ongoing input mechanisms — any currently pressed keys,
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
@class CSMachine;
|
@class CSMachine;
|
||||||
|
|
||||||
|
@interface CSMediaSet : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithFileAtURL:(NSURL *)url;
|
||||||
|
- (void)applyToMachine:(CSMachine *)machine;
|
||||||
|
- (void)addPermissionHandler;
|
||||||
|
|
||||||
|
@property(nonatomic, readonly) BOOL empty;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, CSMachineAmigaModel) {
|
typedef NS_ENUM(NSInteger, CSMachineAmigaModel) {
|
||||||
CSMachineAmigaModelA500,
|
CSMachineAmigaModelA500,
|
||||||
};
|
};
|
||||||
@@ -171,15 +181,7 @@ typedef int Kilobytes;
|
|||||||
|
|
||||||
@property(nonatomic, readonly, nullable) NSString *optionsNibName;
|
@property(nonatomic, readonly, nullable) NSString *optionsNibName;
|
||||||
@property(nonatomic, readonly) NSString *displayName;
|
@property(nonatomic, readonly) NSString *displayName;
|
||||||
|
@property(nonatomic, readonly, nonnull) CSMediaSet *mediaSet;
|
||||||
@end
|
|
||||||
|
|
||||||
@interface CSMediaSet : NSObject
|
|
||||||
|
|
||||||
- (instancetype)initWithFileAtURL:(NSURL *)url;
|
|
||||||
- (void)applyToMachine:(CSMachine *)machine;
|
|
||||||
|
|
||||||
@property(nonatomic, readonly) BOOL empty;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,113 @@
|
|||||||
|
|
||||||
#import "Clock_Signal-Swift.h"
|
#import "Clock_Signal-Swift.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct PermissionDelegate: public Storage::FileBundle::FileBundle::PermissionDelegate {
|
||||||
|
void validate_open(const std::string &, Storage::FileMode) {
|
||||||
|
// TODO.
|
||||||
|
// // (1) Does this file bundle have a base path?
|
||||||
|
// const auto path = bundle->base_path();
|
||||||
|
// if(!path.has_value()) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// NSString *pathName = [NSString stringWithUTF8String:path->c_str()];
|
||||||
|
//
|
||||||
|
// // (2) Can everything in that base path already be freely read?
|
||||||
|
// NSError *error;
|
||||||
|
// NSArray<NSString *> *allFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pathName error:&error];
|
||||||
|
// BOOL hasFullAccess = YES;
|
||||||
|
// for(NSString *file in allFiles) {
|
||||||
|
// FILE *pilot = fopen([[pathName stringByAppendingPathComponent:file] UTF8String], "rb");
|
||||||
|
// if(!pilot) {
|
||||||
|
// hasFullAccess = NO;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// fclose(pilot);
|
||||||
|
// }
|
||||||
|
// if(hasFullAccess) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // (3) Ask the user for permission.
|
||||||
|
// NSOpenPanel *request = [NSOpenPanel openPanel];
|
||||||
|
// request.prompt = @"Grant Permission";
|
||||||
|
// request.message = @"Please Grant Permission For Full Folder Access";
|
||||||
|
// request.canChooseFiles = NO;
|
||||||
|
// request.allowsMultipleSelection = NO;
|
||||||
|
// request.canChooseDirectories = YES;
|
||||||
|
// [request setDirectoryURL:[NSURL fileURLWithPath:pathName isDirectory:YES]];
|
||||||
|
//
|
||||||
|
// request.accessoryView = [NSTextField labelWithString:
|
||||||
|
// @"Clock Signal is sandboxed; it cannot access any of your files without explicit permission.\n"
|
||||||
|
// @"The type of program you are loading might require access to other files in its directory, which this "
|
||||||
|
// @"application does not currently have permission to do.\n"
|
||||||
|
// @"Please select 'Grant Permission' to give it permission to do so."
|
||||||
|
// ];
|
||||||
|
// request.accessoryViewDisclosed = YES;
|
||||||
|
// // TODO: use delegate further to shepherd user.
|
||||||
|
//
|
||||||
|
// const auto response = [request runModal];
|
||||||
|
// if(response != NSModalResponseOK) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Possibly substitute the base path, in case the one returned
|
||||||
|
// // is an indirection out of the sandbox.
|
||||||
|
// if(![request.URL isEqual:[NSURL fileURLWithPath:pathName]]) {
|
||||||
|
// NSLog(@"Need to substitute: %@", request.URL);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: bookmarkDataWithOptions on the URL, and store that somewhere for
|
||||||
|
// // later retrieval. Then try that again if the same directory presents itself.
|
||||||
|
}
|
||||||
|
void validate_erase(const std::string &) {
|
||||||
|
// Currently a no-op, as it so happens that the only machine that currently
|
||||||
|
// uses a file bundle is the Enterprise, and its semantics involve opening
|
||||||
|
// a file before it can be erased.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PermissionDelegate permission_delegate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation CSMediaSet {
|
||||||
|
Analyser::Static::Media _media;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithMedia:(Analyser::Static::Media)media {
|
||||||
|
self = [super init];
|
||||||
|
if(self) {
|
||||||
|
_media = media;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFileAtURL:(NSURL *)url {
|
||||||
|
self = [super init];
|
||||||
|
if(self) {
|
||||||
|
_media = Analyser::Static::GetMedia([url fileSystemRepresentation]);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)empty {
|
||||||
|
return _media.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applyToMachine:(CSMachine *)machine {
|
||||||
|
[machine applyMedia:_media];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addPermissionHandler {
|
||||||
|
for(const auto &bundle: _media.file_bundles) {
|
||||||
|
bundle->set_permission_delegate(&permission_delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation CSStaticAnalyser {
|
@implementation CSStaticAnalyser {
|
||||||
Analyser::Static::TargetList _targets;
|
Analyser::Static::TargetList _targets;
|
||||||
}
|
}
|
||||||
@@ -438,26 +545,12 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
|||||||
return _targets;
|
return _targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
- (nonnull CSMediaSet *)mediaSet {
|
||||||
|
Analyser::Static::Media net;
|
||||||
@implementation CSMediaSet {
|
for(const auto &target: _targets) {
|
||||||
Analyser::Static::Media _media;
|
net += target->media;
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithFileAtURL:(NSURL *)url {
|
|
||||||
self = [super init];
|
|
||||||
if(self) {
|
|
||||||
_media = Analyser::Static::GetMedia([url fileSystemRepresentation]);
|
|
||||||
}
|
}
|
||||||
return self;
|
return [[CSMediaSet alloc] initWithMedia:net];
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applyToMachine:(CSMachine *)machine {
|
|
||||||
[machine applyMedia:_media];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)empty {
|
|
||||||
return _media.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -26,10 +26,26 @@ std::optional<std::string> LocalFSFileBundle::key_file() {
|
|||||||
return key_file_;
|
return key_file_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalFSFileBundle::set_permission_delegate(PermissionDelegate *const delegate) {
|
||||||
|
permission_delegate_ = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) {
|
Storage::FileHolder LocalFSFileBundle::open(const std::string &name, const Storage::FileMode mode) {
|
||||||
return Storage::FileHolder(base_path_ + name, mode);
|
const auto full_name = base_path_ + name;
|
||||||
|
if(permission_delegate_) {
|
||||||
|
permission_delegate_->validate_open(full_name, mode);
|
||||||
|
}
|
||||||
|
return Storage::FileHolder(full_name, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LocalFSFileBundle::erase(const std::string &name) {
|
bool LocalFSFileBundle::erase(const std::string &name) {
|
||||||
|
const auto full_name = base_path_ + name;
|
||||||
|
if(permission_delegate_) {
|
||||||
|
permission_delegate_->validate_erase(full_name);
|
||||||
|
}
|
||||||
return !remove((base_path_ + name).c_str());
|
return !remove((base_path_ + name).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> LocalFSFileBundle::base_path() {
|
||||||
|
return base_path_;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,9 +23,17 @@ namespace Storage::FileBundle {
|
|||||||
bundles in the future.
|
bundles in the future.
|
||||||
*/
|
*/
|
||||||
struct FileBundle {
|
struct FileBundle {
|
||||||
|
struct PermissionDelegate {
|
||||||
|
virtual void validate_open(const std::string &, FileMode) = 0;
|
||||||
|
virtual void validate_erase(const std::string &) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
virtual std::optional<std::string> key_file() = 0;
|
virtual std::optional<std::string> key_file() = 0;
|
||||||
virtual FileHolder open(const std::string &, FileMode) = 0;
|
virtual FileHolder open(const std::string &, FileMode) = 0;
|
||||||
virtual bool erase(const std::string &) = 0;
|
virtual bool erase(const std::string &) = 0;
|
||||||
|
|
||||||
|
virtual std::optional<std::string> base_path() = 0;
|
||||||
|
virtual void set_permission_delegate(PermissionDelegate *) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -36,9 +44,13 @@ struct LocalFSFileBundle: public FileBundle {
|
|||||||
FileHolder open(const std::string &, FileMode) override;
|
FileHolder open(const std::string &, FileMode) override;
|
||||||
bool erase(const std::string &) override;
|
bool erase(const std::string &) override;
|
||||||
|
|
||||||
|
std::optional<std::string> base_path() override;
|
||||||
|
void set_permission_delegate(PermissionDelegate *) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string key_file_;
|
std::string key_file_;
|
||||||
std::string base_path_;
|
std::string base_path_;
|
||||||
|
PermissionDelegate *permission_delegate_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user