(feat) new junk/not junk capability with generic SMTP integration

pull/187/head
Ludovic Marcotte 2016-01-22 10:30:27 -05:00
parent 04c68e0674
commit 7ca66b4078
22 changed files with 344 additions and 18 deletions

View File

@ -1630,6 +1630,16 @@ Defaults to `Trash` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Trash`.
|U |SOGoJunkFolderName
|Parameter used to set the IMAP folder name used to store junk
messages.
Defaults to `Junk` when unset.
Use a `/` as a hierarchy separator if referring to an IMAP subfolder.
For example: `INBOX/Junk`. Also see the SOGoMailJunkSettings for
more options regarding junk/not-junk actions.
|D |SOGoIMAPCASServiceName
|Parameter used to set the CAS service name (URL) of the imap service.
This is useful if SOGo is connecting to the IMAP service through a
@ -2084,7 +2094,17 @@ Defaults to `%{FolderName} (%{UserName} <%{Email}>)` when unset.
the template named `UIxAdditionalPreferences.wox`. This template should
be put under `~sogo/GNUstep/Library/SOGo/Templates/PreferencesUI/`.
Defaults to `NO` when unset.
|D |SOGoMailJunkSettings
|Parameter used to enable email junk settings. The value is a dictionary
and the follow keys are supported: `vendor` (which must be set to "generic"
for now), `junkEmailAddress` which sets the email address to whom SOGo will
send junk mails to, `notJunkEmailAddress` which sets the email address to
whome SOGo will send non-junk mails to and `limit`, which is an integer value
and sets the maximum number of mails that will be attached to a
junk/not junk report sent by SOGo. Example: `SOGoMailJunkSettings = {
vendor = "generic"; junkEmailAddress = "spam@foo.com";
notJunkEmailAddress = "ham@foo.com"; limit = 10;
};`
|=======================================================================
SOGo Configuration Summary
@ -2113,6 +2133,7 @@ like this:
SOGoDraftsFolderName = Drafts;
SOGoSentFolderName = Sent;
SOGoTrashFolderName = Trash;
SOGoJunkFolderName = Junk;
SOGoMailingMechanism = smtp;
SOGoSMTPServer = 127.0.0.1;
SOGoUserSources = (

6
NEWS
View File

@ -1,3 +1,9 @@
2.3.7 (2016-01-XX)
------------------
New features
- new junk/not junk capability with generic SMTP integration
2.3.6 (2016-01-18)
------------------

View File

@ -23,6 +23,7 @@ Mailer_OBJC_FILES += \
SOGoSentFolder.m \
SOGoDraftsFolder.m \
SOGoTrashFolder.m \
SOGoJunkFolder.m \
\
SOGoMailBodyPart.m \
SOGoHTMLMailBodyPart.m \

View File

@ -0,0 +1,30 @@
/*
Copyright (C) 2007-2016 Inverse inc.
This file is part of SOGo.
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.
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
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#ifndef __Mailer_SOGoJunkFolder_H__
#define __Mailer_SOGoJunkFolder_H__
#import "SOGoMailFolder.h"
@interface SOGoJunkFolder : SOGoSpecialMailFolder
@end
#endif /* __Mailer_SOGoJunkFolder_H__ */

View File

@ -0,0 +1,26 @@
/*
Copyright (C) 2007-2016 Inverse inc.
This file is part of SOGo.
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.
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
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#import "SOGoJunkFolder.h"
@implementation SOGoJunkFolder
@end /* SOGoJunkFolder */

View File

@ -42,6 +42,7 @@
@class SOGoDraftsFolder;
@class SOGoSentFolder;
@class SOGoTrashFolder;
@class SOGoJunkFolder;
typedef enum {
undefined = -1,
@ -55,6 +56,7 @@ typedef enum {
SOGoDraftsFolder *draftsFolder;
SOGoSentFolder *sentFolder;
SOGoTrashFolder *trashFolder;
SOGoJunkFolder *junkFolder;
SOGoIMAPAclStyle imapAclStyle;
NSMutableArray *identities;
NSString *otherUsersFolderName;
@ -95,11 +97,13 @@ typedef enum {
- (NSString *) trashFolderNameInContext: (id)_ctx;
- (NSString *) otherUsersFolderNameInContext: (id)_ctx;
- (NSString *) sharedFoldersNameInContext: (id)_ctx;
- (NSString *) junkFolderNameInContext: (id)_ctx;
- (SOGoMailFolder *) inboxFolderInContext: (id)_ctx;
- (SOGoDraftsFolder *) draftsFolderInContext: (id)_ctx;
- (SOGoSentFolder *) sentFolderInContext: (id)_ctx;
- (SOGoTrashFolder *) trashFolderInContext: (id)_ctx;
- (SOGoJunkFolder *) junkFolderInContext: (id)_ctx;
/* namespaces */

View File

@ -1,6 +1,6 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2007-2016 Inverse inc.
This file is part of SOGo.
@ -58,6 +58,7 @@
#import "SOGoMailNamespace.h"
#import "SOGoSentFolder.h"
#import "SOGoTrashFolder.h"
#import "SOGoJunkFolder.h"
#import "SOGoUser+Mailer.h"
#import "SOGoMailAccount.h"
@ -78,6 +79,7 @@ static NSString *inboxFolderName = @"INBOX";
draftsFolder = nil;
sentFolder = nil;
trashFolder = nil;
junkFolder = nil;
imapAclStyle = undefined;
identities = nil;
otherUsersFolderName = nil;
@ -93,6 +95,7 @@ static NSString *inboxFolderName = @"INBOX";
[draftsFolder release];
[sentFolder release];
[trashFolder release];
[junkFolder release];
[identities release];
[otherUsersFolderName release];
[sharedFoldersName release];
@ -366,6 +369,7 @@ static NSString *inboxFolderName = @"INBOX";
[self draftsFolderNameInContext: context],
[self sentFolderNameInContext: context],
[self trashFolderNameInContext: context],
[self junkFolderNameInContext: context],
nil] stringsWithFormat: @"/%@"];
folders = [[self imap4Connection] allFoldersForURL: [self imap4URL]
onlySubscribedFolders: subscribedOnly];
@ -408,6 +412,8 @@ static NSString *inboxFolderName = @"INBOX";
folderType = @"sent";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self trashFolderNameInContext: context]]])
folderType = @"trash";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self junkFolderNameInContext: context]]])
folderType = @"junk";
else
folderType = @"folder";
@ -678,6 +684,7 @@ static NSString *inboxFolderName = @"INBOX";
ud = [[context activeUser] userDefaults];
// We skip the Junk folder here, as EAS doesn't know about this
if ([ud synchronizeOnlyDefaultMailFolders])
folderList = [[NSArray arrayWithObjects:
[self inboxFolderNameInContext: context],
@ -773,6 +780,9 @@ static NSString *inboxFolderName = @"INBOX";
else if ([folderName
isEqualToString: [self trashFolderNameInContext: _ctx]])
klazz = [SOGoTrashFolder class];
else if ([folderName
isEqualToString: [self junkFolderNameInContext: _ctx]])
klazz = [SOGoJunkFolder class];
else
klazz = [SOGoMailFolder class];
@ -837,6 +847,11 @@ static NSString *inboxFolderName = @"INBOX";
return [self _userFolderNameWithPurpose: @"Trash"];
}
- (NSString *) junkFolderNameInContext: (id)_ctx
{
return [self _userFolderNameWithPurpose: @"Junk"];
}
- (NSString *) otherUsersFolderNameInContext: (id)_ctx
{
return otherUsersFolderName;
@ -935,6 +950,19 @@ static NSString *inboxFolderName = @"INBOX";
return trashFolder;
}
- (SOGoJunkFolder *) junkFolderInContext: (id) _ctx
{
if (!junkFolder)
{
junkFolder
= [self folderWithTraversal: [self junkFolderNameInContext: _ctx]
andClassName: @"SOGoJunkFolder"];
[trashFolder retain];
}
return junkFolder;
}
/* account delegation */
- (NSArray *) delegates
{

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2009-2016 Inverse inc.
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.

View File

@ -77,6 +77,9 @@
toFolder: (NSString *) destinationFolder
inContext: (id) localContext;
- (WOResponse *) markMessagesAsJunkOrNotJunk: (NSArray *) uids
junk: (BOOL) isJunk;
- (NSException *) postData: (NSData *) _data flags: (id) _flags;
- (void) markForExpunge;
@ -120,6 +123,8 @@
- (id) appendMessage: (NSData *) message
usingId: (int *) imap4id;
- (NSString *) className;
@end
@interface SOGoSpecialMailFolder : SOGoMailFolder

View File

@ -35,6 +35,8 @@
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSFileManager+Extensions.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <DOM/DOMElement.h>
#import <DOM/DOMProtocols.h>
@ -42,12 +44,20 @@
#import <EOControl/EOSortOrdering.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeFileData.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <NGMail/NGMimeMessage.h>
#import <NGMail/NGMimeMessageGenerator.h>
#import <NGImap4/NGImap4Connection.h>
#import <NGImap4/NGImap4Client.h>
#import <NGImap4/NSString+Imap4.h>
#import <SOGo/DOMNode+SOGo.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/SOGoDAVAuthenticator.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/NSString+DAV.h>
@ -60,6 +70,7 @@
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
#import <SOGo/SOGoMailer.h>
#import "EOQualifier+MailDAV.h"
#import "SOGoMailObject.h"
@ -67,6 +78,8 @@
#import "SOGoMailManager.h"
#import "SOGoMailFolder.h"
#import "SOGoTrashFolder.h"
#import "SOGoMailObject+Draft.h"
#define XMLNS_INVERSEDAV @"urn:inverse:params:xml:ns:inverse-dav"
@ -783,6 +796,115 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return result;
}
- (WOResponse *) markMessagesAsJunkOrNotJunk: (NSArray *) uids
junk: (BOOL) isJunk
{
NSDictionary *junkSettings;
NSString *recipient;
NSException *error;
unsigned int limit;
junkSettings = [[[context activeUser] domainDefaults] mailJunkSettings];
error = nil;
if ([[junkSettings objectForKey: @"vendor"] caseInsensitiveCompare: @"generic"] == NSOrderedSame)
{
if (isJunk)
recipient = [junkSettings objectForKey: @"junkEmailAddress"];
else
recipient = [junkSettings objectForKey: @"notJunkEmailAddress"];
limit = [[junkSettings objectForKey: @"limit"] intValue];
// If no limit is set, we only attach one mail at the time
// to reports sent by SOGo.
if (!limit)
limit = 1;
if ([recipient length])
{
NGMimeMessage *messageToSend;
SOGoMailObject *mailObject;
NGMimeBodyPart *bodyPart;
NGMutableHashMap *map;
NGMimeFileData *fdata;
NSArray *identities;
NSData *data;
id body;
int i;
identities = [[self mailAccountFolder] identities];
for (i = 0; i < [uids count]; i++)
{
// If we are starting or reaching the limit, we create
// a new mail message
if ((i%limit) == 0)
{
map = [NGMutableHashMap hashMapWithCapacity: 5];
#warning SOPE is just plain stupid here - if you change the case of keys, it will break the encoding of fields
[map setObject: @"multipart/mixed" forKey: @"content-type"];
[map setObject: @"1.0" forKey: @"MIME-Version"];
[map setObject: [[identities objectAtIndex: 0] objectForKey: @"email"] forKey: @"from"];
[map setObject: recipient forKey: @"to"];
[map setObject: [[NSCalendarDate date] rfc822DateString] forKey: @"date"];
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
}
mailObject = [self lookupName: [uids objectAtIndex: i] inContext: context acquire: NO];
// We skip emails that might have disappeared before we were able
// to perform the action
if ([mailObject isKindOfClass: [NSException class]])
continue;
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
[map setObject: @"message/rfc822" forKey: @"content-type"];
[map setObject: @"8bit" forKey: @"content-transfer-encoding"];
[map addObject: [NSString stringWithFormat: @"attachment; filename=\"%@\"", [mailObject filenameForForward]] forKey: @"content-disposition"];
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader: map] autorelease];
data = [mailObject content];
fdata = [[NGMimeFileData alloc] initWithBytes: [data bytes] length: [data length]];
[bodyPart setBody: fdata];
RELEASE(fdata);
[body addBodyPart: bodyPart];
// We reached the limit or the end of the array
if ((i%limit) == 0 || i == [uids count]-1)
{
id <SOGoAuthenticator> authenticator;
NGMimeMessageGenerator *generator;
[messageToSend setBody: body];
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
data = [generator generateMimeFromPart: messageToSend];
authenticator = [SOGoDAVAuthenticator sharedSOGoDAVAuthenticator];
error = [[SOGoMailer mailerWithDomainDefaults: [[context activeUser] domainDefaults]]
sendMailData: data
toRecipients: [NSArray arrayWithObject: recipient]
sender: [[identities objectAtIndex: 0] objectForKey: @"email"]
withAuthenticator: authenticator
inContext: context];
if (error)
break;
}
}
}
}
return error;
}
- (NSDictionary *) statusForFlags: (NSArray *) flags
{
NGImap4Client *client;
@ -1931,6 +2053,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return response;
}
- (NSString *) className
{
return NSStringFromClass([self class]);
}
- (id) PUTAction: (WOContext *) _ctx
{
WORequest *rq;

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2007-2016 Inverse inc.
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.

View File

@ -1,14 +1,15 @@
/*
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2007-2016 Inverse inc.
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.
@ -19,8 +20,6 @@
02111-1307, USA.
*/
#import <Foundation/NSString.h>
#import "SOGoTrashFolder.h"
@implementation SOGoTrashFolder

View File

@ -67,6 +67,7 @@
SOGoSentFolderName = "Sent";
SOGoDraftsFolderName = "Drafts";
SOGoTrashFolderName = "Trash";
SOGoJunkFolderName = "Junk";
SOGoMailAutoSave = "5";

View File

@ -1,6 +1,6 @@
/* SOGoDomainDefaults.h - this file is part of SOGo
*
* Copyright (C) 2009-2015 Inverse inc.
* Copyright (C) 2009-2016 Inverse inc.
*
* 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
@ -81,6 +81,8 @@
- (BOOL) notifyOnPersonalModifications;
- (BOOL) notifyOnExternalModifications;
- (NSDictionary *) mailJunkSettings;
@end
#endif /* SOGODOMAINDEFAULTS_H */

View File

@ -361,4 +361,9 @@
return [self boolForKey: @"SOGoNotifyOnExternalModifications"];
}
- (NSDictionary *) mailJunkSettings
{
return [self objectForKey: @"SOGoMailJunkSettings"];
}
@end

View File

@ -797,6 +797,8 @@
forKey: @"Sent"];
[mailboxes setObject: [_defaults trashFolderName]
forKey: @"Trash"];
[mailboxes setObject: [_defaults junkFolderName]
forKey: @"Junk"];
[mailAccount setObject: mailboxes forKey: @"mailboxes"];
[mailboxes release];

View File

@ -108,6 +108,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setTrashFolderName: (NSString *) newValue;
- (NSString *) trashFolderName;
- (void) setJunkFolderName: (NSString *) newValue;
- (NSString *) junkFolderName;
- (void) setFirstDayOfWeek: (int) newValue;
- (int) firstDayOfWeek;

View File

@ -458,6 +458,17 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
stringByEncodingImap4FolderName];
}
- (void) setJunkFolderName: (NSString *) newValue
{
[self setObject: newValue forKey: @"SOGoJunkFolderName"];
}
- (NSString *) junkFolderName
{
return [[self stringForKey: @"SOGoJunkFolderName"]
stringByEncodingImap4FolderName];
}
- (void) setFirstDayOfWeek: (int) newValue
{
[self setInteger: newValue forKey: @"SOGoFirstDayOfWeek"];

View File

@ -140,6 +140,7 @@
"TrashFolderName" = "Trash";
"InboxFolderName" = "Inbox";
"DraftsFolderName" = "Drafts";
"JunkFolderName" = "Junk";
"SieveFolderName" = "Filters";
"Folders" = "Folders"; /* title line */
/* MailMoveToPopUp */
@ -171,6 +172,7 @@
"Sent Messages" = "Sent Messages";
"Drafts" = "Drafts";
"Deleted Messages" = "Deleted Messages";
"Junk Messages" = "Junk Messages";
/* Message list popup menu */
"Open Message In New Window" = "Open Message In New Window";
"Reply to Sender Only" = "Reply to Sender Only";
@ -186,6 +188,8 @@
"Print..." = "Print...";
"Delete Message" = "Delete Message";
"Delete Selected Messages" = "Delete Selected Messages";
"Mark the selected messages as junk" = "Mark the selected messages as junk";
"Mark the selected messages as not junk" = "Mark the selected messages as not junk";
/* Number of selected messages in list */
"selected" = "selected";
"This Folder" = "This Folder";

View File

@ -1,8 +1,6 @@
/* UIxMailFolderActions.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2016 Inverse inc.
*
* 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

View File

@ -1,6 +1,6 @@
/* UIxMailFolderActions.m - this file is part of SOGo
*
* Copyright (C) 2007-2013 Inverse inc.
* Copyright (C) 2007-2016 Inverse inc.
*
* 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
@ -544,6 +544,11 @@
return [self _setFolderPurpose: @"Trash"];
}
- (WOResponse *) setAsJunkFolderAction
{
return [self _setFolderPurpose: @"Junk"];
}
- (WOResponse *) expungeAction
{
NSException *error;
@ -778,5 +783,36 @@
return response;
}
- (WOResponse *) _markMessagesAsJunkOrNotJunk: (BOOL) isJunk
{
NSDictionary *content;
WOResponse *response;
WORequest *request;
SOGoMailFolder *co;
NSArray *uids;
request = [context request];
content = [[request contentAsString] objectFromJSONString];
uids = [NSArray arrayWithArray: [content objectForKey:@"uids"]];
co = [self clientObject];
if ([co markMessagesAsJunkOrNotJunk: uids junk: isJunk])
response = [self responseWithStatus: 500];
else
response = [self responseWith204];
return response;
}
- (WOResponse *) markMessagesAsJunkAction
{
return [self _markMessagesAsJunkOrNotJunk: YES];
}
- (WOResponse *) markMessagesAsNotJunkAction
{
return [self _markMessagesAsJunkOrNotJunk: NO];
}
@end

View File

@ -167,6 +167,21 @@
actionClass = "UIxMailFolderActions";
actionName = "setAsTrashFolder";
};
setAsJunkFolder = {
protectedBy = "View";
actionClass = "UIxMailFolderActions";
actionName = "setAsJunkFolder";
};
markMessagesAsJunk = {
protectedBy = "View";
actionClass = "UIxMailFolderActions";
actionName = "markMessagesAsJunk";
};
markMessagesAsNotJunk = {
protectedBy = "View";
actionClass = "UIxMailFolderActions";
actionName = "markMessagesAsNotJunk";
};
userRights = {
protectedBy = "ReadAcls";
pageName = "UIxMailUserRightsEditor";