From 7e720da571276ef37c1db938549e8c9a95a4cb15 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 29 Jun 2011 17:43:10 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: 178cf4e5311729ac333305676b5ad2aa35a7ca71 Monotone-Revision: d51bb1b592f986d2e34036c3dad0c70f43f0150a Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2011-06-29T17:43:10 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 6 + SoObjects/Mailer/GNUmakefile | 2 - SoObjects/Mailer/SOGoMailAccount.m | 232 +--------------- SoObjects/SOGo/GNUmakefile | 4 + .../SOGoSieveManager.h} | 22 +- .../SOGoSieveManager.m} | 255 +++++++++++++++++- SoObjects/SOGo/SOGoUserSettings.m | 2 +- Tools/GNUmakefile | 1 + Tools/SOGoTool.h | 2 +- Tools/SOGoTool.m | 2 +- Tools/SOGoToolUserPreferences.m | 240 +++++++++++++++++ 11 files changed, 521 insertions(+), 247 deletions(-) rename SoObjects/{Mailer/SOGoSieveConverter.h => SOGo/SOGoSieveManager.h} (68%) rename SoObjects/{Mailer/SOGoSieveConverter.m => SOGo/SOGoSieveManager.m} (72%) create mode 100644 Tools/SOGoToolUserPreferences.m diff --git a/ChangeLog b/ChangeLog index ae9e16e3e..fbb20bf1e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-06-29 Ludovic Marcotte + + * Added get/set/unset capabilities to sogo-tool to set + user's defaults/settings. Refactored some code to be + more cleanly structured. + 2011-06-24 Francis Lachapelle * SoObjects/SOGo/SOGoSystemDefaults.m (-loginDomains): new method diff --git a/SoObjects/Mailer/GNUmakefile b/SoObjects/Mailer/GNUmakefile index 4ca84ceaa..a58b1406c 100644 --- a/SoObjects/Mailer/GNUmakefile +++ b/SoObjects/Mailer/GNUmakefile @@ -33,8 +33,6 @@ Mailer_OBJC_FILES += \ SOGoMailForward.m \ SOGoMailReply.m \ \ - SOGoSieveConverter.m \ - \ EOQualifier+MailDAV.m \ NSData+Mail.m \ NSString+Mail.m \ diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index df63b8007..1e795f74f 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -30,14 +30,12 @@ #import #import #import -#import #import #import #import #import #import #import -#import #import #import @@ -45,13 +43,13 @@ #import #import #import +#import #import "SOGoDraftsFolder.h" #import "SOGoMailFolder.h" #import "SOGoMailManager.h" #import "SOGoMailNamespace.h" #import "SOGoSentFolder.h" -#import "SOGoSieveConverter.h" #import "SOGoTrashFolder.h" #import "SOGoUser+Mailer.h" @@ -60,7 +58,6 @@ @implementation SOGoMailAccount static NSString *inboxFolderName = @"INBOX"; -static NSString *sieveScriptName = @"sogo"; - (id) init { @@ -283,228 +280,15 @@ static NSString *sieveScriptName = @"sogo"; - (BOOL) updateFilters { - NSMutableArray *requirements; - NSMutableString *script, *header; - NGInternetSocketAddress *address; - NSDictionary *result, *values; - SOGoUserDefaults *ud; - SOGoDomainDefaults *dd; - NGSieveClient *client; - NSString *filterScript, *v, *password, *sieveServer; - SOGoSieveConverter *converter; - int sievePort; - BOOL b; + SOGoSieveManager *manager; - dd = [[context activeUser] domainDefaults]; - if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled])) - return YES; + manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]]; - requirements = [NSMutableArray arrayWithCapacity: 15]; - ud = [[context activeUser] userDefaults]; - b = NO; - - script = [NSMutableString string]; - - // Right now, we handle Sieve filters here and only for vacation - // and forwards. Traditional filters support (for fileinto, for - // example) will be added later. - values = [ud vacationOptions]; - - // We handle vacation messages. - // See http://ietfreport.isoc.org/idref/draft-ietf-sieve-vacation/ - if (values && [[values objectForKey: @"enabled"] boolValue]) - { - NSArray *addresses; - NSString *text; - BOOL ignore; - int days, i; - - days = [[values objectForKey: @"daysBetweenResponse"] intValue]; - addresses = [values objectForKey: @"autoReplyEmailAddresses"]; - ignore = [[values objectForKey: @"ignoreLists"] boolValue]; - text = [values objectForKey: @"autoReplyText"]; - b = YES; - - if (days == 0) - days = 7; - - [requirements addObjectUniquely: @"vacation"]; - - // Skip mailing lists - if (ignore) - [script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"]; - - [script appendFormat: @"vacation :days %d :addresses [", days]; - - for (i = 0; i < [addresses count]; i++) - { - [script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]]; - - if (i == [addresses count]-1) - [script appendString: @"] "]; - else - [script appendString: @", "]; - } - - [script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text]; - - if (ignore) - [script appendString: @"}\r\n"]; - } - - - // We handle mail forward - values = [ud forwardOptions]; - - if (values && [[values objectForKey: @"enabled"] boolValue]) - { - id addresses; - int i; - - b = YES; - - addresses = [values objectForKey: @"forwardAddress"]; - if ([addresses isKindOfClass: [NSString class]]) - addresses = [NSArray arrayWithObject: addresses]; - - for (i = 0; i < [addresses count]; i++) - { - v = [addresses objectAtIndex: i]; - if (v && [v length] > 0) - [script appendFormat: @"redirect \"%@\";\r\n", v]; - } - - if ([[values objectForKey: @"keepCopy"] boolValue]) - [script appendString: @"keep;\r\n"]; - } - - converter = [SOGoSieveConverter sieveConverterForUser: [context activeUser]]; - filterScript = [converter sieveScriptWithRequirements: requirements]; - if (filterScript) - { - if ([filterScript length]) - { - b = YES; - [script appendString: filterScript]; - } - } - else - { - [self errorWithFormat: @"Sieve generation failure: %@", - [converter lastScriptError]]; - return NO; - } - - if ([requirements count]) - { - header = [NSString stringWithFormat: @"require [\"%@\"];\r\n", - [requirements componentsJoinedByString: @"\",\""]]; - [script insertString: header atIndex: 0]; - } - - // We connect to our Sieve server and upload the script. - // - // sieveServer might have the following format: - // - // sieve://localhost - // sieve://localhost:2000 - // - // Values such as "localhost" or "localhost:2000" are NOT supported. - // - sieveServer = [dd sieveServer]; - sievePort = 2000; - - if (!sieveServer) - { - NSString *s; - - sieveServer = @"localhost"; - s = [dd imapServer]; - - if (s) - { - NSURL *url; - - url = [NSURL URLWithString: s]; - - if ([url host]) - sieveServer = [url host]; - else - sieveServer = s; - } - } - else - { - NSURL *url; - - url = [NSURL URLWithString: sieveServer]; - - if ([url host]) - sieveServer = [url host]; - - if ([[url port] intValue] != 0) - sievePort = [[url port] intValue]; - } - - address = [NGInternetSocketAddress addressWithPort: sievePort onHost: sieveServer]; - - client = [NGSieveClient clientWithAddress: address]; - - if (!client) { - [self errorWithFormat: @"Sieve connection failed on %@", [address description]]; - return NO; - } - - password = [self imap4PasswordRenewed: NO]; - if (!password) { - [client closeConnection]; - return NO; - } - result = [client login: [[self imap4URL] user] password: password]; - if (![[result valueForKey:@"result"] boolValue]) { - [self errorWithFormat: @"failure. Attempting with a renewed password."]; - password = [self imap4PasswordRenewed: YES]; - result = [client login: [[self imap4URL] user] password: password]; - } - - if (![[result valueForKey:@"result"] boolValue]) { - [self errorWithFormat: @"Could not login '%@' on Sieve server: %@: %@", - [[self imap4URL] user], client, result]; - [client closeConnection]; - return NO; - } - - /* We ensure to deactive the current active script since it could prevent - its deletion from the server. */ - result = [client setActiveScript: @""]; - // We delete the existing Sieve script - result = [client deleteScript: sieveScriptName]; - - if (![[result valueForKey:@"result"] boolValue]) { - [self logWithFormat:@"WARNING: Could not delete Sieve script - continuing...: %@", result]; - } - - // We put and activate the script only if we actually have a script - // that does something... - if (b) - { - result = [client putScript: sieveScriptName script: script]; - - if (![[result valueForKey:@"result"] boolValue]) { - [self errorWithFormat:@"Could not upload Sieve script: %@", result]; - [client closeConnection]; - return NO; - } - - result = [client setActiveScript: sieveScriptName]; - if (![[result valueForKey:@"result"] boolValue]) { - [self errorWithFormat:@"Could not enable Sieve script: %@", result]; - [client closeConnection]; - return NO; - } - } - - return YES; + return [manager updateFiltersForLogin: [[self imap4URL] user] + authname: [[self imap4URL] user] + password: [self imap4PasswordRenewed: NO] + account: self]; + } diff --git a/SoObjects/SOGo/GNUmakefile b/SoObjects/SOGo/GNUmakefile index f3464af79..35f42ba32 100644 --- a/SoObjects/SOGo/GNUmakefile +++ b/SoObjects/SOGo/GNUmakefile @@ -23,6 +23,8 @@ SOGo_HEADER_FILES = \ SOGoParentFolder.h \ SOGoUserFolder.h \ \ + SOGoSieveManager.h \ + \ SOGoUserManager.h \ LDAPSource.h \ SQLSource.h \ @@ -81,6 +83,8 @@ SOGo_OBJC_FILES = \ SOGoPublicBaseFolder.m \ SOGoUserFolder.m \ \ + SOGoSieveManager.m \ + \ SOGoDefaultsSource.m \ SOGoSystemDefaults.m \ SOGoDomainDefaults.m \ diff --git a/SoObjects/Mailer/SOGoSieveConverter.h b/SoObjects/SOGo/SOGoSieveManager.h similarity index 68% rename from SoObjects/Mailer/SOGoSieveConverter.h rename to SoObjects/SOGo/SOGoSieveManager.h index 3a8b2359b..2f180426a 100644 --- a/SoObjects/Mailer/SOGoSieveConverter.h +++ b/SoObjects/SOGo/SOGoSieveManager.h @@ -1,8 +1,9 @@ -/* SOGoSieveConverter.h - this file is part of SOGo +/* SOGoSieveManager.h - this file is part of SOGo * - * Copyright (C) 2010 Wolfgang Sourdeau + * Copyright (C) 2010-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Ludovic Marcotte * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,30 +21,35 @@ * Boston, MA 02111-1307, USA. */ -#ifndef SOGOSIEVECONVERTER_H -#define SOGOSIEVECONVERTER_H +#ifndef SOGOSIEVEMANAGER_H +#define SOGOSIEVEMANAGER_H #import @class NSDictionary; @class NSMutableArray; @class NSString; - +@class SOGoMailAccount; @class SOGoUser; -@interface SOGoSieveConverter : NSObject +@interface SOGoSieveManager : NSObject { SOGoUser *user; NSMutableArray *requirements; NSString *scriptError; } -+ (id) sieveConverterForUser: (SOGoUser *) user; ++ (id) sieveManagerForUser: (SOGoUser *) user; - (id) initForUser: (SOGoUser *) newUser; - (NSString *) sieveScriptWithRequirements: (NSMutableArray *) newRequirements; - (NSString *) lastScriptError; +- (BOOL) updateFiltersForLogin: (NSString *) theLogin + authname: (NSString *) theAuthName + password: (NSString *) thePassword + account: (SOGoMailAccount *) theAccount; + @end -#endif /* SOGOSIEVECONVERTER_H */ +#endif /* SOGOSIEVEMANAGER_H */ diff --git a/SoObjects/Mailer/SOGoSieveConverter.m b/SoObjects/SOGo/SOGoSieveManager.m similarity index 72% rename from SoObjects/Mailer/SOGoSieveConverter.m rename to SoObjects/SOGo/SOGoSieveManager.m index 7a605b2a6..fc14c103d 100644 --- a/SoObjects/Mailer/SOGoSieveConverter.m +++ b/SoObjects/SOGo/SOGoSieveManager.m @@ -1,8 +1,9 @@ -/* SOGoSieveConverter.m - this file is part of SOGo +/* SOGoSieveManager.m - this file is part of SOGo * - * Copyright (C) 2010 Wolfgang Sourdeau + * Copyright (C) 2010-2011 Inverse inc. * * Author: Wolfgang Sourdeau + * Ludovic Marcotte * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ #import #import #import +#import #import #import @@ -30,9 +32,12 @@ #import #import -#import "SOGoMailAccounts.h" +#import +#import -#import "SOGoSieveConverter.h" +#import "../Mailer/SOGoMailAccount.h" + +#import "SOGoSieveManager.h" typedef enum { UIxFilterFieldTypeAddress, @@ -47,6 +52,8 @@ static NSDictionary *sieveFields = nil; static NSDictionary *sieveFlags = nil; static NSDictionary *operatorRequirements = nil; static NSDictionary *methodRequirements = nil; +static NSString *sieveScriptName = @"sogo"; + @interface NSString (SOGoSieveExtension) @@ -106,7 +113,7 @@ static NSDictionary *methodRequirements = nil; @end -@implementation SOGoSieveConverter +@implementation SOGoSieveManager + (void) initialize { @@ -195,14 +202,14 @@ static NSDictionary *methodRequirements = nil; } } -+ (id) sieveConverterForUser: (SOGoUser *) newUser ++ (id) sieveManagerForUser: (SOGoUser *) newUser { - SOGoSieveConverter *newConverter; + SOGoSieveManager *newManager; - newConverter = [[self alloc] initForUser: newUser]; - [newConverter autorelease]; + newManager = [[self alloc] initForUser: newUser]; + [newManager autorelease]; - return newConverter; + return newManager; } - (id) init @@ -605,4 +612,232 @@ static NSDictionary *methodRequirements = nil; return scriptError; } +// +// +// +- (BOOL) updateFiltersForLogin: (NSString *) theLogin + authname: (NSString *) theAuthName + password: (NSString *) thePassword + account: (SOGoMailAccount *) theAccount +{ + NSMutableArray *req; + NSMutableString *script, *header; + NGInternetSocketAddress *address; + NSDictionary *result, *values; + SOGoUserDefaults *ud; + SOGoDomainDefaults *dd; + NGSieveClient *client; + NSString *filterScript, *v, *sieveServer; + int sievePort; + BOOL b; + + dd = [user domainDefaults]; + if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled])) + return YES; + + req = [NSMutableArray arrayWithCapacity: 15]; + ud = [user userDefaults]; + b = NO; + + script = [NSMutableString string]; + + // Right now, we handle Sieve filters here and only for vacation + // and forwards. Traditional filters support (for fileinto, for + // example) will be added later. + values = [ud vacationOptions]; + + // We handle vacation messages. + // See http://ietfreport.isoc.org/idref/draft-ietf-sieve-vacation/ + if (values && [[values objectForKey: @"enabled"] boolValue]) + { + NSArray *addresses; + NSString *text; + BOOL ignore; + int days, i; + + days = [[values objectForKey: @"daysBetweenResponse"] intValue]; + addresses = [values objectForKey: @"autoReplyEmailAddresses"]; + ignore = [[values objectForKey: @"ignoreLists"] boolValue]; + text = [values objectForKey: @"autoReplyText"]; + b = YES; + + if (days == 0) + days = 7; + + [req addObjectUniquely: @"vacation"]; + + // Skip mailing lists + if (ignore) + [script appendString: @"if allof ( not exists [\"list-help\", \"list-unsubscribe\", \"list-subscribe\", \"list-owner\", \"list-post\", \"list-archive\", \"list-id\", \"Mailing-List\"], not header :comparator \"i;ascii-casemap\" :is \"Precedence\" [\"list\", \"bulk\", \"junk\"], not header :comparator \"i;ascii-casemap\" :matches \"To\" \"Multiple recipients of*\" ) {"]; + + [script appendFormat: @"vacation :days %d :addresses [", days]; + + for (i = 0; i < [addresses count]; i++) + { + [script appendFormat: @"\"%@\"", [addresses objectAtIndex: i]]; + + if (i == [addresses count]-1) + [script appendString: @"] "]; + else + [script appendString: @", "]; + } + + [script appendFormat: @"text:\r\n%@\r\n.\r\n;\r\n", text]; + + if (ignore) + [script appendString: @"}\r\n"]; + } + + + // We handle mail forward + values = [ud forwardOptions]; + + if (values && [[values objectForKey: @"enabled"] boolValue]) + { + id addresses; + int i; + + b = YES; + + addresses = [values objectForKey: @"forwardAddress"]; + if ([addresses isKindOfClass: [NSString class]]) + addresses = [NSArray arrayWithObject: addresses]; + + for (i = 0; i < [addresses count]; i++) + { + v = [addresses objectAtIndex: i]; + if (v && [v length] > 0) + [script appendFormat: @"redirect \"%@\";\r\n", v]; + } + + if ([[values objectForKey: @"keepCopy"] boolValue]) + [script appendString: @"keep;\r\n"]; + } + + filterScript = [self sieveScriptWithRequirements: req]; + if (filterScript) + { + if ([filterScript length]) + { + b = YES; + [script appendString: filterScript]; + } + } + else + { + NSLog(@"Sieve generation failure: %@", [self lastScriptError]); + return NO; + } + + if ([req count]) + { + header = [NSString stringWithFormat: @"require [\"%@\"];\r\n", + [req componentsJoinedByString: @"\",\""]]; + [script insertString: header atIndex: 0]; + } + + // We connect to our Sieve server and upload the script. + // + // sieveServer might have the following format: + // + // sieve://localhost + // sieve://localhost:2000 + // + // Values such as "localhost" or "localhost:2000" are NOT supported. + // + sieveServer = [dd sieveServer]; + sievePort = 2000; + + if (!sieveServer) + { + NSString *s; + + sieveServer = @"localhost"; + s = [dd imapServer]; + + if (s) + { + NSURL *url; + + url = [NSURL URLWithString: s]; + + if ([url host]) + sieveServer = [url host]; + else + sieveServer = s; + } + } + else + { + NSURL *url; + + url = [NSURL URLWithString: sieveServer]; + + if ([url host]) + sieveServer = [url host]; + + if ([[url port] intValue] != 0) + sievePort = [[url port] intValue]; + } + + address = [NGInternetSocketAddress addressWithPort: sievePort onHost: sieveServer]; + + client = [NGSieveClient clientWithAddress: address]; + + if (!client) { + NSLog(@"Sieve connection failed on %@", [address description]); + return NO; + } + + if (!thePassword) { + [client closeConnection]; + return NO; + } + result = [client login: theLogin authname: theAuthName password: thePassword]; + if (![[result valueForKey:@"result"] boolValue]) { + NSLog(@"failure. Attempting with a renewed password (no authname supported)"); + thePassword = [theAccount imap4PasswordRenewed: YES]; + result = [client login: theLogin password: thePassword]; + } + + if (![[result valueForKey:@"result"] boolValue]) { + NSLog(@"Could not login '%@' on Sieve server: %@: %@", + theLogin, client, result); + [client closeConnection]; + return NO; + } + + /* We ensure to deactive the current active script since it could prevent + its deletion from the server. */ + result = [client setActiveScript: @""]; + // We delete the existing Sieve script + result = [client deleteScript: sieveScriptName]; + + if (![[result valueForKey:@"result"] boolValue]) { + NSLog(@"WARNING: Could not delete Sieve script - continuing...: %@", result); + } + + // We put and activate the script only if we actually have a script + // that does something... + if (b) + { + result = [client putScript: sieveScriptName script: script]; + + if (![[result valueForKey:@"result"] boolValue]) { + NSLog(@"Could not upload Sieve script: %@", result); + [client closeConnection]; + return NO; + } + + result = [client setActiveScript: sieveScriptName]; + if (![[result valueForKey:@"result"] boolValue]) { + NSLog(@"Could not enable Sieve script: %@", result); + [client closeConnection]; + return NO; + } + } + + return YES; +} + @end diff --git a/SoObjects/SOGo/SOGoUserSettings.m b/SoObjects/SOGo/SOGoUserSettings.m index 74387cdb1..9739e6f85 100644 --- a/SoObjects/SOGo/SOGoUserSettings.m +++ b/SoObjects/SOGo/SOGoUserSettings.m @@ -1,6 +1,6 @@ /* SOGoUserSettings.m - this file is part of SOGo * - * Copyright (C) 2009-2010 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. * * Author: Wolfgang Sourdeau * diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 7fbad4c9d..ceb7af2ba 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -16,6 +16,7 @@ $(SOGO_TOOL)_OBJC_FILES += \ SOGoToolRemoveDoubles.m \ SOGoToolRemove.m \ SOGoToolRenameUser.m \ + SOGoToolUserPreferences.m SOGO_SLAPD_SOCKD = sogo-slapd-sockd $(SOGO_SLAPD_SOCKD)_INSTALL_DIR = $(SOGO_ADMIN_TOOLS) diff --git a/Tools/SOGoTool.h b/Tools/SOGoTool.h index a714ac0e7..30613c834 100644 --- a/Tools/SOGoTool.h +++ b/Tools/SOGoTool.h @@ -1,6 +1,6 @@ /* SOGoTool.h - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. * * Author: Wolfgang Sourdeau * diff --git a/Tools/SOGoTool.m b/Tools/SOGoTool.m index f55bc2700..602fcf04d 100644 --- a/Tools/SOGoTool.m +++ b/Tools/SOGoTool.m @@ -1,6 +1,6 @@ /* SOGoTool.m - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. + * Copyright (C) 2009-2011 Inverse inc. * * Author: Wolfgang Sourdeau * diff --git a/Tools/SOGoToolUserPreferences.m b/Tools/SOGoToolUserPreferences.m new file mode 100644 index 000000000..6b41ff248 --- /dev/null +++ b/Tools/SOGoToolUserPreferences.m @@ -0,0 +1,240 @@ +/* SOGoToolUserPreferences.m - this file is part of SOGo + * + * Copyright (C) 2011 Inverse inc. + * + * Author: Ludovic Marcotte + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import + +#import +#import +#import +#import +#import + +#import "SOGoTool.h" + +typedef enum +{ + UserPreferencesUnknown = -1, + UserPreferencesGet = 0, + UserPreferencesSet = 1, + UserPreferencesUnset = 2, +} SOGoUserPreferencesCommand; + +@interface SOGoToolUserPreferences : SOGoTool +@end + +@implementation SOGoToolUserPreferences + ++ (void) initialize +{ +} + ++ (NSString *) command +{ + return @"user-preferences"; +} + ++ (NSString *) description +{ + return @"set user defaults / settings in the database"; +} + +- (void) usage +{ + fprintf (stderr, "user-preferences get|set|unset defaults|settings user [authname:authpassword] key value\n\n" + " user the user of whom to set the defaults/settings key/value\n"); +} + +// +// possible values are: get | set | unset +// +- (SOGoUserPreferencesCommand) _cmdFromString: (NSString *) theString +{ + if ([theString length] > 2) + { + if ([theString caseInsensitiveCompare: @"get"] == NSOrderedSame) + return UserPreferencesGet; + else if ([theString caseInsensitiveCompare: @"set"] == NSOrderedSame) + return UserPreferencesSet; + else if ([theString caseInsensitiveCompare: @"unset"] == NSOrderedSame) + return UserPreferencesUnset; } + + return UserPreferencesUnknown; +} + +// If we got any of those keys for "defaults", we regenerate the Sieve script +// +// Forward +// SOGoSieveFilters +// Vacation +// +- (BOOL) _updateSieveScripsForkey: (NSString *) theKey + manager: (SOGoSieveManager *) theManager + login: (NSString *) theLogin + authname: (NSString *) theAuthName + password: (NSString *) thePassword +{ + 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; + } + + return [theManager updateFiltersForLogin: theLogin + authname: theAuthName + password: thePassword + account: nil]; + } + + return YES; +} + + + +- (BOOL) run +{ + NSString *userId, *type, *key; + SOGoUserPreferencesCommand cmd; + id o; + + NSRange r; + BOOL rc; + int max; + + max = [arguments count]; + rc = NO; + + if (max > 3) + { + SOGoDefaultsSource *source; + SOGoSieveManager *manager; + SOGoUser *user; + + cmd = [self _cmdFromString: [arguments objectAtIndex: 0]]; + + if (cmd != UserPreferencesUnknown) + { + type = [arguments objectAtIndex: 1]; + userId = [arguments objectAtIndex: 2]; + key = [arguments objectAtIndex: 3]; + + user = [SOGoUser userWithLogin: userId]; + manager = [SOGoSieveManager sieveManagerForUser: user]; + + if ([type caseInsensitiveCompare: @"defaults"] == NSOrderedSame) + source = [user userDefaults]; + else + source = [user userSettings]; + + if (cmd == UserPreferencesGet) + { + o = [source objectForKey: key]; + + if (o) + NSLog(@"value for key \"%@\": %@", key, [o jsonRepresentation]); + else + NSLog(@"Value for key \"%@\" not found in %@", key, type); + + rc = YES; + } + else + { + NSString *authname, *authpwd, *value; + + authname = @""; + authpwd = @""; + value = @""; + + if (max > 4) + { + r = [[arguments objectAtIndex: 3] rangeOfString: @":"]; + authname = [[arguments objectAtIndex: 3] substringToIndex: r.location]; + authpwd = [[arguments objectAtIndex: 3] substringFromIndex: r.location+1]; + key = [arguments objectAtIndex: 4]; + + if (max > 5) + value = [arguments objectAtIndex: 5]; + } + 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."); + } + + [source synchronize]; + rc = [self _updateSieveScripsForkey: key + manager: manager + login: userId + authname: authname + password: authpwd]; + } + } + } + + if (!rc) + { + [self usage]; + } + + return rc; +} + +@end