diff --git a/Tools/SOGoTool.h b/Tools/SOGoTool.h index 30613c834..bbc4de098 100644 --- a/Tools/SOGoTool.h +++ b/Tools/SOGoTool.h @@ -31,6 +31,7 @@ { BOOL verbose; NSArray *arguments; + NSArray *sanitizedArguments; /* arguments w/o args from NSArgumentDomain */ } + (NSString *) command; @@ -40,6 +41,7 @@ verbose: (BOOL) isVerbose; - (void) setArguments: (NSArray *) newArguments; +- (void) setSanitizedArguments: (NSArray *) newArguments; - (void) setVerbose: (BOOL) newVerbose; - (BOOL) run; diff --git a/Tools/SOGoTool.m b/Tools/SOGoTool.m index 602fcf04d..1091f6bad 100644 --- a/Tools/SOGoTool.m +++ b/Tools/SOGoTool.m @@ -21,7 +21,10 @@ */ #import +#import +#import #import +#import #import "SOGoTool.h" @@ -49,6 +52,7 @@ [instance autorelease]; [instance setArguments: toolArguments]; + [instance setSanitizedArguments: toolArguments]; [instance setVerbose: isVerbose]; return [instance run]; @@ -59,6 +63,7 @@ if ((self = [super init])) { arguments = nil; + sanitizedArguments = nil; verbose = NO; } @@ -70,6 +75,51 @@ ASSIGN (arguments, newArguments); } +- (void) setSanitizedArguments: (NSArray *) newArguments +{ + NSString *argsString = [newArguments componentsJoinedByString:@" "]; + NSDictionary *cliArguments; + NSArray *keys; + + int i; + + argsString = [newArguments componentsJoinedByString:@" "]; + + /* Remove NSArgumentDomain -key value from the arguments */ + cliArguments = [[NSUserDefaults standardUserDefaults] + volatileDomainForName:NSArgumentDomain]; + keys = [cliArguments allKeys]; + for (i=0; i < [keys count]; i++) + { + NSString *k = [keys objectAtIndex: i]; + NSString *v = [cliArguments objectForKey:k]; + NSString *argPair = [NSString stringWithFormat:@"-%@ %@", k, v]; + argsString = [argsString stringByReplacingOccurrencesOfString: argPair + withString: @""]; + } + if ([argsString length]) + { + /* dance to compact whitespace */ + NSArray *wordsWP = [argsString componentsSeparatedByCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]; + NSMutableArray *words = [NSMutableArray array]; + for (NSString *word in wordsWP) + { + if([word length] > 1) + { + [words addObject:word]; + } + } + argsString = [words componentsJoinedByString:@" "]; + ASSIGN (sanitizedArguments, [argsString componentsSeparatedByString:@" "]); + } + else + { + DESTROY(sanitizedArguments); + } + +} + - (void) setVerbose: (BOOL) newVerbose { verbose = newVerbose; @@ -78,6 +128,7 @@ - (void) dealloc { [arguments release]; + [sanitizedArguments release]; [super dealloc]; } diff --git a/Tools/SOGoToolExpireAutoReply.m b/Tools/SOGoToolExpireAutoReply.m index b44b1863b..08ae18487 100644 --- a/Tools/SOGoToolExpireAutoReply.m +++ b/Tools/SOGoToolExpireAutoReply.m @@ -22,9 +22,11 @@ #import #import +#import #import #import #import +#import #import #import @@ -63,10 +65,12 @@ - (void) usage { - fprintf (stderr, "expire-autoreply authname:authpassword\n\n" - " authname administrator username of the Sieve server\n" - " authpassword administrator password of the Sieve server\n\n" - "The expire-autoreply action should be configured as a daily cronjob.\n"); + fprintf (stderr, "expire-autoreply -p credentialFile\n\n" + " -p credentialFile Specify the file containing the sieve admin credentials\n" + " The file should contain a single line:\n" + " username:password\n" + "\n" + "The expire-autoreply action should be configured as a daily cronjob.\n"); } - (BOOL) removeAutoReplyForLogin: (NSString *) theLogin @@ -123,12 +127,20 @@ now = [[NSCalendarDate calendarDate] timeIntervalSince1970]; sd = [SOGoSystemDefaults sharedSystemDefaults]; profileURL = [sd profileURL]; - if (profileURL) + if (!profileURL) + { + NSLog(@"Couldn't obtain the profileURL. (Hint: SOGoProfileURL)"); + } + else { tableURL = [[NSURL alloc] initWithString: profileURL]; cm = [GCSChannelManager defaultChannelManager]; channel = [cm acquireOpenChannelForURL: tableURL]; - if (channel) + if (!channel) + { + NSLog(@"Couldn't acquire channel for profileURL"); + } + else { sql = [NSString stringWithFormat: @"SELECT c_uid, c_defaults FROM %@", [tableURL gcsTableName]]; @@ -166,30 +178,60 @@ } } - - (BOOL) run { + NSError *err; NSRange r; - NSString *creds, *authname, *authpwd; + NSString *creds, *credsFile, *authname, *authpwd; BOOL rc; int max; - max = [arguments count]; + max = [sanitizedArguments count]; + creds = nil; + authname = nil; + authpwd = nil; rc = NO; + credsFile = [[NSUserDefaults standardUserDefaults] stringForKey: @"p"]; + if (credsFile) + { + creds = [NSString stringWithContentsOfFile: credsFile + encoding: NSUTF8StringEncoding + error: &err]; + if (!creds) + { + NSLog(@"Error reading credential file '%@': %@", credsFile, err); + } + creds = [creds stringByTrimmingCharactersInSet: + [NSCharacterSet newlineCharacterSet]]; + } + if (max > 0) { - creds = [arguments objectAtIndex: 0]; - r = [creds rangeOfString: @":"]; - if (r.location != NSNotFound) - { - authname = [creds substringToIndex: r.location]; - authpwd = [creds substringFromIndex: r.location+1]; - [self expireAutoReplyWithUsername: authname andPassword: authpwd]; - rc = YES; - } + /* assume we got the creds directly on the cli */ + creds = [sanitizedArguments objectAtIndex: 0]; } + if (creds) + { + r = [creds rangeOfString: @":"]; + if (r.location == NSNotFound) + { + NSLog(@"Invalid credential string format (user:pass)"); + } + else + { + authname = [creds substringToIndex: r.location]; + authpwd = [creds substringFromIndex: r.location+1]; + } + } + + if (authname && authpwd) + { + [self expireAutoReplyWithUsername: authname andPassword: authpwd]; + rc = YES; + } + if (!rc) [self usage]; diff --git a/Tools/SOGoToolUserPreferences.m b/Tools/SOGoToolUserPreferences.m index 32e9490d5..7f5c6d99b 100644 --- a/Tools/SOGoToolUserPreferences.m +++ b/Tools/SOGoToolUserPreferences.m @@ -21,9 +21,11 @@ */ #import +#import #import #import #import +#import #import #import @@ -62,13 +64,16 @@ typedef enum - (void) usage { - fprintf (stderr, "user-preferences get|set|unset defaults|settings user [authname:authpassword] key [value|-f filename]\n\n" - " user the user of whom to set the defaults/settings key/value\n" - " value the JSON-formatted value of the key\n\n" - " Examples:\n" - " sogo-tool user-preferences get defaults janedoe SOGoLanguage\n" - " sogo-tool user-preferences unset settings janedoe Mail\n" - " sogo-tool user-preferences set defaults janedoe SOGoTimeFormat '{\"SOGoTimeFormat\":\"%%I:%%M %%p\"}'\n"); + fprintf (stderr, "user-preferences get|set|unset defaults|settings user [-p credentialFile] key [value|-f filename]\n\n" + " user the user of whom to set the defaults/settings key/value\n" + " value the JSON-formatted value of the key\n\n" + " -p credentialFile Specify the file containing the sieve admin credentials\n" + " The file should contain a single line:\n" + " username:password\n" + " Examples:\n" + " sogo-tool user-preferences get defaults janedoe SOGoLanguage\n" + " sogo-tool user-preferences unset settings janedoe Mail\n" + " sogo-tool user-preferences set defaults janedoe SOGoTimeFormat '{\"SOGoTimeFormat\":\"%%I:%%M %%p\"}'\n"); } // @@ -79,11 +84,12 @@ typedef enum if ([theString length] > 2) { if ([theString caseInsensitiveCompare: @"get"] == NSOrderedSame) - return UserPreferencesGet; + return UserPreferencesGet; else if ([theString caseInsensitiveCompare: @"set"] == NSOrderedSame) - return UserPreferencesSet; + return UserPreferencesSet; else if ([theString caseInsensitiveCompare: @"unset"] == NSOrderedSame) - return UserPreferencesUnset; } + return UserPreferencesUnset; + } return UserPreferencesUnknown; } @@ -96,42 +102,64 @@ typedef enum // - (BOOL) _updateSieveScripsForkey: (NSString *) theKey manager: (SOGoSieveManager *) theManager - login: (NSString *) theLogin - authname: (NSString *) theAuthName - password: (NSString *) thePassword + login: (NSString *) theLogin { if ([theKey caseInsensitiveCompare: @"Forward"] == NSOrderedSame || [theKey caseInsensitiveCompare: @"SOGoSieveFilters"] == NSOrderedSame || [theKey caseInsensitiveCompare: @"Vacation"] == NSOrderedSame) { - if ([theAuthName length] == 0 || [thePassword length] == 0) - { - NSLog(@"To update Sieve scripts, you must provide the \"authname:password\" parameter"); - return NO; - } + /* credentials file handling */ + NSRange r; + NSString *credsFile, *creds, *authname, *authpwd; + authname = nil; + authpwd = nil; + + credsFile = [[NSUserDefaults standardUserDefaults] stringForKey: @"p"]; + if (credsFile) + { + /* TODO: add back support for user:pwd here? */ + creds = [NSString stringWithContentsOfFile: credsFile + encoding: NSUTF8StringEncoding + error: NULL]; + if (creds == nil) + { + NSLog(@"Error reading credential file '%@'", credsFile); + return NO; + } + creds = [creds stringByTrimmingCharactersInSet: + [NSCharacterSet newlineCharacterSet]]; + r = [creds rangeOfString: @":"]; + authname = [creds substringToIndex: r.location]; + authpwd = [creds substringFromIndex: r.location+1]; + } + if (authname == nil || authpwd == nil) + { + NSLog(@"To update Sieve scripts, you must provide the \"-p credentialFile\" parameter"); + return NO; + } return [theManager updateFiltersForLogin: theLogin - authname: theAuthName - password: thePassword - account: nil]; + authname: authname + password: authpwd + account: nil]; } return YES; } - + - (BOOL) run { - NSString *userId, *type, *key; + NSString *userId, *type, *key, *value; + NSString *jsonValueFile; SOGoUserPreferencesCommand cmd; id o; - NSRange r; BOOL rc; int max; - max = [arguments count]; + max = [sanitizedArguments count]; rc = NO; if (max > 3) @@ -140,128 +168,126 @@ typedef enum SOGoSieveManager *manager; SOGoUser *user; - cmd = [self _cmdFromString: [arguments objectAtIndex: 0]]; + cmd = [self _cmdFromString: [sanitizedArguments objectAtIndex: 0]]; - if (cmd != UserPreferencesUnknown) - { - type = [arguments objectAtIndex: 1]; - userId = [arguments objectAtIndex: 2]; - key = [arguments objectAtIndex: 3]; + type = [sanitizedArguments objectAtIndex: 1]; + userId = [sanitizedArguments objectAtIndex: 2]; + key = [sanitizedArguments objectAtIndex: 3]; - user = [SOGoUser userWithLogin: userId]; - manager = [SOGoSieveManager sieveManagerForUser: user]; + user = [SOGoUser userWithLogin: userId]; + manager = [SOGoSieveManager sieveManagerForUser: user]; - if ([type caseInsensitiveCompare: @"defaults"] == NSOrderedSame) - source = [user userDefaults]; - else - source = [user userSettings]; + if ([type caseInsensitiveCompare: @"defaults"] == NSOrderedSame) + source = [user userDefaults]; + else + source = [user userSettings]; - if (cmd == UserPreferencesGet) - { - o = [source objectForKey: key]; + switch (cmd) + { + case UserPreferencesGet: + o = [source objectForKey: key]; - if (o) - printf("%s: %s\n", [key UTF8String], [[o jsonRepresentation] UTF8String]); - else - NSLog(@"Value for key \"%@\" not found in %@", key, type); + if (o) + { + printf("%s: %s\n", [key UTF8String], [[o jsonRepresentation] UTF8String]); + rc = YES; + } + else + { + NSLog(@"Value for key \"%@\" not found in %@", key, type); + return rc; + } + break; - rc = YES; - } - else - { - NSString *authname, *authpwd, *value; - NSData *data; - int i; - - authname = @""; - authpwd = @""; - value = @""; - - if (max > 4) - { - r = [[arguments objectAtIndex: 3] rangeOfString: @":"]; - if (r.location == NSNotFound) - { - i = 3; - } - else - { - authname = [[arguments objectAtIndex: 3] substringToIndex: r.location]; - authpwd = [[arguments objectAtIndex: 3] substringFromIndex: r.location+1]; - i = 4; - } + case UserPreferencesSet: + if (max > 4) + { + /* value specified on command line */ + value = [sanitizedArguments objectAtIndex: 4]; + } + else + { + /* value is to be found in file specified with -f filename */ + jsonValueFile = [[NSUserDefaults standardUserDefaults] + stringForKey: @"f"]; - key = [arguments objectAtIndex: i++]; + if (jsonValueFile == nil) + { + NSLog(@"No value specified, aborting"); + [self usage]; + return rc; + } + else + { - if (max > i) - { - value = [arguments objectAtIndex: i++]; - if ([value caseInsensitiveCompare: @"-f"] == NSOrderedSame) - { - if (max > i) - { - data = [NSData dataWithContentsOfFile: [arguments objectAtIndex: i]]; - value = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; - [value autorelease]; - } - } - } - } - else - { - if (cmd == UserPreferencesUnset) - { - key = [arguments objectAtIndex: 3]; - } - else - { - key = [arguments objectAtIndex: 3]; - value = [arguments objectAtIndex: 4]; - } - } - - if (cmd == UserPreferencesUnset) - [source removeObjectForKey: key]; - else - { - o = [value objectFromJSONString]; - - // - // We support setting only "values" - for example, setting : - // - // SOGoDayStartTime to 9:00 - // - // Values in JSON must be a dictionary so we must support passing: - // - // key == SOGoDayStartTime - // value == '{"SOGoDayStartTime": "09:00"}' - // - // to achieve what we want. - // - if (o && [o count] == 1) - { - [source setObject: [[o allValues] lastObject] forKey: key]; - } - // - // We also support passing values that are already dictionaries so in this - // case, we simply set it to the passed key. - // - else if (o) - { - [source setObject: o forKey: key]; - } - else - NSLog(@"Invalid JSON input - no changes performed in the database. The supplied value was: %@", value); - } - - [source synchronize]; - rc = [self _updateSieveScripsForkey: key - manager: manager - login: userId - authname: authname - password: authpwd]; - } - } + NSData *data = [NSData dataWithContentsOfFile: jsonValueFile]; + if (data == nil) + { + NSLog(@"Error reading file '%@'", jsonValueFile); + [self usage]; + return rc; + } + value = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; + [value autorelease]; + } + } + o = [value objectFromJSONString]; + + // + // We support setting only "values" - for example, setting : + // + // SOGoDayStartTime to 9:00 + // + // Values in JSON must be a dictionary so we must support passing: + // + // key == SOGoDayStartTime + // value == '{"SOGoDayStartTime": "09:00"}' + // + // to achieve what we want. + // + if (o && [o count] == 1) + { + [source setObject: [[o allValues] lastObject] forKey: key]; + } + // + // We also support passing values that are already dictionaries so in this + // case, we simply set it to the passed key. + // + else if (o) + { + [source setObject: o forKey: key]; + } + else + { + NSLog(@"Invalid JSON input - no changes performed in the database. The supplied value was: %@", value); + [self usage]; + return rc; + } + + rc = [self _updateSieveScripsForkey: key + manager: manager + login: userId]; + if (rc) + [source synchronize]; + else + NSLog(@"Error updating sieve script, not updating database"); + + break; + + case UserPreferencesUnset: + [source removeObjectForKey: key]; + rc = [self _updateSieveScripsForkey: key + manager: manager + login: userId]; + if (rc) + [source synchronize]; + else + NSLog(@"Error updating sieve script, not updating database"); + + break; + case UserPreferencesUnknown: + break; + } } if (!rc)