Skip to content

Commit

Permalink
Implemented live blocklist additions and lock file usage for increase…
Browse files Browse the repository at this point in the history
…d reliability, esp. with multiple users.
  • Loading branch information
cstigler committed Apr 11, 2009
1 parent caf1b8d commit 9d26408
Show file tree
Hide file tree
Showing 24 changed files with 6,190 additions and 1,809 deletions.
465 changes: 465 additions & 0 deletions AddToBlock.xib

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions AppController.h
Expand Up @@ -20,10 +20,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// Forward declaration to avoid compiler weirdness
@class TimerWindowController;

#import <Cocoa/Cocoa.h>
#import "DomainListWindowController.h"
#import "TimerWindowController.h"
#import <Security/Security.h>
#import <SystemConfiguration/SCNetwork.h>
#import <unistd.h>

// The main controller for the SelfControl app, which includes several methods
// to handle command flow and acts as delegate for the initial window.
Expand All @@ -50,11 +55,11 @@
// the block duration in words (hours and minutes).
- (IBAction)updateTimeSliderDisplay:(id)sender;

// Gets authorization for and then immediately removes the block by calling
/* // Gets authorization for and then immediately removes the block by calling
// SelfControl's helper tool with the appropriate arguments. This can be used
// for testing, but should not be called at all during normal execution of the
// program.
- (void)removeBlock;
- (void)removeBlock; */

// Called when the main Start button is clicked. Gets authorization for and
// then immediately adds the block by calling SelfControl's helper tool with the
Expand Down Expand Up @@ -91,12 +96,22 @@
// was just changed a few seconds ago.
- (BOOL)networkConnectionIsAvailable;

// Called whenever the selection of sound to play in the Preferences menu changes.
// Plays the sound so that the user can "sample" them.
- (IBAction)soundSelectionChanged:(id)sender;

// Called by timerWindowController_ after its sheet returns, to add a specified
// host to the blacklist (and refresh the block to use the new blacklist)
- (void)addToBlockList:(NSString*)host;

// Property allows initialWindow to be accessed from TimerWindowController
// @property (retain, nonatomic, readonly) id initialWindow;

// Changed property to manual accessor for pre-Leopard compatibility
- (id)initialWindow;

// Getter/setter for domainListWindowController to be accessed from a TimerWindowController
- (id)domainListWindowController;
- (void)setDomainListWindowController:(id)newController;

@end
154 changes: 144 additions & 10 deletions AppController.m
Expand Up @@ -21,9 +21,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#import "AppController.h"
#import <Security/Security.h>
#import <SystemConfiguration/SCNetwork.h>
#import <unistd.h>

NSString* const kSelfControlLockFilePath = @"/etc/SelfControl.lock";

@implementation AppController

Expand Down Expand Up @@ -220,7 +219,7 @@ - (void)closeTimerWindow {
[timerWindowController_ close];
}

- (void)removeBlock {
/* - (void)removeBlock {
// Remember not to use this method, it defeats the point of SelfControl!
[defaults_ synchronize];
Expand Down Expand Up @@ -261,7 +260,7 @@ - (void)removeBlock {
NSLog(@"WARNING: Helper tool returned failure status code %d.");
return;
}
}
} */

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp setDelegate: self];
Expand All @@ -287,11 +286,15 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

- (BOOL)selfControlLaunchDaemonIsLoaded {
[defaults_ synchronize];
NSDate* blockStartedDate = [defaults_ objectForKey:@"BlockStartedDate"];
return (![blockStartedDate isEqualToDate: [NSDate distantFuture]]);
// If the user deletes the defaults and therefore destroys our flag (that the
// blockStartedDate will be set to distantFuture if no block is on) our program
// will get very confused. Oh well.
// NSDate* blockStartedDate = [defaults_ objectForKey:@"BlockStartedDate"];
/*BOOL cur = (![blockStartedDate isEqualToDate: [NSDate distantFuture]]);
BOOL next = [[NSFileManager defaultManager] fileExistsAtPath: kSelfControlLockFilePath];
if(cur != next)
NSLog(@"cur isn't next!");
return next; */
return [[NSFileManager defaultManager] fileExistsAtPath: kSelfControlLockFilePath];
/* return (![blockStartedDate isEqualToDate: [NSDate distantFuture]]);
return [[NSFileManager defaultManager] fileExistsAtPath: kSelfControlLockFilePath]; */
}

- (IBAction)showDomainList:(id)sender {
Expand Down Expand Up @@ -357,6 +360,127 @@ - (IBAction)soundSelectionChanged:(id)sender {
[alertSound play];
}

- (void)addToBlockList:(NSString*)host {
if(host == nil)
return;

host = [[host stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString];

// Remove "http://" if a user tried to put that in
NSArray* splitString = [host componentsSeparatedByString: @"http://"];
for(int i = 0; i < [splitString count]; i++) {
if(![[splitString objectAtIndex: i] isEqual: @""]) {
host = [splitString objectAtIndex: i];
break;
}
}

// Delete anything after a "/" in case a user tried to copy-paste a web address.
host = [[host componentsSeparatedByString: @"/"] objectAtIndex: 0];

if([host isEqualToString: @""])
return;

NSMutableArray* list = [[defaults_ arrayForKey: @"HostBlacklist"] mutableCopy];
[list addObject: host];
[defaults_ setObject: list forKey: @"HostBlacklist"];
[defaults_ synchronize];

if(([[defaults_ objectForKey:@"BlockStartedDate"] isEqualToDate: [NSDate distantFuture]])) {
// This method shouldn't be getting called, a block is not on (block started
// is in the distantFuture) so the Start button should be disabled.
// Maybe the UI didn't get properly refreshed, so try refreshing it again
// before we return.
[self refreshUserInterface];

// Reverse the blacklist change made before we fail
NSMutableArray* list = [[defaults_ arrayForKey: @"HostBlacklist"] mutableCopy];
[list removeLastObject];
[defaults_ setObject: list forKey: @"HostBlacklist"];

return;
}

if([defaults_ boolForKey: @"VerifyInternetConnection"] && ![self networkConnectionIsAvailable]) {
NSAlert* networkUnavailableAlert = [[[NSAlert alloc] init] autorelease];
[networkUnavailableAlert setMessageText: @"No network connection detected"];
[networkUnavailableAlert setInformativeText:@"A block cannot be started without a working network connection. You can override this setting in Preferences."];
[networkUnavailableAlert addButtonWithTitle: @"Cancel"];
[networkUnavailableAlert addButtonWithTitle: @"Network Diagnostics..."];
if([networkUnavailableAlert runModal] == NSAlertFirstButtonReturn) {
// User clicked cancel
// Reverse the blacklist change made before we fail
NSMutableArray* list = [[defaults_ arrayForKey: @"HostBlacklist"] mutableCopy];
[list removeLastObject];
[defaults_ setObject: list forKey: @"HostBlacklist"];

return;
}

// If the user selected Network Diagnostics, launch an assisant to help them.
// apple.com is an arbitrary host chosen to pass to Network Diagnostics.
CFURLRef url = CFURLCreateWithString(NULL, CFSTR("http://apple.com"), NULL);
CFNetDiagnosticRef diagRef = CFNetDiagnosticCreateWithURL(NULL, url);
CFNetDiagnosticDiagnoseProblemInteractively(diagRef);

// Reverse the blacklist change made before we fail
NSMutableArray* list = [[defaults_ arrayForKey: @"HostBlacklist"] mutableCopy];
[list removeLastObject];
[defaults_ setObject: list forKey: @"HostBlacklist"];

return;
}

AuthorizationRef authorizationRef;
char* helperToolPath = [self selfControlHelperToolPathUTF8String];
int helperToolPathSize = strlen(helperToolPath);
AuthorizationItem right = {
kAuthorizationRightExecute,
helperToolPathSize,
helperToolPath,
0
};
AuthorizationRights authRights = {
1,
&right
};
AuthorizationFlags myFlags = kAuthorizationFlagDefaults |
kAuthorizationFlagExtendRights |
kAuthorizationFlagInteractionAllowed;
OSStatus status;

status = AuthorizationCreate (&authRights,
kAuthorizationEmptyEnvironment,
myFlags,
&authorizationRef);

if(status) {
NSLog(@"ERROR: Failed to authorize block refresh.");

// Reverse the blacklist change made before we fail
NSMutableArray* list = [[defaults_ arrayForKey: @"HostBlacklist"] mutableCopy];
[list removeLastObject];
[defaults_ setObject: list forKey: @"HostBlacklist"];

return;
}

// We need to pass our UID to the helper tool. It needs to know whose defaults
// it should read in order to properly load the blacklist.
char uidString[10];
snprintf(uidString, sizeof(uidString), "%d", getuid());

char* args[] = { uidString, "--refresh", NULL };
status = AuthorizationExecuteWithPrivileges(authorizationRef,
helperToolPath,
kAuthorizationFlagDefaults,
args,
NULL);
if(status) {
NSLog(@"WARNING: Authorized execution of helper tool returned failure status code %d", status);
}
}

- (void)dealloc {
[domainListWindowController_ release];
[timerWindowController_ release];
Expand All @@ -376,4 +500,14 @@ - (id)initialWindow {
return initialWindow_;
}

- (id)domainListWindowController {
return domainListWindowController_;
}

- (void)setDomainListWindowController:(id)newController {
[newController retain];
[domainListWindowController_ release];
domainListWindowController_ = newController;
}

@end
3 changes: 3 additions & 0 deletions DomainListWindowController.h
Expand Up @@ -25,6 +25,9 @@
#import "HostImporter.h"
#import "ThunderbirdPreferenceParser.h"

// This is a reference to the kSelfControlLockFilePath const variable in AppController.m
extern NSString* const kSelfControlLockFilePath;

// A subclass of NSWindowController created to manage the domain list (actually
// host list, but domain list seems more understandable to inexperienced users
// and experienced users will figure out they can put in IP addresses) window,
Expand Down
7 changes: 7 additions & 0 deletions DomainListWindowController.m
Expand Up @@ -28,6 +28,8 @@ @implementation DomainListWindowController
- (DomainListWindowController*)init {
self = [super initWithWindowNibName:@"DomainList"];

// [domainListTableView_ retain];

defaults_ = [NSUserDefaults standardUserDefaults];

NSArray* curArray = [defaults_ arrayForKey: @"HostBlacklist"];
Expand Down Expand Up @@ -223,4 +225,9 @@ - (IBAction)importOutgoingMailServersFromMail:(id)sender {
object: nil];
}

- (void)dealloc {
// [domainListTableView_ release];
[super dealloc];
}

@end
21 changes: 18 additions & 3 deletions English.lproj/DomainList.xib
Expand Up @@ -8,7 +8,7 @@
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="2"/>
<integer value="12"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
Expand All @@ -26,7 +26,7 @@
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="1001">
<string key="NSClassName">NSWindowController</string>
<string key="NSClassName">DomainListWindowController</string>
</object>
<object class="NSCustomObject" id="1003">
<string key="NSClassName">FirstResponder</string>
Expand Down Expand Up @@ -471,6 +471,14 @@ ARcABAAAAAEAAAACARwAAwAAAAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA</bytes>
</object>
<int key="connectionID">107</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">domainListTableView_</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="206820569"/>
</object>
<int key="connectionID">108</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
Expand Down Expand Up @@ -799,7 +807,7 @@ ARcABAAAAAEAAAACARwAAwAAAAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA</bytes>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">107</int>
<int key="maxID">108</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
Expand Down Expand Up @@ -857,6 +865,13 @@ ARcABAAAAAEAAAACARwAAwAAAAEAAQAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA</bytes>
<string key="minorKey">DomainListWindowController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSApplication</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">NSApplication+SystemVersion.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
Expand Down
6 changes: 4 additions & 2 deletions HelperMain.h
Expand Up @@ -20,8 +20,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.


#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import "IPFirewall.h"
#import "LaunchctlHelper.h"
#import <unistd.h>

// The main class for SelfControl's helper tool to be run by launchd with high
// privileges in order to handle the root-only configuration.
Expand Down

0 comments on commit 9d26408

Please sign in to comment.