Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Kyle Hickinson
committed
Nov 3, 2012
0 parents
commit d1ef719
Showing
45 changed files
with
6,693 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,4 @@ | |||
xcuserdata | |||
.DS_Store | |||
build | |||
DerivedData |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,19 @@ | |||
// | |||
// RFAppDelegate.h | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-15. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import <Cocoa/Cocoa.h> | |||
#import "RFReflect.h" | |||
|
|||
@interface RFAppDelegate : NSObject <NSApplicationDelegate> | |||
|
|||
@property (nonatomic, strong) RFReflect *reflect; | |||
@property (strong) IBOutlet NSWindow *preferencesWindow; | |||
|
|||
- (IBAction)randomizePasscodeNow:(id)sender; | |||
|
|||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,57 @@ | |||
// | |||
// RFAppDelegate.m | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-15. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import "RFAppDelegate.h" | |||
#import <SystemConfiguration/SystemConfiguration.h> | |||
|
|||
@implementation RFAppDelegate | |||
|
|||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification | |||
{ | |||
NSString *computerName = (__bridge NSString *)SCDynamicStoreCopyComputerName(NULL, NULL); | |||
if (computerName == nil) { | |||
computerName = @""; | |||
} | |||
|
|||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults]; | |||
|
|||
// I should probably throw all these keys in a header file so I can stop trying to remember all of them... | |||
[defaults registerDefaults:@{ | |||
@"server-start-on-launch" : @YES, | |||
@"passcode-enabled" : @NO, | |||
@"passcode" : @"", | |||
@"passcode-randomize" : @YES, | |||
@"passcode-simple" : @YES, | |||
@"bonjour-use-name" : @NO, | |||
@"bonjour-name" : computerName, | |||
@"bonjour-use-type" : @NO, | |||
@"bonjour-type" : @"_reflect", | |||
@"recent-files" : @[ ] | |||
}]; | |||
|
|||
// (Maybe) Start Reflect server. | |||
self.reflect = [[RFReflect alloc] init]; | |||
} | |||
|
|||
- (void)randomizePasscodeNow:(id)sender | |||
{ | |||
[self.reflect randomizePasscode]; | |||
} | |||
|
|||
- (void)_showPreferences:(id)sender | |||
{ | |||
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; | |||
[self.preferencesWindow makeKeyAndOrderFront:nil]; | |||
} | |||
|
|||
- (void)_quit | |||
{ | |||
[[NSApplication sharedApplication] terminate:self]; | |||
} | |||
|
|||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,36 @@ | |||
// | |||
// RFConnectedService.h | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-20. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import <Foundation/Foundation.h> | |||
|
|||
/** | |||
The RFConnectedService class represents a service connected to the server. | |||
This would be written usually as a struct, but we can't have objects in a struct | |||
and I don't feel like bridging between CF and NS objects every time. | |||
*/ | |||
@interface RFConnectedService : NSObject | |||
|
|||
/** Whether or not the service has entered a passcode—if they need too—and is ready to receive data. */ | |||
@property (nonatomic, assign) BOOL enteredPasscode; | |||
|
|||
/** The file descriptor for the socket that is opened. */ | |||
@property (nonatomic, assign) CGFloat socketDescriptor; | |||
|
|||
/** The input stream for this service. */ | |||
@property (nonatomic, strong) NSInputStream *inputStream; | |||
|
|||
/** The output stream for this service. */ | |||
@property (nonatomic, strong) NSOutputStream *outputStream; | |||
|
|||
/** | |||
Disconnect this client by closing its streams and closing the socket. | |||
*/ | |||
- (void)disconnect; | |||
|
|||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,39 @@ | |||
// | |||
// RFConnectedService.m | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-20. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import "RFConnectedService.h" | |||
|
|||
@implementation RFConnectedService | |||
|
|||
- (BOOL)isEqual:(id)object | |||
{ | |||
if ([object isKindOfClass:[RFConnectedService class]]) { | |||
return ((RFConnectedService *)object).socketDescriptor == self.socketDescriptor; | |||
} | |||
return NO; | |||
} | |||
|
|||
- (id)init | |||
{ | |||
if ((self = [super init])) { | |||
self.enteredPasscode = NO; | |||
self.socketDescriptor = 0; | |||
} | |||
return self; | |||
} | |||
|
|||
- (void)disconnect | |||
{ | |||
[self.inputStream close]; | |||
[self.outputStream close]; | |||
[self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; | |||
[self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; | |||
close(self.socketDescriptor); | |||
} | |||
|
|||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,103 @@ | |||
// | |||
// RFFileMonitor.h | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-21. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import <Foundation/Foundation.h> | |||
|
|||
extern NSString * const RFFileMonitorErrorDomain; | |||
|
|||
/** | |||
An enumeration of errors that could appear in the fileMonitor:failedToBeginMonitoringWithError | |||
*/ | |||
typedef enum : NSInteger { | |||
/** | |||
The file failed to open properly. | |||
*/ | |||
RFFileMonitorFailedOpenError = -200L, | |||
|
|||
/** | |||
The path given was a directory, which is currently unsupported by RFFileMonitor | |||
*/ | |||
RFFileMonitorFileIsDirectoryError = -201L, | |||
|
|||
/** | |||
The given path did not exist. | |||
*/ | |||
RFFileMonitorFileDoesntExistError = -202L, | |||
|
|||
/** | |||
The call to kqueue() faliled. | |||
*/ | |||
RFFileMonitorQueueFailedError = -203L, | |||
|
|||
} RFFileMonitorErrorCode; | |||
|
|||
@class RFFileMonitor; | |||
|
|||
/** | |||
The file monitor delegate communicates when a path changed or when erorrs occur. | |||
*/ | |||
@protocol RFFileMonitorDelegate<NSObject> | |||
@required | |||
|
|||
/** | |||
A file under watch changed. | |||
@param fileMonitor The sender | |||
@param path The path of the file changed. | |||
*/ | |||
- (void)fileMonitor:(RFFileMonitor *)fileMonitor pathDidChange:(NSString *)path; | |||
|
|||
@optional | |||
|
|||
/** | |||
An error occured while attempting to begin monitoring a file. | |||
@param fileMonitor The sender | |||
@param error The error that occured with the domain RFFileMonitorErrorDomain with possible codes in | |||
the RFFileMonitorErrorCode enumeration. | |||
*/ | |||
- (void)fileMonitor:(RFFileMonitor *)fileMonitor failedToBeginMonitoringWithError:(NSError *)error; | |||
|
|||
/** | |||
The file monitor successfully began monitoring the file at path. | |||
@param fileMonitor The sender | |||
@param path The path to the file that is being monitored. | |||
*/ | |||
- (void)fileMonitor:(RFFileMonitor *)fileMonitor didBeginMonitoringPath:(NSString *)path; | |||
|
|||
@end | |||
|
|||
/** | |||
The RFFileMonitor class allows the developer to watch a specific file for given changes. | |||
At the moment RFFileMonitor only allows watching 1 file at a time, but can be easily | |||
modified to watch multiple files if needed, or even directories using FSEvent APIs. | |||
*/ | |||
@interface RFFileMonitor : NSObject { | |||
struct { | |||
unsigned respondsToFailedBegin:1; | |||
unsigned respondsToDidBeginMonitoring:1; | |||
} _flags; | |||
} | |||
|
|||
/** The delegate */ | |||
@property (nonatomic, weak) id<RFFileMonitorDelegate> delegate; | |||
|
|||
/** | |||
Begin monitoring a file with a given path. | |||
@param path The path to the file to monitor. | |||
*/ | |||
- (void)beginMonitoringForPath:(NSString *)path; | |||
|
|||
/** | |||
Stop monitoring the file that was originally watched after calling beginMonitoringForPath: | |||
*/ | |||
- (void)stopMonitoring; | |||
|
|||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,131 @@ | |||
// | |||
// RFFileMonitor.m | |||
// Reflect | |||
// | |||
// Created by Kyle Hickinson on 2012-10-21. | |||
// Copyright (c) 2012 Kyle Hickinson. All rights reserved. | |||
// | |||
|
|||
#import "RFFileMonitor.h" | |||
|
|||
#include <sys/types.h> | |||
#include <sys/event.h> | |||
#include <sys/time.h> | |||
#include <fcntl.h> | |||
|
|||
NSString * const RFFileMonitorErrorDomain = @"RFFileMonitorErrorDomain"; | |||
|
|||
@interface RFFileMonitor () | |||
|
|||
@property (nonatomic, copy) NSString *path; | |||
@property (nonatomic, assign) int fileDescriptor; | |||
@property (nonatomic, assign) int queue; | |||
@property (nonatomic, strong) NSThread *monitoringThread; | |||
|
|||
@end | |||
|
|||
@implementation RFFileMonitor | |||
|
|||
- (void)beginMonitoringForPath:(NSString *)path | |||
{ | |||
if (self.queue > 0) { | |||
// Currently already watching a file... | |||
// I'm going to go ahead and just close it, maybe offer user a choice or just fail? | |||
[self stopMonitoring]; | |||
} | |||
|
|||
BOOL isDirectory = NO; | |||
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]) { | |||
if (_flags.respondsToFailedBegin) { | |||
[self.delegate fileMonitor:self failedToBeginMonitoringWithError:[NSError errorWithDomain:RFFileMonitorErrorDomain code:RFFileMonitorFileDoesntExistError userInfo:nil]]; | |||
} | |||
} | |||
|
|||
// File exists, but if its a directory we're opting out. Though we could watch a directory using the FSEventStream's. | |||
if (isDirectory) { | |||
if (_flags.respondsToFailedBegin) { | |||
[self.delegate fileMonitor:self failedToBeginMonitoringWithError:[NSError errorWithDomain:RFFileMonitorErrorDomain code:RFFileMonitorFileIsDirectoryError userInfo:nil]]; | |||
} | |||
return; | |||
} | |||
|
|||
// Get the file descriptor. | |||
self.fileDescriptor = open([path UTF8String], O_EVTONLY); | |||
|
|||
if (self.fileDescriptor < 0) { | |||
if (_flags.respondsToFailedBegin) { | |||
[self.delegate fileMonitor:self failedToBeginMonitoringWithError:[NSError errorWithDomain:RFFileMonitorErrorDomain code:RFFileMonitorFailedOpenError userInfo:nil]]; | |||
} | |||
return; | |||
}; | |||
|
|||
// Now setup kqueue. | |||
self.path = path; | |||
self.queue = kqueue(); | |||
if (self.queue < 0) { | |||
if (_flags.respondsToFailedBegin) { | |||
[self.delegate fileMonitor:self failedToBeginMonitoringWithError:[NSError errorWithDomain:RFFileMonitorErrorDomain code:RFFileMonitorQueueFailedError userInfo:nil]]; | |||
} | |||
close(self.fileDescriptor); | |||
return; | |||
} | |||
|
|||
// Start the monitoring thread, and we're off! | |||
self.monitoringThread = [[NSThread alloc] initWithTarget:self selector:@selector(_monitorInBackground) object:nil]; | |||
[self.monitoringThread start]; | |||
|
|||
if (_flags.respondsToDidBeginMonitoring) { | |||
[self.delegate fileMonitor:self didBeginMonitoringPath:self.path]; | |||
} | |||
} | |||
|
|||
- (void)stopMonitoring | |||
{ | |||
close(self.fileDescriptor); | |||
close(self.queue); | |||
[self.monitoringThread cancel]; | |||
} | |||
|
|||
- (void)setDelegate:(id<RFFileMonitorDelegate>)delegate | |||
{ | |||
_delegate = delegate; | |||
memset(&_flags, 0, sizeof(_flags)); | |||
|
|||
if (_delegate) { | |||
_flags.respondsToDidBeginMonitoring = [_delegate respondsToSelector:@selector(fileMonitor:didBeginMonitoringPath:)]; | |||
_flags.respondsToFailedBegin = [_delegate respondsToSelector:@selector(fileMonitor:failedToBeginMonitoringWithError:)]; | |||
} | |||
} | |||
|
|||
#pragma mark - Private | |||
|
|||
- (void)_monitorInBackground | |||
{ | |||
struct kevent change; | |||
struct kevent event; | |||
struct timespec timeout; | |||
|
|||
// Setup kevent changes | |||
EV_SET(&change, self.fileDescriptor, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_DELETE | NOTE_WRITE, 0, 0); | |||
|
|||
// Setup timeout | |||
timeout.tv_sec = 0; | |||
timeout.tv_nsec = 500000000; | |||
|
|||
// Register for events | |||
for (;;) | |||
{ | |||
// Make sure we check if the thread was cancelled during each loop run. | |||
if ([[NSThread currentThread] isCancelled]) { | |||
[NSThread exit]; | |||
} | |||
|
|||
if (kevent(self.queue, &change, 1, &event, 1, &timeout) != -1) { | |||
if (event.fflags & NOTE_WRITE || event.fflags & NOTE_DELETE) { | |||
[self.delegate fileMonitor:self pathDidChange:self.path]; | |||
} | |||
} | |||
} | |||
} | |||
|
|||
@end |
Oops, something went wrong.