Permalink
Browse files

Implemented live blocklist additions and lock file usage for increase…

…d reliability, esp. with multiple users.
  • Loading branch information...
cstigler committed Apr 11, 2009
1 parent caf1b8d commit 9d26408aa96502164cc11796869a04d04ca1ddc2
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -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.
@@ -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
@@ -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
View
@@ -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
@@ -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];
@@ -261,7 +260,7 @@ - (void)removeBlock {
NSLog(@"WARNING: Helper tool returned failure status code %d.");
return;
}
-}
+} */
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp setDelegate: self];
@@ -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 {
@@ -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];
@@ -376,4 +500,14 @@ - (id)initialWindow {
return initialWindow_;
}
+- (id)domainListWindowController {
+ return domainListWindowController_;
+}
+
+- (void)setDomainListWindowController:(id)newController {
+ [newController retain];
+ [domainListWindowController_ release];
+ domainListWindowController_ = newController;
+}
+
@end
@@ -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,
@@ -28,6 +28,8 @@ @implementation DomainListWindowController
- (DomainListWindowController*)init {
self = [super initWithWindowNibName:@"DomainList"];
+ // [domainListTableView_ retain];
+
defaults_ = [NSUserDefaults standardUserDefaults];
NSArray* curArray = [defaults_ arrayForKey: @"HostBlacklist"];
@@ -223,4 +225,9 @@ - (IBAction)importOutgoingMailServersFromMail:(id)sender {
object: nil];
}
+- (void)dealloc {
+ // [domainListTableView_ release];
+ [super dealloc];
+}
+
@end
@@ -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>
@@ -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>
@@ -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">
@@ -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">
@@ -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>
View
@@ -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.
Oops, something went wrong.

0 comments on commit 9d26408

Please sign in to comment.