Skip to content

Commit 120e0ab

Browse files
committed
Add ability to run --install on command-line tool with a blockfile and end date (instead of using settings)
1 parent 415a596 commit 120e0ab

File tree

7 files changed

+114
-19
lines changed

7 files changed

+114
-19
lines changed

AppController.m

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -890,22 +890,15 @@ - (IBAction)save:(id)sender {
890890

891891
/* if successful, save file under designated name */
892892
if (runResult == NSOKButton) {
893-
[defaults_ synchronize];
894-
NSString* err;
895-
NSDictionary* saveDict = @{@"HostBlacklist": [settings_ valueForKey: @"Blocklist"],
896-
@"BlockAsWhitelist": [settings_ valueForKey: @"BlockAsWhitelist"]};
897-
NSData* saveData = [NSPropertyListSerialization dataFromPropertyList: saveDict format: NSPropertyListBinaryFormat_v1_0 errorDescription: &err];
898-
if(err) {
899-
NSError* displayErr = [NSError errorWithDomain: kSelfControlErrorDomain code: -902 userInfo: @{NSLocalizedDescriptionKey: [@"Error 902: " stringByAppendingString: err]}];
893+
NSString* errDescription;
894+
[SCUtilities writeBlocklistToFileURL: sp.URL settings: settings_ errorDescription: &errDescription];
895+
896+
if(errDescription) {
897+
NSError* displayErr = [NSError errorWithDomain: kSelfControlErrorDomain code: -902 userInfo: @{NSLocalizedDescriptionKey: [@"Error 902: " stringByAppendingString: errDescription]}];
898+
NSBeep();
900899
[NSApp presentError: displayErr];
901900
return;
902901
}
903-
if (![saveData writeToURL: sp.URL atomically: YES]) {
904-
NSBeep();
905-
} else {
906-
NSDictionary* attribs = @{NSFileExtensionHidden: @YES};
907-
[[NSFileManager defaultManager] setAttributes: attribs ofItemAtPath: [sp.URL path] error: NULL];
908-
}
909902
}
910903
}
911904

@@ -917,10 +910,10 @@ - (IBAction)open:(id)sender {
917910
long result = [oPanel runModal];
918911
if (result == NSOKButton) {
919912
if([oPanel.URLs count] > 0) {
920-
NSDictionary* openedDict = [NSDictionary dictionaryWithContentsOfURL: oPanel.URLs[0]];
921-
[settings_ setValue: openedDict[@"HostBlacklist"] forKey: @"Blocklist"];
922-
[settings_ setValue: openedDict[@"BlockAsWhitelist"] forKey: @"BlockAsWhitelist"];
923-
BOOL domainListIsOpen = [[domainListWindowController_ window] isVisible];
913+
[SCUtilities readBlocklistFromFile: oPanel.URLs[0] toSettings: settings_];
914+
915+
// close the domain list (and reopen again if need be to refresh)
916+
BOOL domainListIsOpen = [[domainListWindowController_ window] isVisible];
924917
NSRect frame = [[domainListWindowController_ window] frame];
925918
[self closeDomainList];
926919
if(domainListIsOpen) {

ERRORS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
-217 - Could not write lock file.
2424
-218 - Could not remove lock file.
2525
-219 - Trying to add block but lock file already found. Please try again.
26+
-220 - Block end date argument is invalid
27+
-221 - Block could not be read from file
28+
-221 - Block is already running (so we can't start another)
2629

2730
-901 - Selected sound not found.
2831
-902 - Error serializing blocklist for save

HelperCommon.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
#import <sysexits.h>
3030
#import "HostFileBlocker.h"
3131
#import "SelfControlCommon.h"
32+
#import "SCUtilities.h"
33+
#import "SCSettings.h"
34+
35+
// Uses seteuid to check both settings and defaults for whether the block is running, or not
36+
BOOL blockIsRunningInSettingsOrDefaults(uid_t controllingUID);
3237

3338
// Reads the domain block list from the settings for SelfControl, and adds deny
3439
// rules for all of the IPs (or the A DNS record IPS for doamin names) to the

HelperCommon.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@
1313
#import "SCSettings.h"
1414
#import "SCConstants.h"
1515

16+
BOOL blockIsRunningInSettingsOrDefaults(uid_t controllingUID) {
17+
SCSettings* settings = [SCSettings settingsForUser: controllingUID];
18+
19+
// pull up the user's defaults to check for the existence of a legacy block
20+
// to do that, we have to seteuid to the controlling UID so NSUserDefaults thinks we're them
21+
seteuid(controllingUID);
22+
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
23+
[defaults addSuiteNamed: @"org.eyebeam.SelfControl"];
24+
[defaults synchronize];
25+
26+
BOOL response = [SCUtilities blockIsRunningWithSettings: settings defaults: defaults];
27+
28+
// reset the euid so nothing else gets funky
29+
[NSUserDefaults resetStandardUserDefaults];
30+
seteuid(0);
31+
32+
return response;
33+
}
34+
1635
void addRulesToFirewall(uid_t controllingUID) {
1736
SCSettings* settings = [SCSettings settingsForUser: controllingUID];
1837
BOOL shouldEvaluateCommonSubdomains = [[settings valueForKey: @"EvaluateCommonSubdomains"] boolValue];

HelperMain.m

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,52 @@ int main(int argc, char* argv[]) {
5858
SCSettings* settings = [SCSettings settingsForUser: controllingUID];
5959

6060
if([modeString isEqual: @"--install"]) {
61+
if (blockIsRunningInSettingsOrDefaults(controllingUID)) {
62+
NSLog(@"ERROR: Block is already running");
63+
printStatus(-222);
64+
exit(EX_CONFIG);
65+
}
66+
6167
NSFileManager* fileManager = [NSFileManager defaultManager];
6268

6369
// Initialize writeErr to nil so calling messages on it later don't cause
6470
// crashes (it doesn't make sense we need to do this, but whatever).
6571
NSError* writeErr = nil;
6672
NSString* plistFormatPath = [[NSBundle mainBundle] pathForResource:@"org.eyebeam.SelfControl"
6773
ofType:@"plist"];
68-
6974
NSString* plistFormatString = [NSString stringWithContentsOfFile: plistFormatPath encoding: NSUTF8StringEncoding error: NULL];
75+
76+
// while the main app will just expect us to fetch the blocklist / end date from settings
77+
// this helper tool can also be used via command line with no other settings
78+
// (ex: by auto-selfcontrol) and in that case they'll pass a blocklist file and the end date via args
79+
// we should read those values into settings for use later
80+
NSString* pathToBlocklistFile;
81+
NSDate* blockEndDateArg;
82+
if (argv[3] != NULL && argv[4] != NULL) {
83+
pathToBlocklistFile = @(argv[3]);
84+
blockEndDateArg = [[NSISO8601DateFormatter new] dateFromString: @(argv[4])];
85+
86+
// if we didn't get a valid block end date in the future, ignore the other args
87+
if (blockEndDateArg == nil || [blockEndDateArg timeIntervalSinceNow] < 1) {
88+
pathToBlocklistFile = nil;
89+
NSLog(@"Error: Block end date argument %@ is invalid", @(argv[4]));
90+
printStatus(-220);
91+
exit(EX_IOERR);
92+
} else {
93+
[settings setValue: blockEndDateArg forKey: @"BlockEndDate"];
94+
BOOL readSuccess = [SCUtilities readBlocklistFromFile: [NSURL fileURLWithPath: pathToBlocklistFile] toSettings: settings];
95+
96+
if (!readSuccess) {
97+
NSLog(@"ERROR: Block could not be read from file %@", pathToBlocklistFile);
98+
printStatus(-221);
99+
exit(EX_IOERR);
100+
}
101+
}
102+
}
70103

71104
// get the expiration minute, to make sure we run the helper then (if it hasn't run already)
72-
NSDate* blockEndDate = [settings valueForKey: @"BlockEndDate"];
105+
// use the block end date from the argument if it's available; otherwise fall back to the one in settings
106+
NSDate* blockEndDate = (blockEndDateArg != nil) ? blockEndDateArg : [settings valueForKey: @"BlockEndDate"];
73107
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
74108
NSDateComponents* components = [calendar components: NSMinuteCalendarUnit fromDate: blockEndDate];
75109
long expirationMinute = [components minute];

SCUtilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@
4141
+ (BOOL) blockIsRunningInLegacyDictionary:(NSDictionary*)dict;
4242
+ (NSDate*) endDateFromLegacyBlockDictionary:(NSDictionary *)dict;
4343

44+
// read and write saved block files
45+
+ (BOOL)writeBlocklistToFileURL:(NSURL*)targetFileURL settings:(SCSettings*)settings errorDescription:(NSString**)errDescriptionRef;
46+
+ (BOOL)readBlocklistFromFile:(NSURL*)fileURL toSettings:(SCSettings*)settings;
47+
4448
@end

SCUtilities.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,41 @@ + (NSDate*) endDateFromLegacyBlockDictionary:(NSDictionary *)dict {
200200
return [startDate dateByAddingTimeInterval: (duration * 60)];
201201
}
202202

203+
+ (BOOL)writeBlocklistToFileURL:(NSURL*)targetFileURL settings:(SCSettings*)settings errorDescription:(NSString**)errDescriptionRef {
204+
NSDictionary* saveDict = @{@"HostBlacklist": [settings valueForKey: @"Blocklist"],
205+
@"BlockAsWhitelist": [settings valueForKey: @"BlockAsWhitelist"]};
206+
207+
NSString* saveDataErr;
208+
NSData* saveData = [NSPropertyListSerialization dataFromPropertyList: saveDict format: NSPropertyListBinaryFormat_v1_0 errorDescription: &saveDataErr];
209+
if (saveDataErr != nil) {
210+
*errDescriptionRef = saveDataErr;
211+
return NO;
212+
}
213+
214+
if (![saveData writeToURL: targetFileURL atomically: YES]) {
215+
NSLog(@"ERROR: Failed to write blocklist to URL %@", targetFileURL);
216+
return NO;
217+
}
218+
219+
// for prettiness sake, attempt to hide the file extension
220+
NSDictionary* attribs = @{NSFileExtensionHidden: @YES};
221+
[[NSFileManager defaultManager] setAttributes: attribs ofItemAtPath: [targetFileURL path] error: NULL];
222+
223+
return YES;
224+
}
225+
226+
+ (BOOL)readBlocklistFromFile:(NSURL*)fileURL toSettings:(SCSettings*)settings {
227+
NSDictionary* openedDict = [NSDictionary dictionaryWithContentsOfURL: fileURL];
228+
229+
if (openedDict == nil || openedDict[@"HostBlacklist"] == nil || openedDict[@"BlockAsWhitelist"] == nil) {
230+
NSLog(@"ERROR: Could not read a valid block from file %@", fileURL);
231+
return NO;
232+
}
233+
234+
[settings setValue: openedDict[@"HostBlacklist"] forKey: @"Blocklist"];
235+
[settings setValue: openedDict[@"BlockAsWhitelist"] forKey: @"BlockAsWhitelist"];
236+
237+
return YES;
238+
}
239+
203240
@end

0 commit comments

Comments
 (0)