From 4475ac651d1d94513729d6133a70d0e70ea52b87 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Wed, 6 Nov 2019 14:53:30 -0500 Subject: [PATCH] feat(core): allow pre/appended Sieve scripts Allow domain admins to now set Sieve scripts that can be prepended and appended to all users' scripts. This can now be controller using the SOGoSieveScriptHeaderTemplateFile and SOGoSieveScriptFooterTemplateFile domain defaults. --- Documentation/SOGoInstallationGuide.asciidoc | 10 +++ SoObjects/SOGo/SOGoDomainDefaults.h | 2 + SoObjects/SOGo/SOGoDomainDefaults.m | 10 +++ SoObjects/SOGo/SOGoSieveManager.m | 79 ++++++++++++++++++-- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index a09ab36b2..6c7914344 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -2138,6 +2138,16 @@ host. Defaults to `NO` when unset. +|D |SOGoSieveScriptHeaderTemplateFile +|Parameter used to set the full path of the Sieve script that will be +automatically prepended to any Sieve scripts a user might define. The file +must be encoded in UTF-8 and it must also respect the RFC5228 syntax. + +|D |SOGoSieveScriptFooterTemplateFile +|Parameter used to set the full path of the Sieve script that will be +automatically appended to any Sieve scripts a user might define. The file +must be encoded in UTF-8 and it must also respect the RFC5228 syntax. + |U |SOGoSieveFilters |Parameter used to define initial Sieve scripts for users. The user can still modify the scripts and the initial values will be written diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index a56c71c5c..fafa07918 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -48,6 +48,8 @@ - (BOOL) forceExternalLoginWithEmail; - (BOOL) externalAvatarsEnabled; - (BOOL) sieveScriptsEnabled; +- (NSString *) sieveScriptHeaderTemplateFile; +- (NSString *) sieveScriptFooterTemplateFile; - (BOOL) forwardEnabled; - (int) forwardConstraints; - (NSArray *) forwardConstraintsDomains; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index df710083e..a8a6c93e0 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -196,6 +196,16 @@ return [self boolForKey: @"SOGoSieveScriptsEnabled"]; } +- (NSString *) sieveScriptHeaderTemplateFile +{ + return [self stringForKey: @"SOGoSieveScriptHeaderTemplateFile"]; +} + +- (NSString *) sieveScriptFooterTemplateFile +{ + return [self stringForKey: @"SOGoSieveScriptFooterTemplateFile"]; +} + - (BOOL) forwardEnabled { return [self boolForKey: @"SOGoForwardEnabled"]; diff --git a/SoObjects/SOGo/SOGoSieveManager.m b/SoObjects/SOGo/SOGoSieveManager.m index ee8d81a0b..8f140944c 100644 --- a/SoObjects/SOGo/SOGoSieveManager.m +++ b/SoObjects/SOGo/SOGoSieveManager.m @@ -21,6 +21,7 @@ */ #import +#import #import #import @@ -32,6 +33,7 @@ #import #import +#import #import #import #import @@ -253,6 +255,46 @@ static NSString *sieveScriptName = @"sogo"; return YES; } +- (NSString *) _extractRequirementsFromContent: (NSString *) theContent + intoArray: (NSMutableArray *) theRequirements +{ + NSString *line, *v; + NSArray *lines; + id o; + + int i, count; + + lines = [theContent componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]]; + count = [lines count]; + + for (i = 0; i < count; i++) + { + line = [[lines objectAtIndex: i] stringByTrimmingSpaces]; + if ([line hasPrefix: @"require "]) + { + line = [line substringFromIndex: 8]; + // Handle lines like: require "imapflags"; + if ([line characterAtIndex: 0] == '"') + { + v = [line substringToIndex: [line length]-2]; + [theRequirements addObject: v]; + NSLog(@"require: |%@|", v); + } + // Else handle lines like: require ["imapflags","vacation"]; + else if ([line characterAtIndex: 0] == '[') + { + o = [[line substringToIndex: [line length]-1] objectFromJSONString]; + [theRequirements addObjectsFromArray: o]; + NSLog(@"requires: |%@|", o); + } + } + else + break; + } + + return [[lines subarrayWithRange: NSMakeRange(i, count-i)] componentsJoinedByString: @"\n"]; +} + - (BOOL) _extractRuleField: (NSString **) field fromRule: (NSDictionary *) rule andType: (UIxFilterFieldType *) type @@ -592,7 +634,7 @@ static NSString *sieveScriptName = @"sogo"; sieveScript = [NSMutableString string]; - ASSIGN (requirements, newRequirements); + ASSIGN(requirements, newRequirements); [scriptError release]; scriptError = nil; @@ -613,8 +655,7 @@ static NSString *sieveScriptName = @"sogo"; } [scriptError retain]; - [requirements release]; - requirements = nil; + DESTROY(requirements); if (scriptError) sieveScript = nil; @@ -783,6 +824,7 @@ static NSString *sieveScriptName = @"sogo"; withUsername: (NSString *) theUsername andPassword: (NSString *) thePassword { + NSString *filterScript, *v, *content; NSMutableArray *req; NSMutableString *script, *header; NSDictionary *result, *values; @@ -790,7 +832,6 @@ static NSString *sieveScriptName = @"sogo"; SOGoDomainDefaults *dd; NGSieveClient *client; NGImap4Client *imapClient; - NSString *filterScript, *v; BOOL b, dateCapability; unsigned int now; @@ -1029,10 +1070,38 @@ static NSString *sieveScriptName = @"sogo"; [script appendString: @"keep;\r\n"]; } + // We handle header/footer Sieve scripts + if ((v = [dd sieveScriptHeaderTemplateFile])) + { + content = [NSString stringWithContentsOfFile: v + encoding: NSUTF8StringEncoding + error: NULL]; + if (content) + { + v = [self _extractRequirementsFromContent: content + intoArray: req]; + [script insertString: v atIndex: 0]; + } + } + + if ((v = [dd sieveScriptFooterTemplateFile])) + { + content = [NSString stringWithContentsOfFile: v + encoding: NSUTF8StringEncoding + error: NULL]; + if (content) + { + v = [self _extractRequirementsFromContent: content + intoArray: req]; + [script appendString: @"\n"]; + [script appendString: v]; + } + } + if ([req count]) { header = [NSString stringWithFormat: @"require [\"%@\"];\r\n", - [req componentsJoinedByString: @"\",\""]]; + [[req uniqueObjects] componentsJoinedByString: @"\",\""]]; [script insertString: header atIndex: 0]; }