View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001392 | SOGo | Backend Mail | public | 2011-07-20 22:09 | 2019-11-11 14:12 |
Reporter | mra | Assigned To | ludovic | ||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | resolved | Resolution | fixed | ||
Product Version | nightly v2 | ||||
Fixed in Version | 4.2.0 | ||||
Summary | 0001392: Patch: Inclusion/Embedding of server-side sieve scripts into standard sogo-script | ||||
Description | In our company we migrated to SOGo with Dovecot and Postfix for IMAP and SMTP. REMARK: This feature is IMHO only usable for experienced Users! If the embedded (userwritten) Script has an error, the sogo.sieve-script will fail, too! | ||||
Additional Information | ConfigurationSOGoSievePrependFilterName - name of user sieve script, which will run before sogo.sieve SOGoSieveAppendFilterName - name of user sieve script, which will run at the end of sogo.sieve SOGoSievePrependGlobalFilterName - Dovecot special: name of configured global sieve script, which will run before the user sieve script in sogo.sieve (should be unofficial, maybe removed) SOGoSieveAppendGlobalFilterName - Dovecot special: name of configured global sieve script, which will run after the use sieve script in sogo.sieve (should be unofficial, maybe removed) SOGoSieveInsertServerFilter - Default is NO, if YES than all the sieve scripts which current stored in the users sieve directory will be used instead of the configured 'SOGoSieve*FilterName' scripts. The convention is
Example:
SOGoSieveDebugEnabled - enables some debug messages DependenciesThis patch depends on 0001391 FunctionThe external scripts inclusion into sogo.sieve will be done in two possible ways
The partly used sieve plugin 'include' is a RFC Proprosal for sieve and is current implemented only in dovecot,
REMARKThe plugin uses only the 'include' command with the locations ":personal" and (if configured) ':global'. | ||||
Tags | No tags attached. | ||||
2011-07-20 22:09
|
patch_SOGo_sieve_external_scripts.diff (16,363 bytes)
# # old_revision [6573eb6d5779706b5935161822c561cf325f7fa6] # # patch "SoObjects/SOGo/SOGoDomainDefaults.h" # from [0b2be24987ff61077a11ffe0bf02116625d782d1] # to [88b910b96713c21a9d0710066a0f569e8e3eefa7] # # patch "SoObjects/SOGo/SOGoDomainDefaults.m" # from [fa3475a65ccea244b1223c960aa0dbd8dd990be4] # to [f380c5be1577fe21a0e2c27d1944b3e124623e9c] # # patch "SoObjects/SOGo/SOGoSieveManager.h" # from [d3a6cf989982a7dc20ef92ffc786f2e278742951] # to [8bc04ad0515f258519b08969699eb3374e24ae2c] # # patch "SoObjects/SOGo/SOGoSieveManager.m" # from [3d8e3ce324744a5bb8ebfe9d8fdb738a26bd6b92] # to [03246493ff500bf6224324f1820ca235a24972fc] # # patch "UI/PreferencesUI/UIxPreferences.m" # from [118f6316688e83962d53584288dd47e498270881] # to [495102cb29027e04b934358bd6c3ffbd0cf4f0c2] # ============================================================ --- SoObjects/SOGo/SOGoDomainDefaults.h 0b2be24987ff61077a11ffe0bf02116625d782d1 +++ SoObjects/SOGo/SOGoDomainDefaults.h 88b910b96713c21a9d0710066a0f569e8e3eefa7 @@ -47,7 +47,17 @@ - (NSString *) imapFolderSeparator; - (BOOL) imapAclConformsToIMAPExt; - (BOOL) forceIMAPLoginWithEmail; + + +- (NSString *) sievePrependFilterName; +- (NSString *) sieveAppendFilterName; +- (NSString *) sievePrependGlobalFilterName; +- (NSString *) sieveAppendGlobalFilterName; +- (BOOL) sieveInsertServerFilter; - (BOOL) sieveScriptsEnabled; +- (BOOL) sieveDebugEnabled; + + - (BOOL) forwardEnabled; - (BOOL) vacationEnabled; - (NSString *) mailingMechanism; ============================================================ --- SoObjects/SOGo/SOGoDomainDefaults.m fa3475a65ccea244b1223c960aa0dbd8dd990be4 +++ SoObjects/SOGo/SOGoDomainDefaults.m f380c5be1577fe21a0e2c27d1944b3e124623e9c @@ -176,6 +176,39 @@ return [self boolForKey: @"SOGoSieveScriptsEnabled"]; } + + +- (NSString *) sievePrependFilterName +{ + return [self stringForKey: @"SOGoSievePrependFilterName"]; +} + +- (NSString *) sieveAppendFilterName +{ + return [self stringForKey: @"SOGoSieveAppendFilterName"]; +} + +- (NSString *) sievePrependGlobalFilterName +{ + return [self stringForKey: @"SOGoSievePrependGlobalFilterName"]; +} + +- (NSString *) sieveAppendGlobalFilterName +{ + return [self stringForKey: @"SOGoSieveAppendGlobalFilterName"]; +} + +- (BOOL) sieveInsertServerFilter +{ + return [self boolForKey: @"SOGoSieveInsertServerFilter"]; +} + +- (BOOL) sieveDebugEnabled +{ + return [self boolForKey: @"SOGoSieveDebugEnabled"]; +} + + - (BOOL) forwardEnabled { return [self boolForKey: @"SOGoForwardEnabled"]; @@ -186,6 +219,10 @@ return [self boolForKey: @"SOGoVacationEnabled"]; } + + + + - (NSString *) mailingMechanism { NSString *mailingMechanism; ============================================================ --- SoObjects/SOGo/SOGoSieveManager.h d3a6cf989982a7dc20ef92ffc786f2e278742951 +++ SoObjects/SOGo/SOGoSieveManager.h 8bc04ad0515f258519b08969699eb3374e24ae2c @@ -31,6 +31,7 @@ @class NSString; @class SOGoMailAccount; @class SOGoUser; +@class NGSieveClient; @interface SOGoSieveManager : NSObject { @@ -45,11 +46,24 @@ - (NSString *) sieveScriptWithRequirements: (NSMutableArray *) newRequirements; - (NSString *) lastScriptError; + - (BOOL) updateFiltersForLogin: (NSString *) theLogin authname: (NSString *) theAuthName password: (NSString *) thePassword account: (SOGoMailAccount *) theAccount; +- (NSString *) makeScriptEntryWithScript:(NSString*)scriptName + withContent:(BOOL)retrieveContent + andClient:(NGSieveClient *)client; + +- (NSString *) makeScriptEntryWithScript:(NSString*)scriptName + withContent:(BOOL)retrieveContent + andClient:(NGSieveClient *)client + asGlobal:(BOOL) isGlobal; + +- (NSDictionary *) loadScriptList: (NGSieveClient *) sieveClient; + + @end #endif /* SOGOSIEVEMANAGER_H */ ============================================================ --- SoObjects/SOGo/SOGoSieveManager.m 3d8e3ce324744a5bb8ebfe9d8fdb738a26bd6b92 +++ SoObjects/SOGo/SOGoSieveManager.m 03246493ff500bf6224324f1820ca235a24972fc @@ -26,6 +26,7 @@ #import <Foundation/NSString.h> #import <Foundation/NSURL.h> #import <Foundation/NSValue.h> +#import <NGExtensions/NSString+Ext.h> #import <SOGo/NSArray+Utilities.h> #import <SOGo/NSDictionary+Utilities.h> @@ -618,10 +619,10 @@ static NSString *sieveScriptName = @"sog - (BOOL) updateFiltersForLogin: (NSString *) theLogin authname: (NSString *) theAuthName password: (NSString *) thePassword - account: (SOGoMailAccount *) theAccount + account: (SOGoMailAccount *) theAccount { NSMutableArray *req; - NSMutableString *script, *header; + NSMutableString *script, *header, *appendScriptStr, *prependScriptStr; NGInternetSocketAddress *address; NSDictionary *result, *values; SOGoUserDefaults *ud; @@ -640,6 +641,8 @@ static NSString *sieveScriptName = @"sog b = NO; script = [NSMutableString string]; + appendScriptStr = [NSMutableString string]; // needed for include + prependScriptStr = [NSMutableString string]; // needed for include // Right now, we handle Sieve filters here and only for vacation // and forwards. Traditional filters support (for fileinto, for @@ -713,6 +716,9 @@ static NSString *sieveScriptName = @"sog if ([[values objectForKey: @"keepCopy"] boolValue]) [script appendString: @"keep;\r\n"]; } + + + filterScript = [self sieveScriptWithRequirements: req]; if (filterScript) @@ -729,13 +735,8 @@ static NSString *sieveScriptName = @"sog 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: @@ -793,6 +794,9 @@ static NSString *sieveScriptName = @"sog [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)"); @@ -807,14 +811,164 @@ static NSString *sieveScriptName = @"sog return NO; } + + // Handle external scripts for inclusion in sogo.sieve + BOOL useExternalScripts = [dd sieveInsertServerFilter] + || [dd sievePrependFilterName] + || [dd sieveAppendFilterName] + || [dd sievePrependGlobalFilterName] + || [dd sieveAppendGlobalFilterName]; + + + if(useExternalScripts) + { + NSMutableDictionary *serverscripts = [NSMutableDictionary dictionary]; + + NSMutableArray *_preFiles = [NSMutableArray array]; + NSMutableArray *_postFiles = [NSMutableArray array]; + NSMutableArray *_preGlobalFiles = [NSMutableArray array]; + NSMutableArray *_postGlobalFiles = [NSMutableArray array]; + + // Check for an available 'include' sieve plugin. + // When the plugin is not available, + // - the script/s will be included directly as source (read from server, lasts some microseconds longer ;-) ) + // - global scripts not possible (they are only possible, too, when configured in dovecot) + // + BOOL withScriptContent = ![client hasCapability: @"include"]; + + if(withScriptContent) + { + NSLog(@"WARNING! Configured managesieve server does not support the 'include' plugin. Including scripts directly."); + } + else + { + // Usefull if available - so you do not need to include all the stuff in one file + [req addObjectUniquely: @"include"]; + } + + if([dd sieveInsertServerFilter]) // Insert all current filters on and from the Server, but without sogo.sieve + { + // Read all current scripts from server, but they must be sorted by a prefix 'pre_' and/or 'post_' + // Sorting with number after the prefix will help ... + serverscripts = [self loadScriptList: client]; + } + else // Std case: pre-configured scriptnames for pre-run and post-run + { + // prepend an external script, name is configured in sievePrependFilterName + if([dd sievePrependFilterName]) + { + [_preFiles addObject: [dd sievePrependFilterName]]; + } + + if([dd sieveAppendFilterName]) + { + [_postFiles addObject: [dd sieveAppendFilterName]]; + } + + // prepend an global external script, name is configured in sievePrependGlobalFilterName + // This runs only when the 'include' sieve-plugin is available + if(!withScriptContent) + { + if([dd sievePrependGlobalFilterName]) + { + [_preGlobalFiles addObject: [dd sievePrependGlobalFilterName]]; + } + + + if([dd sieveAppendGlobalFilterName]) + { + [_postGlobalFiles addObject: [dd sieveAppendGlobalFilterName]]; + } + } + + [serverscripts setObject: _preFiles forKey: @"prefiles" ]; + [serverscripts setObject: _preGlobalFiles forKey: @"globalPrefiles" ]; + + [serverscripts setObject: _postFiles forKey: @"postfiles" ]; + [serverscripts setObject: _postGlobalFiles forKey: @"globalPostfiles" ]; + } + + + // Now creating stuff from 'serverscripts' and make scripts + if([dd sieveDebugEnabled]) NSLog(@"SIEVE: Now creating stuff from 'serverscripts' and make sieve scripts"); + NSString *_scriptName; + + if([[serverscripts objectForKey: @"prefiles"] count] > 0) + { + NSEnumerator *preFiles = [[serverscripts objectForKey: @"prefiles"] objectEnumerator]; + + //while(_scriptName = (NSString *)[preFiles nextObject]) + while(_scriptName = [preFiles nextObject]) + { + [prependScriptStr appendString: [self makeScriptEntryWithScript: _scriptName + withContent:withScriptContent + andClient: client]]; + } + } + + if([[serverscripts objectForKey: @"postfiles"] count] > 0) + { + NSEnumerator *postFiles = [[serverscripts objectForKey: @"postfiles"] objectEnumerator]; + + // while(_scriptName = (NSString *)[postFiles nextObject]) + while(_scriptName = [postFiles nextObject]) + { + [appendScriptStr appendString: [self makeScriptEntryWithScript: _scriptName + withContent:withScriptContent + andClient: client]]; + } + } + + // Global + if(!withScriptContent) // Global scripts could only be used with the sieve-plugin 'include' AND server configuration (known in dovecot) + { + [prependScriptStr appendString: [self makeScriptEntryWithScript: _scriptName + withContent:NO + andClient: client + asGlobal: YES]]; + + [appendScriptStr appendString: [self makeScriptEntryWithScript: _scriptName + withContent:NO + andClient: client + asGlobal: YES]]; + } + + + // sievePrepend*FilterName MUST be the first entry after file header + if([prependScriptStr length]) + { + [script insertString: prependScriptStr atIndex: 0]; + } + + // sieveAppend*FilterName MUST be the last entry + if([appendScriptStr length]) + { + [script appendString: appendScriptStr]; + } + } + + + // Creating require-string + if ([req count]) + { + header = [NSString stringWithFormat: @"require [\"%@\"];\r\n", + [req componentsJoinedByString: @"\",\""]]; + [script insertString: header atIndex: 0]; + } + + if([dd sieveDebugEnabled]) NSLog(@"SIEVE: Created sieve script:\r\n----\r\n%@", script); + + /* 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); + 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 @@ -836,8 +990,129 @@ static NSString *sieveScriptName = @"sog return NO; } } + + return YES; +} - return YES; + +- (NSString *) makeScriptEntryWithScript:(NSString*)scriptName + withContent:(BOOL)retrieveContent + andClient:(NGSieveClient *)sieveClient +{ + return [self makeScriptEntryWithScript: scriptName + withContent:retrieveContent + andClient:sieveClient + asGlobal:NO]; } +- (NSString *) makeScriptEntryWithScript:(NSString*)scriptName + withContent:(BOOL)retrieveContent + andClient:(NGSieveClient *)sieveClient + asGlobal:(BOOL) isGlobal +{ + // Configdata + SOGoDomainDefaults *dd = [user domainDefaults]; + + // when a content is given (!= NIL), a "global" file is not "reachable" + if(retrieveContent) + { + isGlobal = NO; + } + + NSString *sieveFileName = [scriptName stringWithoutSuffix:@".sieve"]; + + // Check, if configures local/":personal" configured in 'sieve*FilterName' are on place. + // If not, create it + if(isGlobal == NO && ![sieveClient getScript: sieveFileName]) + { + if([dd sieveDebugEnabled]) NSLog(@"SIEVE: Script '%@' not found, now creating it.", scriptName); + [sieveClient putScript: sieveFileName script:[NSString stringWithFormat: @"# Dummy file '%@' created by SOGo", scriptName]]; + } + + + // when global, another include-string has to be created + if(isGlobal) + { + if(!scriptName) + { + return @""; + } + + // create include global configured '*FilterName' + return [NSString stringWithFormat: @"\r\ninclude :global \"%@\";\r\n\r\n", scriptName]; + } + else + { + if(retrieveContent) + { + // get content of script and return it + if(![sieveClient getScript: sieveFileName]) // Create file if not available + { + if([dd sieveDebugEnabled]) NSLog(@"SIEVE: Script '%@' not found, now creating it as dummy.", scriptName); + [sieveClient putScript: sieveFileName script:[NSString stringWithFormat: @"# This file '%@' was created by SOGo", scriptName]]; + } + + + NSString *loadedScript = [sieveClient getScript: sieveFileName]; + if(!loadedScript) + { + if([dd sieveDebugEnabled]) NSLog(@"SIEVE: WARNING: Cannot include script content, script '%@' not found.", scriptName); + return nil; // No file, no string. + } + else + { + return [NSString stringWithFormat: @"\r\n##### included file content follows #####\r\n%@\r\n##### END OF included file content #####\r\n", loadedScript]; + } + } + else + { + // create include for personal 'sieve*FilterName' + return [NSString stringWithFormat: @"\r\ninclude :personal \"%@\";\r\n\r\n", scriptName]; + } + } +} + + +- (NSDictionary *) loadScriptList: (NGSieveClient *)sieveClient +{ + NSMutableArray *_preFiles = [NSMutableArray array]; + NSMutableArray *_postFiles = [NSMutableArray array]; + NSString *activeFile = @""; + + // Get list of scripts + NSDictionary *scriptList = [sieveClient listScripts]; + + if([scriptList count] > 0) // objectForKey + { + NSArray *keys = [scriptList allKeys]; + NSArray *values = [scriptList allValues]; + + int i; + for(i=0; i< [keys count]; i++) + { + if([[keys objectAtIndex: i] hasPrefix: @"pre_"]) + { + [_preFiles addObject: [keys objectAtIndex: i]]; + } + else if([[keys objectAtIndex: i] hasPrefix: @"post_"]) + { + [_postFiles addObject: [keys objectAtIndex: i]]; + } + + if([[[values objectAtIndex: i] lowercaseString] isEqualToString: @"active"]) + { + activeFile = [keys objectAtIndex: i]; + } + } + } + + // Packing results into structure + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:4]; + [dict setObject: [NSArray arrayWithArray:_preFiles ] forKey: @"prefiles"]; + [dict setObject: [NSArray arrayWithArray:_postFiles ] forKey: @"postfiles" ]; + [dict setObject: activeFile forKey: @"activeFile"]; + + return dict; +} + @end ============================================================ --- UI/PreferencesUI/UIxPreferences.m 118f6316688e83962d53584288dd47e498270881 +++ UI/PreferencesUI/UIxPreferences.m 495102cb29027e04b934358bd6c3ffbd0cf4f0c2 @@ -975,6 +975,7 @@ folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context]; account = [folder lookupName: @"0" inContext: context acquire: NO]; + [account updateFilters]; if (hasChanged) |
Possible script names in the configuration: SOGoSievePrependFilterName -> pre.sieve |
|
Maybe this patch will show a solution for 0000809 - toy around with sieve under control of SOGo ;-) |
|
This patch could be seen as a child of 0001391, it depends on it. |
|
https://github.com/inverse-inc/sogo/commit/4475ac651d1d94513729d6133a70d0e70ea52b87 |
|
Date Modified | Username | Field | Change |
---|---|---|---|
2011-07-20 22:09 | mra | New Issue | |
2011-07-20 22:09 | mra | File Added: patch_SOGo_sieve_external_scripts.diff | |
2011-07-20 22:13 | mra | Note Added: 0002731 | |
2011-07-20 22:18 | mra | Note Added: 0002732 | |
2012-07-11 07:35 | Christian Mack | Relationship added | parent of 0001391 |
2012-07-11 08:39 | mra | Note Added: 0004121 | |
2015-07-13 14:55 | Christian Mack | Relationship added | related to 0003209 |
2019-11-11 14:12 | ludovic | Note Added: 0013886 | |
2019-11-11 14:12 | ludovic | Status | new => resolved |
2019-11-11 14:12 | ludovic | Fixed in Version | => 4.2.0 |
2019-11-11 14:12 | ludovic | Resolution | open => fixed |
2019-11-11 14:12 | ludovic | Assigned To | => ludovic |