refactor(preferences): conditionally activate the Sieve script

All the user defaults are now editable through the Preferences module,
even if an external Sieve script is enabled. However, the user can
disable the external Sieve script and force the activation of the
"sogo" Sieve script.
pull/281/head
Francis Lachapelle 2019-11-15 14:37:35 -05:00
parent 350677bbce
commit 5b3d84ee24
8 changed files with 137 additions and 94 deletions

View File

@ -80,8 +80,10 @@ typedef enum {
- (id) getInboxQuota;
- (BOOL) updateFilters;
- (BOOL) updateFiltersAndForceActivation: (BOOL) forceActivation;
- (BOOL) updateFiltersWithUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation;
- (NSArray *) identities;
- (NSString *) signature;

View File

@ -300,19 +300,32 @@ static NSString *inboxFolderName = @"INBOX";
return inboxQuota;
}
- (BOOL) updateFiltersAndForceActivation: (BOOL) forceActivation
{
return [self updateFiltersWithUsername: nil
andPassword: nil
forceActivation: forceActivation];
}
- (BOOL) updateFilters
{
return [self updateFiltersWithUsername: nil andPassword: nil];
return [self updateFiltersWithUsername: nil
andPassword: nil
forceActivation: NO];
}
- (BOOL) updateFiltersWithUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation
{
SOGoSieveManager *manager;
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
return [manager updateFiltersForAccount: self withUsername: theUsername andPassword: thePassword];
return [manager updateFiltersForAccount: self
withUsername: theUsername
andPassword: thePassword
forceActivation: forceActivation];
}

View File

@ -50,10 +50,13 @@
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
- (BOOL) hasActiveExternalSieveScripts: (NGSieveClient *) client;
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount;
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword;
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation;
@end

View File

@ -804,6 +804,24 @@ static NSString *sieveScriptName = @"sogo";
return [client autorelease];
}
- (BOOL) hasActiveExternalSieveScripts: (NGSieveClient *) client
{
NSDictionary *scripts;
NSEnumerator *keys;
NSString *key;
scripts = [client listScripts];
keys = [scripts keyEnumerator];
while ((key = [keys nextObject]))
{
if ([key caseInsensitiveCompare: @"sogo"] != NSOrderedSame &&
[[[scripts objectForKey: key] stringValue] length] > 0)
return YES;
}
return NO;
}
//
//
@ -812,7 +830,8 @@ static NSString *sieveScriptName = @"sogo";
{
return [self updateFiltersForAccount: theAccount
withUsername: nil
andPassword: nil];
andPassword: nil
forceActivation: NO];
}
//
@ -821,6 +840,7 @@ static NSString *sieveScriptName = @"sogo";
- (BOOL) updateFiltersForAccount: (SOGoMailAccount *) theAccount
withUsername: (NSString *) theUsername
andPassword: (NSString *) thePassword
forceActivation: (BOOL) forceActivation
{
NSString *filterScript, *v, *content;
NSMutableArray *req;
@ -830,7 +850,7 @@ static NSString *sieveScriptName = @"sogo";
SOGoDomainDefaults *dd;
NGSieveClient *client;
NGImap4Client *imapClient;
BOOL b, dateCapability;
BOOL b, activate, dateCapability;
unsigned int now;
dd = [user domainDefaults];
@ -844,6 +864,9 @@ static NSString *sieveScriptName = @"sogo";
if (!client)
return NO;
// Activate script Sieve when forced or when no external script is enabled
activate = forceActivation || ![self hasActiveExternalSieveScripts: client];
// We adjust the "methodRequirements" based on the server's
// capabilities. Cyrus exposes "imapflags" while Dovecot (and
// potentially others) expose "imap4flags" as specified in RFC5332
@ -1094,6 +1117,7 @@ static NSString *sieveScriptName = @"sogo";
intoArray: req];
[script appendString: @"\n"];
[script appendString: v];
b = YES;
}
}
@ -1102,21 +1126,23 @@ static NSString *sieveScriptName = @"sogo";
header = [NSString stringWithFormat: @"require [\"%@\"];\r\n",
[[req uniqueObjects] componentsJoinedByString: @"\",\""]];
[script insertString: header atIndex: 0];
b = YES;
}
/* We ensure to deactive the current active script since it could prevent
its deletion from the server. */
if (activate)
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 && [script length])
{
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];
result = [client putScript: sieveScriptName script: script];
if (![[result valueForKey:@"result"] boolValue])
@ -1126,12 +1152,15 @@ static NSString *sieveScriptName = @"sogo";
return NO;
}
result = [client setActiveScript: sieveScriptName];
if (![[result valueForKey:@"result"] boolValue])
if (activate)
{
[self logWithFormat: @"Could not enable Sieve script: %@", result];
[client closeConnection];
return NO;
result = [client setActiveScript: sieveScriptName];
if (![[result valueForKey:@"result"] boolValue])
{
[self logWithFormat: @"Could not enable Sieve script: %@", result];
[client closeConnection];
return NO;
}
}
}

View File

@ -25,18 +25,23 @@
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGImap4/NGSieveClient.h>
#import <SOPE/NGCards/iCalRecurrenceRule.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoSieveManager.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoUserProfile.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import <SOGoUI/UIxComponent.h>
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h>
#import <Mailer/SOGoMailLabel.h>
#import "UIxJSONPreferences.h"
@ -71,6 +76,27 @@ static SoProduct *preferencesProduct = nil;
return labelsDictionary;
}
//
// Used internally
//
- (BOOL) _hasActiveExternalSieveScripts
{
NGSieveClient *client;
SOGoMailAccount *account;
SOGoMailAccounts *folder;
SOGoSieveManager *manager;
folder = [[[context activeUser] homeFolderInContext: context] mailAccountsFolder: @"Mail" inContext: context];
account = [folder lookupName: @"0" inContext: context acquire: NO];
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
client = [manager clientForAccount: account];
if (client)
return [manager hasActiveExternalSieveScripts: client];
return NO;
}
- (WOResponse *) jsonDefaultsAction
{
return [self responseWithStatus: 200
@ -401,6 +427,9 @@ static SoProduct *preferencesProduct = nil;
[values setObject: vacation forKey: @"Vacation"];
}
// Detect if an external Sieve script is active
[values setObject: [NSNumber numberWithBool: [self _hasActiveExternalSieveScripts]] forKey: @"hasActiveExternalSieveScripts"];
return [values jsonRepresentation];
}

View File

@ -728,25 +728,6 @@ static NSArray *reminderValues = nil;
return [[user domainDefaults] sieveScriptsEnabled];
}
- (NSString *) hasActiveExternalSieveScripts
{
NSDictionary *scripts;
NSEnumerator *keys;
NSString *key;
scripts = [[self _sieveClient] listScripts];
keys = [scripts keyEnumerator];
while ((key = [keys nextObject]))
{
if ([key caseInsensitiveCompare: @"sogo"] != NSOrderedSame &&
[[scripts objectForKey: key] boolValue])
return @"true";
}
return @"false";
}
//
// Used by wox template
//
@ -1645,12 +1626,13 @@ static NSArray *reminderValues = nil;
// We check if the Sieve server is available *ONLY* if at least one of the option is enabled
if (!([dd sieveScriptsEnabled] || [dd vacationEnabled] || [dd forwardEnabled]) || [self _isSieveServerAvailable])
{
BOOL forceActivation = ![[v objectForKey: @"hasActiveExternalSieveScripts"] boolValue];
folder = [[[context activeUser] homeFolderInContext: context] mailAccountsFolder: @"Mail"
inContext: context];
account = [folder lookupName: @"0" inContext: context acquire: NO];
if (![account updateFilters])
if (![account updateFiltersAndForceActivation: forceActivation])
{
results = (id <WOActionResults>) [self responseWithStatus: 502
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Connection error", @"message", nil]];

View File

@ -23,7 +23,6 @@
var forwardConstraints = <var:string value="forwardConstraints" const:escapeHTML="NO"/>;
var forwardConstraintsDomains = <var:string value="forwardConstraintsDomains" const:escapeHTML="NO"/>;
var sieveCapabilities = <var:string value="sieveCapabilities" const:escapeHTML="NO"/>;
var hasActiveExternalSieveScripts = <var:string value="hasActiveExternalSieveScripts" const:escapeHTML="NO"/>;
</script>
<script type="text/ng-template" id="preferences.html">
@ -839,7 +838,12 @@
aria-controls="mailFiltersTab-content"
label:label="Filters"
sg-labels="MailerUI">
<md-content role="tabpanel" aria-labelledby="mailFiltersView" ng-hide="app.hasActiveExternalSieveScripts">
<md-content class="md-padding"
ng-include="'hasActiveExternalSieveScripts.html'"
ng-show="app.preferences.defaults.hasActiveExternalSieveScripts">
<!-- external Sieve script detected -->
</md-content>
<md-content role="tabpanel" aria-labelledby="mailFiltersView">
<div class="sg-list-sortable"
ng-model="app.preferences.defaults.SOGoSieveFilters"
as-sortable="{ scrollableContainer: '.sg-list-sortable', containment: '.sg-list-sortable', containerPositioning: 'relative', orderChanged: app.onFiltersOrderChanged(preferencesForm) }">
@ -885,22 +889,6 @@
</md-button>
</div>
</md-content>
<md-content class="md-padding" ng-show="app.hasActiveExternalSieveScripts">
<md-card flex="50" flex-xs="100">
<md-card-title>
<md-card-title-text>
<span class="md-headline"><var:string label:value="An external Sieve script is active"/></span>
<span class="md-subhead"><var:string label:value="Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled." /></span>
</md-card-title-text>
</md-card-title>
<md-card-actions layout="row" layout-align="end center">
<md-button type="button" class="md-raised md-warn"
ng-click="app.hasActiveExternalSieveScripts = false">
<var:string label:value="Let SOGo handle everything" />
</md-button>
</md-card-actions>
</md-card>
</md-content>
</md-tab>
</var:if>
<!-- END OF MAIL > FILTERS -->
@ -908,9 +896,13 @@
<!-- MAIL > VACATION -->
<var:if condition="isVacationEnabled">
<md-tab id="mailVacationTab" aria-controls="mailVacationTab-content" label:label="Vacation">
<md-content class="md-padding"
ng-include="'hasActiveExternalSieveScripts.html'"
ng-show="app.preferences.defaults.hasActiveExternalSieveScripts">
<!-- external Sieve script detected -->
</md-content>
<div role="tabpanel" aria-labelledby="mailVacationView"
id="mailVacationView-content" class="md-padding"
ng-hide="app.hasActiveExternalSieveScripts">
id="mailVacationView-content" class="md-padding">
<md-checkbox
ng-model="app.preferences.defaults.Vacation.enabled"
@ -1056,22 +1048,6 @@
</div>
</div>
<md-content class="md-padding" ng-show="app.hasActiveExternalSieveScripts">
<md-card flex="50" flex-xs="100">
<md-card-title>
<md-card-title-text>
<span class="md-headline"><var:string label:value="An external Sieve script is active"/></span>
<span class="md-subhead"><var:string label:value="Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled." /></span>
</md-card-title-text>
</md-card-title>
<md-card-actions layout="row" layout-align="end center">
<md-button type="button" class="md-raised md-warn"
ng-click="app.hasActiveExternalSieveScripts = false">
<var:string label:value="Let SOGo handle everything" />
</md-button>
</md-card-actions>
</md-card>
</md-content>
</md-tab>
</var:if>
<!-- END OF MAIL > VACATION -->
@ -1079,9 +1055,13 @@
<!-- MAIL > FORWARD -->
<var:if condition="isForwardEnabled">
<md-tab id="mailForwardTab" aria-controls="mailForwardTab-content" label:label="Forward">
<md-content class="md-padding"
ng-include="'hasActiveExternalSieveScripts.html'"
ng-show="app.preferences.defaults.hasActiveExternalSieveScripts">
<!-- external Sieve script detected -->
</md-content>
<div role="tabpanel" aria-labelledby="mailForwardView"
id="mailForwardView-content" class="md-padding"
ng-hide="app.hasActiveExternalSieveScripts">
id="mailForwardView-content" class="md-padding">
<md-checkbox
ng-model="app.preferences.defaults.Forward.enabled"
@ -1109,22 +1089,6 @@
</div>
</div>
<md-content class="md-padding" ng-show="app.hasActiveExternalSieveScripts">
<md-card flex="50" flex-xs="100">
<md-card-title>
<md-card-title-text>
<span class="md-headline"><var:string label:value="An external Sieve script is active"/></span>
<span class="md-subhead"><var:string label:value="Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled." /></span>
</md-card-title-text>
</md-card-title>
<md-card-actions layout="row" layout-align="end center">
<md-button type="button" class="md-raised md-warn"
ng-click="app.hasActiveExternalSieveScripts = false">
<var:string label:value="Let SOGo handle everything" />
</md-button>
</md-card-actions>
</md-card>
</md-content>
</md-tab>
</var:if>
<!-- END OF MAIL > FORWARD -->
@ -1132,5 +1096,22 @@
</md-tabs>
</var:if>
</script>
<script type="text/ng-template" id="hasActiveExternalSieveScripts.html">
<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline"><var:string label:value="An external Sieve script is active"/></span>
<span class="md-subhead"><var:string label:value="Sieve is a programming language that can be used for email filtering. If you let SOGo handle your filters, vacation and forward settings, your active script will be disabled." /></span>
</md-card-title-text>
</md-card-title>
<md-card-actions layout="row" layout-align="end center">
<md-button type="button" class="md-raised md-warn"
ng-click="app.manageSieveScript(preferencesForm)">
<var:string label:value="Let SOGo handle everything" />
</md-button>
</md-card-actions>
</md-card>
</script>
</var:component>

View File

@ -18,7 +18,6 @@
this.timeZonesSearchText = '';
this.sieveVariablesCapability = ($window.sieveCapabilities.indexOf('variables') >= 0);
this.mailLabelKeyRE = new RegExp(/^(?!^_\$)[^(){} %*\"\\\\]*?$/);
this.hasActiveExternalSieveScripts = $window.hasActiveExternalSieveScripts;
// Set alternate avatar in User service
if (Preferences.defaults.SOGoAlternateAvatar)
@ -285,6 +284,11 @@
});
};
this.manageSieveScript = function(form) {
this.preferences.defaults.hasActiveExternalSieveScripts = false;
form.$setDirty();
};
this.confirmChanges = function($event, form) {
var target;