diff --git a/ChangeLog b/ChangeLog index 8cce1fce5..b269b0356 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-10-08 Ludovic Marcotte + + * Added filters support (Vacation and Forward) which + can be enabled using the SOGoVacationEnabled and + SOGoForwardEnabled defaults. One Sieve right now is + support and we assume the Sieve server is the same + as the IMAP server, running on port 2000. + 2009-10-08 Wolfgang Sourdeau * SoObjects/SOGo/LDAPSource.m (-_searchAttributes): we already diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index e8befd19e..f6c87bd2b 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -1,20 +1,21 @@ /* + Copyright (C) 2009 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -52,6 +53,7 @@ - (void) setAccountName: (NSString *) newAccountName; - (BOOL) supportsQuotas; +- (BOOL) updateFilters; /* folder pathes */ @@ -68,7 +70,6 @@ - (NSString *) inboxFolderNameInContext: (id)_ctx; - (NSString *) draftsFolderNameInContext: (id)_ctx; -// - (NSString *) sieveFolderNameInContext: (id)_ctx; - (NSString *) sentFolderNameInContext: (id)_ctx; - (NSString *) trashFolderNameInContext: (id)_ctx; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 01278aeb2..750edf6dc 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -15,7 +15,7 @@ License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -29,12 +29,14 @@ #import #import #import +#import #import #import #import #import #import #import +#import #import #import @@ -52,11 +54,12 @@ static NSArray *rootFolderNames = nil; static NSString *inboxFolderName = @"INBOX"; static NSString *draftsFolderName = @"Drafts"; -static NSString *sieveFolderName = @"Filters"; static NSString *sentFolderName = nil; static NSString *trashFolderName = nil; -static NSString *sharedFolderName = @""; // TODO: add English default +static NSString *sharedFolderName = @""; // TODO: add English default static NSString *otherUsersFolderName = @""; // TODO: add English default +static NSString *sieveScriptName = @"sogo"; + static BOOL defaultShowSubscribedFoldersOnly = NO; // this is temporary, until we allow users to manage their own accounts static NSString *fallbackIMAP4Server = nil; @@ -91,15 +94,10 @@ static NSString *fallbackIMAP4Server = nil; NSLog(@"Note: using shared-folders name: '%@'", sharedFolderName); NSLog(@"Note: using other-users-folders name: '%@'", otherUsersFolderName); - if ([ud boolForKey: @"SOGoEnableSieveFolder"]) - rootFolderNames = [[NSArray alloc] initWithObjects: - draftsFolderName, - sieveFolderName, - nil]; - else - rootFolderNames = [[NSArray alloc] initWithObjects: - draftsFolderName, - nil]; + + rootFolderNames = [[NSArray alloc] initWithObjects: + draftsFolderName, + nil]; if (!fallbackIMAP4Server) { @@ -206,6 +204,147 @@ static NSString *fallbackIMAP4Server = nil; return [capability containsObject: @"quota"]; } +- (BOOL) updateFilters +{ + NSMutableString *header, *script; + NGInternetSocketAddress *address; + NSDictionary *result, *values; + NSUserDefaults *ud; + NGSieveClient *client; + NSString *v; + BOOL b; + + if (![[NSUserDefaults standardUserDefaults] boolForKey: @"SOGoVacationEnabled"] && + ![[NSUserDefaults standardUserDefaults] boolForKey: @"SOGoForwardEnabled"]) + return YES; + + ud = [[context activeUser] userDefaults]; + b = NO; + + header = [NSMutableString stringWithString: @"require ["]; + 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 objectForKey: @"Vacation"]; + + // 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; + + [header appendString: @"\"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 objectForKey: @"Forward"]; + + if (values && [[values objectForKey: @"enabled"] boolValue]) + { + b = YES; + + v = [values objectForKey: @"forwardAddress"]; + + if (v && [v length] > 0) + [script appendFormat: @"redirect \"%@\";\r\n", v]; + + if ([[values objectForKey: @"keepCopy"] boolValue]) + [script appendString: @"keep;\r\n"]; + } + + if ([header compare: @"require ["] != NSOrderedSame) + { + [header appendString: @"];\r\n"]; + [script insertString: header atIndex: 0]; + } + + // We connect to our Sieve server and upload the script + address = [NGInternetSocketAddress addressWithPort: 2000 + onHost: [[self imap4URL] host]]; + + client = [NGSieveClient clientWithAddress: address]; + + if (!client) { + [self errorWithFormat: @"Sieve connection failed on %@", [address description]]; + return NO; + } + + result = [client login: [[self imap4URL] user] password:[self imap4Password]]; + + if (![[result valueForKey:@"result"] boolValue]) { + [self errorWithFormat: @"Could not login '%@' (%@) on Sieve server: %@: %@", + [[self imap4URL] user], [self imap4Password], client, result]; + [client closeConnection]; + return NO; + } + + // 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; +} + + /* hierarchy */ - (SOGoMailAccount *) mailAccountFolder @@ -408,11 +547,6 @@ static NSString *fallbackIMAP4Server = nil; return folderName; } -// - (NSString *) sieveFolderNameInContext: (id) _ctx -// { -// return sieveFolderName; -// } - - (NSString *) sentFolderNameInContext: (id)_ctx { NSString *folderName; diff --git a/SoObjects/SOGo/SOGoUserFolder.h b/SoObjects/SOGo/SOGoUserFolder.h index 795a3379b..2de151a56 100644 --- a/SoObjects/SOGo/SOGoUserFolder.h +++ b/SoObjects/SOGo/SOGoUserFolder.h @@ -55,6 +55,9 @@ - (id) freeBusyObject: (NSString *) _key inContext: (WOContext *) _ctx; +- (id) mailAccountsFolder: (NSString *) _key + inContext: (WOContext *) _ctx; + @end #endif /* __SOGo_SOGoUserFolder_H__ */ diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 6a143ea11..a492bc168 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -32,7 +32,10 @@ #import #import +#import #import +#import +#import #import "../../Main/SOGo.h" #import "UIxPreferences.h" @@ -901,7 +904,21 @@ static BOOL forwardEnabled = NO; request = [context request]; if ([[request method] isEqualToString: @"POST"]) { + SOGoMailAccount *account; + id mailAccounts; + id folder; + [userDefaults synchronize]; + + mailAccounts = [[[context activeUser] mailAccounts] objectAtIndex: 0]; + folder = [[self clientObject] mailAccountsFolder: @"Mail" + inContext: context]; + account = [folder lookupName: [[mailAccounts objectForKey: @"name"] asCSSIdentifier] + inContext: context + acquire: NO]; + + [account updateFilters]; + if (composeMessageTypeHasChanged) // Due to a limitation of CKEDITOR, we reload the page when the user // changes the composition mode to avoid Javascript errors.