Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Hickinson committed Nov 3, 2012
0 parents commit d1ef719
Show file tree
Hide file tree
Showing 45 changed files with 6,693 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
xcuserdata
.DS_Store
build
DerivedData
388 changes: 388 additions & 0 deletions Mac/Reflect.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions Mac/Reflect/RFAppDelegate.h
@@ -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
57 changes: 57 additions & 0 deletions Mac/Reflect/RFAppDelegate.m
@@ -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
36 changes: 36 additions & 0 deletions Mac/Reflect/RFConnectedService.h
@@ -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
39 changes: 39 additions & 0 deletions Mac/Reflect/RFConnectedService.m
@@ -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
103 changes: 103 additions & 0 deletions Mac/Reflect/RFFileMonitor.h
@@ -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
131 changes: 131 additions & 0 deletions Mac/Reflect/RFFileMonitor.m
@@ -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

0 comments on commit d1ef719

Please sign in to comment.