2006-06-15 21:34:10 +02:00
|
|
|
/*
|
2019-08-19 16:37:15 +02:00
|
|
|
Copyright (C) 2005-2019 Inverse inc.
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2013-06-04 14:51:38 +02:00
|
|
|
This file is part of SOGo.
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2013-06-04 14:51:38 +02:00
|
|
|
SOGo is free software; you can redistribute it and/or modify it under
|
2006-06-15 21:34:10 +02:00
|
|
|
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.
|
|
|
|
|
2013-06-04 14:51:38 +02:00
|
|
|
SOGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
2006-06-15 21:34:10 +02:00
|
|
|
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
|
2014-11-27 14:58:33 +01:00
|
|
|
License along with SOGo; see the file COPYING. If not, write to the
|
2006-06-15 21:34:10 +02:00
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
#import <Foundation/NSValue.h>
|
|
|
|
|
2007-07-27 19:30:37 +02:00
|
|
|
#import <NGObjWeb/NSException+HTTP.h>
|
|
|
|
#import <NGObjWeb/WORequest.h>
|
2006-12-22 18:02:01 +01:00
|
|
|
#import <NGExtensions/NSException+misc.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
#import <NGExtensions/NGHashMap.h>
|
2007-07-27 19:30:37 +02:00
|
|
|
#import <NGExtensions/NSString+misc.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
|
|
|
|
#import <NGMime/NGMimeBodyPart.h>
|
|
|
|
#import <NGMime/NGMimeMultipartBody.h>
|
2013-06-04 14:51:38 +02:00
|
|
|
#import <NGMail/NGMailAddress.h>
|
|
|
|
#import <NGMail/NGMailAddressParser.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
#import <NGMail/NGMimeMessage.h>
|
|
|
|
#import <NGMail/NGMimeMessageGenerator.h>
|
|
|
|
|
2007-07-27 19:30:37 +02:00
|
|
|
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
#import <NGCards/NGVCard.h>
|
|
|
|
|
|
|
|
#import <Contacts/SOGoContactGCSEntry.h>
|
|
|
|
#import <Contacts/SOGoContactFolders.h>
|
2014-11-21 21:50:01 +01:00
|
|
|
#import <SOGo/NSDictionary+Utilities.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
#import <SOGo/NSString+Utilities.h>
|
2011-03-30 17:01:55 +02:00
|
|
|
#import <SOGo/SOGoBuild.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
#import <SOGo/SOGoMailer.h>
|
|
|
|
#import <SOGo/SOGoUser.h>
|
|
|
|
#import <SOGo/SOGoUserDefaults.h>
|
2017-12-23 13:31:21 +01:00
|
|
|
#import <SOGo/SOGoUserFolder.h>
|
|
|
|
#import <Mailer/SOGoMailBodyPart.h>
|
2010-09-01 22:27:45 +02:00
|
|
|
#import <Mailer/SOGoMailObject.h>
|
|
|
|
#import <Mailer/SOGoMailAccount.h>
|
|
|
|
#import <Mailer/SOGoMailFolder.h>
|
2007-07-27 19:30:37 +02:00
|
|
|
#import <MailPartViewers/UIxMailRenderingContext.h> // cyclic
|
2013-12-20 21:37:01 +01:00
|
|
|
#import <MailPartViewers/UIxMailSizeFormatter.h>
|
2015-10-31 07:10:03 +01:00
|
|
|
#import <MailPartViewers/UIxMailPartViewer.h>
|
2006-12-22 18:02:01 +01:00
|
|
|
|
2007-07-27 19:30:37 +02:00
|
|
|
#import "WOContext+UIxMailer.h"
|
2014-12-06 04:49:08 +01:00
|
|
|
#import "UIxMailFormatter.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@interface UIxMailView : UIxComponent
|
|
|
|
{
|
|
|
|
id currentAddress;
|
2014-11-21 21:50:01 +01:00
|
|
|
NSNumber *shouldAskReceipt;
|
2010-09-01 22:27:45 +02:00
|
|
|
NSString *matchingIdentityEMail;
|
2013-12-20 21:37:01 +01:00
|
|
|
NSDictionary *attachment;
|
|
|
|
NSArray *attachmentAttrs;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
- (BOOL) mailIsDraft;
|
|
|
|
- (NSNumber *) shouldAskReceipt;
|
|
|
|
- (NSString *) formattedDate;
|
2018-09-13 16:04:43 +02:00
|
|
|
- (NSString *) _matchingIdentityEMailOrDefault: (BOOL) useDefault;
|
2014-11-21 21:50:01 +01:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation UIxMailView
|
|
|
|
|
|
|
|
static NSString *mailETag = nil;
|
|
|
|
|
2008-07-17 23:12:43 +02:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
2012-12-21 19:55:03 +01:00
|
|
|
mailETag = [[NSString alloc] initWithFormat:@"\"imap4url_%@_%@_%@\"",
|
2011-03-30 16:46:35 +02:00
|
|
|
SOGO_MAJOR_VERSION,
|
|
|
|
SOGO_MINOR_VERSION,
|
|
|
|
SOGO_SUBMINOR_VERSION];
|
2015-01-22 22:25:16 +01:00
|
|
|
//NSLog (@"Note: using constant etag for mail viewer: '%@'", mailETag);
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[matchingIdentityEMail release];
|
2013-12-20 21:37:01 +01:00
|
|
|
[attachment release];
|
|
|
|
[attachmentAttrs release];
|
2010-09-01 22:27:45 +02:00
|
|
|
[super dealloc];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
2006-09-20 23:38:48 +02:00
|
|
|
- (void) setCurrentAddress: (id) _addr
|
|
|
|
{
|
|
|
|
currentAddress = _addr;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2006-09-20 23:38:48 +02:00
|
|
|
|
|
|
|
- (id) currentAddress
|
|
|
|
{
|
|
|
|
return currentAddress;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-11-05 17:37:17 +01:00
|
|
|
- (NSString *) messageSubject
|
2006-09-20 23:38:48 +02:00
|
|
|
{
|
2007-11-05 17:37:17 +01:00
|
|
|
NSString *subject;
|
|
|
|
|
|
|
|
subject = [[self clientObject] decodedSubject];
|
|
|
|
|
|
|
|
return subject;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2006-09-20 23:38:48 +02:00
|
|
|
|
|
|
|
- (NSString *) panelTitle
|
|
|
|
{
|
|
|
|
return [NSString stringWithFormat: @"%@: %@",
|
|
|
|
[self labelForKey: @"View Mail"],
|
2007-11-05 17:37:17 +01:00
|
|
|
[self messageSubject]];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2013-12-20 21:37:01 +01:00
|
|
|
- (void) setAttachment: (NSDictionary *) newAttachment
|
|
|
|
{
|
|
|
|
ASSIGN (attachment, newAttachment);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary *) attachment
|
|
|
|
{
|
|
|
|
return attachment;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* links (DUP to UIxMailPartViewer!) */
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (NSString *) linkToEnvelopeAddress: (NGImap4EnvelopeAddress *) _address
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
// TODO: make some web-link, eg open a new compose panel?
|
2007-07-30 20:40:25 +02:00
|
|
|
return [NSString stringWithFormat: @"mailto: %@", [_address baseEMail]];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (NSString *) currentAddressLink
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [self linkToEnvelopeAddress:[self currentAddress]];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fetching */
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (id) message
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [[self clientObject] fetchCoreInfos];
|
|
|
|
}
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (BOOL) hasCC
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
|
|
|
|
}
|
|
|
|
|
2011-02-24 00:06:55 +01:00
|
|
|
- (BOOL) hasBCC
|
|
|
|
{
|
|
|
|
return [[[self clientObject] bccEnvelopeAddresses] count] > 0 ? YES : NO;
|
|
|
|
}
|
|
|
|
|
2007-11-19 01:47:07 +01:00
|
|
|
- (BOOL) hasReplyTo
|
|
|
|
{
|
|
|
|
return [[[self clientObject] replyToEnvelopeAddresses] count] > 0 ? YES : NO;
|
|
|
|
}
|
|
|
|
|
2013-12-20 21:37:01 +01:00
|
|
|
/* attachment helper */
|
|
|
|
|
|
|
|
- (NSArray *) attachmentAttrs
|
|
|
|
{
|
|
|
|
if (!attachmentAttrs)
|
|
|
|
{
|
|
|
|
ASSIGN (attachmentAttrs, [[self clientObject] fetchFileAttachmentKeys]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return attachmentAttrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasAttachments
|
|
|
|
{
|
|
|
|
return [[self attachmentAttrs] count] > 0 ? YES : NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFormatter *) sizeFormatter
|
|
|
|
{
|
|
|
|
return [UIxMailSizeFormatter sharedMailSizeFormatter];
|
|
|
|
}
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
- (NSString *) formattedDate
|
|
|
|
{
|
|
|
|
NSFormatter *formatter;
|
|
|
|
|
|
|
|
formatter = [[self context] mailDateFormatter];
|
|
|
|
|
|
|
|
return [formatter stringForObjectValue: [[self clientObject] date]];
|
|
|
|
}
|
|
|
|
|
2013-12-20 21:37:01 +01:00
|
|
|
- (NSString *) attachmentsText
|
|
|
|
{
|
|
|
|
if ([[self attachmentAttrs] count] > 1)
|
|
|
|
return [self labelForKey: @"files"];
|
|
|
|
else
|
|
|
|
return [self labelForKey: @"file"];
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* viewers */
|
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
//
|
|
|
|
// TODO: I would prefer to flatten the body structure prior rendering,
|
|
|
|
// using some delegate to decide which parts to select for alternative.
|
|
|
|
//
|
2007-07-30 20:40:25 +02:00
|
|
|
- (id) contentViewerComponent
|
|
|
|
{
|
2017-12-23 13:31:21 +01:00
|
|
|
NSMutableDictionary *attachmentIds;
|
|
|
|
NSString *filename, *from;
|
|
|
|
NSDictionary *attributes;
|
2015-05-13 04:37:58 +02:00
|
|
|
id info, viewer;
|
2017-12-23 13:31:21 +01:00
|
|
|
|
|
|
|
unsigned int count, max;
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
info = [[self clientObject] bodyStructure];
|
2007-07-30 20:40:25 +02:00
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
viewer = [[context mailRenderingContext] viewerForBodyInfo: info];
|
|
|
|
[viewer setBodyInfo: info];
|
|
|
|
|
2018-01-23 16:35:46 +01:00
|
|
|
if (![[self clientObject] isEncrypted])
|
2017-01-11 22:08:05 +01:00
|
|
|
{
|
2018-01-23 16:35:46 +01:00
|
|
|
max = [[self attachmentAttrs] count];
|
|
|
|
attachmentIds = [NSMutableDictionary dictionaryWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
attributes = [[self attachmentAttrs] objectAtIndex: count];
|
|
|
|
filename = [NSString stringWithFormat: @"<%@>", [attributes objectForKey: @"filename"]];
|
|
|
|
[attachmentIds setObject: [attributes objectForKey: @"url"]
|
|
|
|
forKey: filename];
|
|
|
|
if ([[attributes objectForKey: @"bodyId"] length])
|
|
|
|
[attachmentIds setObject: [attributes objectForKey: @"url"]
|
|
|
|
forKey: [attributes objectForKey: @"bodyId"]];
|
|
|
|
}
|
|
|
|
// Attachment IDs will be decoded in UIxMailPartEncryptedViewer for
|
|
|
|
// S/MIME encrypted emails with file attachments.
|
|
|
|
[viewer setAttachmentIds: attachmentIds];
|
2017-01-11 22:08:05 +01:00
|
|
|
}
|
2018-01-23 16:35:46 +01:00
|
|
|
else
|
|
|
|
[viewer setAttachmentIds: [NSMutableDictionary dictionary]];
|
2017-01-11 22:08:05 +01:00
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
// If we are looking at a S/MIME signed mail which wasn't sent
|
|
|
|
// by our actual active user, we update the certificate of that
|
|
|
|
// sender in the user's address book
|
|
|
|
from = [[[[self clientObject] fromEnvelopeAddresses] lastObject] baseEMail];
|
|
|
|
|
|
|
|
if (![[context activeUser] hasEmail: from] &&
|
|
|
|
[[self clientObject] isSigned])
|
|
|
|
{
|
|
|
|
SOGoContactFolders *contactFolders;
|
|
|
|
NSData *p7s;
|
|
|
|
id card;
|
|
|
|
|
|
|
|
// FIXME: it might not always be part #2
|
|
|
|
p7s = [[[self clientObject] lookupImap4BodyPartKey: @"2" inContext: context] fetchBLOB];
|
|
|
|
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
|
|
|
lookupName: @"Contacts"
|
|
|
|
inContext: context
|
|
|
|
acquire: NO];
|
|
|
|
card = [contactFolders contactForEmail: from];
|
|
|
|
if ([card isKindOfClass: [SOGoContactGCSEntry class]])
|
|
|
|
{
|
|
|
|
[[card vCard] setCertificate: p7s];
|
|
|
|
[card save];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
return viewer;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* actions */
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
- (id <WOActionResults>) defaultAction
|
2007-06-01 06:11:47 +02:00
|
|
|
{
|
2015-10-31 07:10:03 +01:00
|
|
|
WOResponse *response;
|
2014-12-06 05:14:47 +01:00
|
|
|
NSMutableDictionary *data;
|
|
|
|
NSArray *addresses;
|
2014-11-21 21:50:01 +01:00
|
|
|
SOGoMailObject *co;
|
2014-12-06 04:49:08 +01:00
|
|
|
UIxEnvelopeAddressFormatter *addressFormatter;
|
2015-05-13 04:37:58 +02:00
|
|
|
UIxMailRenderingContext *mctx;
|
2014-11-21 21:50:01 +01:00
|
|
|
|
|
|
|
co = [self clientObject];
|
2014-12-06 04:49:08 +01:00
|
|
|
addressFormatter = [context mailEnvelopeAddressFormatter];
|
2007-09-11 21:38:45 +02:00
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
mctx = [[UIxMailRenderingContext alloc] initWithViewer: self context: context];
|
|
|
|
[context pushMailRenderingContext: mctx];
|
|
|
|
[mctx release];
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* check etag to see whether we really must rerender */
|
2009-11-29 05:19:32 +01:00
|
|
|
/*
|
|
|
|
Note: There is one thing which *can* change for an existing message,
|
|
|
|
those are the IMAP4 flags (and annotations, which we do not use).
|
|
|
|
Since we don't render the flags, it should be OK, if this changes
|
|
|
|
we must embed the flagging into the etag.
|
2015-12-10 00:24:09 +01:00
|
|
|
|
|
|
|
2015-12-09: We disable caching for now. Let's do this right soon
|
|
|
|
by taking into account IMAP flags and the Accepted/Declined/etc.
|
|
|
|
state of an even with an IMIP invitation. We should perhaps even
|
|
|
|
store the state as an IMAP flag.
|
2009-11-29 05:19:32 +01:00
|
|
|
*/
|
2017-12-23 13:31:21 +01:00
|
|
|
//s = [[context request] headerForKey: @"if-none-match"];
|
2015-12-10 00:24:09 +01:00
|
|
|
//if (s)
|
2017-12-23 13:31:21 +01:00
|
|
|
// if (0)
|
|
|
|
// {
|
|
|
|
// if ([s rangeOfString:mailETag].length > 0) /* not perfectly correct */
|
|
|
|
// {
|
|
|
|
// /* client already has the proper entity */
|
|
|
|
// // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
// if (![co doesMailExist])
|
|
|
|
// {
|
|
|
|
// data = [NSDictionary dictionaryWithObject: [self labelForKey: @"Message got deleted"]
|
|
|
|
// forKey: @"message"];
|
|
|
|
// return [self responseWithStatus: 404 /* Not Found */
|
|
|
|
// andJSONRepresentation: data];
|
|
|
|
// }
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
// response = [self responseWithStatus: 304];
|
2009-11-29 05:19:32 +01:00
|
|
|
|
2017-12-23 13:31:21 +01:00
|
|
|
// return response;
|
|
|
|
// }
|
|
|
|
// }
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-09-11 21:38:45 +02:00
|
|
|
if (![self message]) // TODO: redirect to proper error
|
2014-11-21 21:50:01 +01:00
|
|
|
{
|
2015-12-22 04:17:40 +01:00
|
|
|
data = [NSDictionary dictionaryWithObject: [self labelForKey: @"Did not find specified message"]
|
|
|
|
forKey: @"message"];
|
2014-11-21 21:50:01 +01:00
|
|
|
return [self responseWithStatus: 404 /* Not Found */
|
2018-01-23 16:35:46 +01:00
|
|
|
andJSONRepresentation: data];
|
2014-11-21 21:50:01 +01:00
|
|
|
}
|
|
|
|
|
2014-12-06 05:14:47 +01:00
|
|
|
data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
2014-11-21 21:50:01 +01:00
|
|
|
[self attachmentAttrs], @"attachmentAttrs",
|
|
|
|
[self shouldAskReceipt], @"shouldAskReceipt",
|
|
|
|
[NSNumber numberWithBool: [self mailIsDraft]], @"isDraft",
|
2015-05-13 04:37:58 +02:00
|
|
|
[[self contentViewerComponent] renderedPart], @"parts",
|
2014-11-21 21:50:01 +01:00
|
|
|
nil];
|
2016-02-04 17:36:26 +01:00
|
|
|
if ([self formattedDate])
|
|
|
|
[data setObject: [self formattedDate] forKey: @"date"];
|
2014-12-11 17:24:22 +01:00
|
|
|
if ([self messageSubject])
|
|
|
|
[data setObject: [self messageSubject] forKey: @"subject"];
|
2014-12-06 05:14:47 +01:00
|
|
|
if ((addresses = [addressFormatter dictionariesForArray: [co fromEnvelopeAddresses]]))
|
|
|
|
[data setObject: addresses forKey: @"from"];
|
|
|
|
if ((addresses = [addressFormatter dictionariesForArray: [co toEnvelopeAddresses]]))
|
|
|
|
[data setObject: addresses forKey: @"to"];
|
|
|
|
if ((addresses = [addressFormatter dictionariesForArray: [co ccEnvelopeAddresses]]))
|
|
|
|
[data setObject: addresses forKey: @"cc"];
|
|
|
|
if ((addresses = [addressFormatter dictionariesForArray: [co bccEnvelopeAddresses]]))
|
|
|
|
[data setObject: addresses forKey: @"bcc"];
|
|
|
|
if ((addresses = [addressFormatter dictionariesForArray: [co replyToEnvelopeAddresses]]))
|
|
|
|
[data setObject: addresses forKey: @"reply-to"];
|
|
|
|
|
2016-09-22 22:08:59 +02:00
|
|
|
// Mark message as read
|
|
|
|
[co addFlags: @"seen"];
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
response = [self responseWithStatus: 200
|
2015-12-22 04:17:40 +01:00
|
|
|
andJSONRepresentation: data];
|
2010-09-01 22:27:45 +02:00
|
|
|
|
2015-05-13 04:37:58 +02:00
|
|
|
[response setHeader: mailETag forKey: @"etag"];
|
|
|
|
|
|
|
|
[[context popMailRenderingContext] reset];
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
return response;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2016-12-07 20:37:59 +01:00
|
|
|
- (id <WOActionResults>) archiveAttachmentsAction
|
|
|
|
{
|
|
|
|
NSString *name;
|
|
|
|
SOGoMailObject *co;
|
|
|
|
|
|
|
|
co = [self clientObject];
|
|
|
|
name = [NSString stringWithFormat: @"%@-%@.zip",
|
|
|
|
[self labelForKey: @"attachments"], [co nameInContainer]];
|
|
|
|
|
|
|
|
return [co archiveAllFilesinArchiveNamed: name];
|
|
|
|
}
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
/* MDN */
|
|
|
|
|
|
|
|
- (BOOL) _userHasEMail: (NSString *) email
|
|
|
|
{
|
|
|
|
NSArray *identities;
|
|
|
|
NSString *identityEmail;
|
|
|
|
SOGoMailAccount *account;
|
|
|
|
int count, max;
|
|
|
|
BOOL rc;
|
|
|
|
|
2011-04-05 17:09:35 +02:00
|
|
|
rc = NO;
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
account = [[self clientObject] mailAccountFolder];
|
|
|
|
identities = [account identities];
|
|
|
|
max = [identities count];
|
|
|
|
for (count = 0; !rc && count < max; count++)
|
|
|
|
{
|
|
|
|
identityEmail = [[identities objectAtIndex: count]
|
|
|
|
objectForKey: @"email"];
|
2013-06-04 14:51:38 +02:00
|
|
|
rc = ([identityEmail caseInsensitiveCompare: email] == NSOrderedSame);
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) _messageHasDraftOrMDNSentFlag
|
|
|
|
{
|
|
|
|
NSArray *flags;
|
|
|
|
NSDictionary *coreInfos;
|
|
|
|
|
|
|
|
coreInfos = [[self clientObject] fetchCoreInfos];
|
|
|
|
flags = [coreInfos objectForKey: @"flags"];
|
|
|
|
|
|
|
|
return ([flags containsObject: @"draft"]
|
|
|
|
|| [flags containsObject: @"$mdnsent"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _matchingIdentityEMail
|
2018-09-13 16:04:43 +02:00
|
|
|
{
|
|
|
|
return [self _matchingIdentityEMailOrDefault: YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _matchingIdentityEMailOrDefault: (BOOL) useDefault
|
2010-09-01 22:27:45 +02:00
|
|
|
{
|
|
|
|
NSMutableArray *recipients;
|
|
|
|
NSArray *headerRecipients;
|
2018-09-13 16:04:43 +02:00
|
|
|
NSString *currentEMail, *email;
|
2010-09-01 22:27:45 +02:00
|
|
|
NGImap4EnvelopeAddress *address;
|
|
|
|
NSInteger count, max;
|
|
|
|
SOGoMailObject *co;
|
|
|
|
|
2018-09-13 16:04:43 +02:00
|
|
|
email = nil;
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
if (!matchingIdentityEMail)
|
|
|
|
{
|
|
|
|
recipients = [NSMutableArray array];
|
|
|
|
co = [self clientObject];
|
|
|
|
headerRecipients = [co toEnvelopeAddresses];
|
|
|
|
if ([headerRecipients count])
|
|
|
|
[recipients addObjectsFromArray: headerRecipients];
|
|
|
|
headerRecipients = [co ccEnvelopeAddresses];
|
|
|
|
if ([headerRecipients count])
|
|
|
|
[recipients addObjectsFromArray: headerRecipients];
|
|
|
|
|
|
|
|
max = [recipients count];
|
|
|
|
for (count = 0; !matchingIdentityEMail && count < max; count++)
|
|
|
|
{
|
|
|
|
address = [recipients objectAtIndex: count];
|
|
|
|
currentEMail = [NSString stringWithFormat: @"%@@%@",
|
|
|
|
[address mailbox],
|
|
|
|
[address host]];
|
|
|
|
if ([self _userHasEMail: currentEMail])
|
|
|
|
{
|
|
|
|
matchingIdentityEMail = currentEMail;
|
|
|
|
[matchingIdentityEMail retain];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-13 16:04:43 +02:00
|
|
|
if (matchingIdentityEMail)
|
|
|
|
{
|
|
|
|
email = matchingIdentityEMail;
|
|
|
|
}
|
|
|
|
else if (useDefault)
|
|
|
|
{
|
|
|
|
// This can happen if we receive the message because we are
|
|
|
|
// in the list of bcc. In this case, we take the first
|
|
|
|
// identity associated with the account.
|
|
|
|
email = [[[[[self clientObject] mailAccountFolder] identities] objectAtIndex: 0] objectForKey: @"email"];
|
|
|
|
}
|
|
|
|
|
|
|
|
return email;
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _domainFromEMail: (NSString *) email
|
|
|
|
{
|
|
|
|
NSString *domain;
|
|
|
|
NSRange separator;
|
|
|
|
|
|
|
|
separator = [email rangeOfString: @"@"];
|
|
|
|
if (separator.location != NSNotFound)
|
|
|
|
domain = [email substringFromIndex: NSMaxRange (separator)];
|
|
|
|
else
|
|
|
|
domain = nil;
|
|
|
|
|
|
|
|
return domain;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) _userEMailDomains
|
|
|
|
{
|
|
|
|
NSMutableArray *domains;
|
|
|
|
NSArray *identities;
|
|
|
|
NSString *email, *domain;
|
|
|
|
SOGoMailAccount *account;
|
|
|
|
NSInteger count, max;
|
|
|
|
|
|
|
|
account = [[self clientObject] mailAccountFolder];
|
|
|
|
identities = [account identities];
|
|
|
|
max = [identities count];
|
|
|
|
domains = [NSMutableArray arrayWithCapacity: max];
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
email = [[identities objectAtIndex: count]
|
|
|
|
objectForKey: @"email"];
|
|
|
|
domain = [self _domainFromEMail: email];
|
|
|
|
if (domain)
|
|
|
|
[domains addObject: domain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return domains;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) _senderIsInUserDomain: (NSDictionary *) headers
|
|
|
|
{
|
|
|
|
NSString *sender, *senderDomain;
|
|
|
|
BOOL rc;
|
|
|
|
|
|
|
|
sender = [[headers objectForKey: @"from"] pureEMailAddress];
|
|
|
|
senderDomain = [self _domainFromEMail: sender];
|
|
|
|
if (senderDomain)
|
|
|
|
rc = [[self _userEMailDomains] containsObject: senderDomain];
|
|
|
|
else
|
|
|
|
rc = NO;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) _receiptAction
|
|
|
|
{
|
|
|
|
SOGoUserDefaults *ud;
|
|
|
|
NSString *action;
|
|
|
|
NSDictionary *headers;
|
|
|
|
|
|
|
|
headers = [[self clientObject] mailHeaders];
|
|
|
|
|
|
|
|
ud = [[context activeUser] userDefaults];
|
|
|
|
if ([ud allowUserReceipt])
|
|
|
|
{
|
2018-09-13 16:04:43 +02:00
|
|
|
if ([self _matchingIdentityEMailOrDefault: NO])
|
2010-09-01 22:27:45 +02:00
|
|
|
{
|
|
|
|
if ([self _senderIsInUserDomain: headers])
|
|
|
|
action = [ud userReceiptAnyAction];
|
|
|
|
else
|
|
|
|
action = [ud userReceiptOutsideDomainAction];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
action = [ud userReceiptNonRecipientAction];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
action = @"ignore";
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _flagMessageWithMDNSent
|
|
|
|
{
|
|
|
|
[[self clientObject] addFlags: @"$MDNSent"];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _appendReceiptTextToBody: (NGMimeMultipartBody *) body
|
|
|
|
{
|
|
|
|
NGMutableHashMap *map;
|
|
|
|
NGMimeBodyPart *bodyPart;
|
|
|
|
NSString *textPartFormat, *textPartMessage;
|
|
|
|
|
|
|
|
map = [[NGMutableHashMap alloc] initWithCapacity: 1];
|
|
|
|
[map setObject: @"text/plain; charset=utf-8; format=flowed"
|
|
|
|
forKey: @"content-type"];
|
|
|
|
|
|
|
|
bodyPart = [[NGMimeBodyPart alloc] initWithHeader: map];
|
|
|
|
[map release];
|
|
|
|
|
|
|
|
textPartFormat = [self labelForKey: @"This is a Return Receipt for the mail"
|
|
|
|
@" that you sent to %@.\n\nNote: This Return Receipt"
|
|
|
|
@" only acknowledges that the message was displayed"
|
|
|
|
@" on the recipient's computer. There is no"
|
|
|
|
@" guarantee that the recipient has read or"
|
|
|
|
@" understood the message contents."];
|
|
|
|
textPartMessage = [NSString stringWithFormat: textPartFormat,
|
|
|
|
[self _matchingIdentityEMail]];
|
|
|
|
[bodyPart setBody: [textPartMessage
|
|
|
|
dataUsingEncoding: NSUTF8StringEncoding]];
|
|
|
|
[body addBodyPart: bodyPart];
|
|
|
|
[bodyPart release];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _appendMDNToBody: (NGMimeMultipartBody *) body
|
|
|
|
{
|
|
|
|
NGMutableHashMap *map;
|
|
|
|
NGMimeBodyPart *bodyPart;
|
|
|
|
NSString *messageId;
|
|
|
|
NSMutableString *mdnPartMessage;
|
|
|
|
|
|
|
|
map = [[NGMutableHashMap alloc] initWithCapacity: 3];
|
|
|
|
[map addObject: @"message/disposition-notification; name=\"MDNPart2.txt\""
|
|
|
|
forKey: @"content-type"];
|
|
|
|
[map addObject: @"inline" forKey: @"content-disposition"];
|
|
|
|
[map addObject: @"7bit" forKey: @"content-transfer-encoding"];
|
|
|
|
|
|
|
|
bodyPart = [[NGMimeBodyPart alloc] initWithHeader: map];
|
|
|
|
[map release];
|
|
|
|
|
|
|
|
mdnPartMessage = [[NSMutableString alloc] initWithCapacity: 100];
|
2011-03-30 17:01:55 +02:00
|
|
|
[mdnPartMessage appendFormat: @"Reporting-UA: SOGoMail %@\n", SOGoVersion];
|
2010-09-01 22:27:45 +02:00
|
|
|
[mdnPartMessage appendFormat: @"Final-Recipient: rfc822;%@\n",
|
|
|
|
[self _matchingIdentityEMail]];
|
|
|
|
messageId = [[self clientObject] messageId];
|
|
|
|
[mdnPartMessage appendFormat: @"Original-Message-ID: %@\n",
|
|
|
|
messageId];
|
|
|
|
[mdnPartMessage appendString: @"Disposition:"
|
|
|
|
@" manual-action/MDN-sent-manually; displayed"];
|
|
|
|
[bodyPart setBody: [mdnPartMessage
|
|
|
|
dataUsingEncoding: NSASCIIStringEncoding]];
|
|
|
|
[mdnPartMessage release];
|
|
|
|
[body addBodyPart: bodyPart];
|
|
|
|
[bodyPart release];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _appendHeadersToBody: (NGMimeMultipartBody *) body
|
|
|
|
{
|
|
|
|
NGMutableHashMap *map;
|
|
|
|
NGMimeBodyPart *bodyPart;
|
|
|
|
NSDictionary *coreInfos;
|
|
|
|
|
|
|
|
map = [[NGMutableHashMap alloc] initWithCapacity: 3];
|
|
|
|
[map addObject: @"text/rfc822-headers; name=\"MDNPart3.txt\""
|
|
|
|
forKey: @"content-type"];
|
|
|
|
[map addObject: @"inline" forKey: @"content-disposition"];
|
|
|
|
[map addObject: @"7bit" forKey: @"content-transfer-encoding"];
|
|
|
|
|
|
|
|
bodyPart = [[NGMimeBodyPart alloc] initWithHeader: map];
|
|
|
|
[map release];
|
|
|
|
|
|
|
|
coreInfos = [[self clientObject] fetchCoreInfos];
|
|
|
|
[bodyPart setBody: [coreInfos objectForKey: @"header"]];
|
|
|
|
[body addBodyPart: bodyPart];
|
|
|
|
[bodyPart release];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NGHashMap *) _receiptMessageHeaderTo: (NSString *) email
|
|
|
|
{
|
2014-10-29 21:42:52 +01:00
|
|
|
NSString *subject, *from;
|
2010-09-01 22:27:45 +02:00
|
|
|
NGMutableHashMap *map;
|
|
|
|
|
|
|
|
map = [[NGMutableHashMap alloc] initWithCapacity: 1];
|
|
|
|
[map autorelease];
|
|
|
|
[map setObject: email forKey: @"to"];
|
2014-10-29 21:42:52 +01:00
|
|
|
|
|
|
|
from = [self _matchingIdentityEMail];
|
|
|
|
|
|
|
|
if (from)
|
|
|
|
[map setObject: from forKey: @"from"];
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
[map setObject: @"multipart/report; report-type=disposition-notification"
|
|
|
|
forKey: @"content-type"];
|
|
|
|
subject = [NSString stringWithFormat:
|
|
|
|
[self labelForKey: @"Return Receipt (displayed) - %@"],
|
|
|
|
[self messageSubject]];
|
2010-09-02 18:39:31 +02:00
|
|
|
[map setObject: [subject asQPSubjectString: @"utf-8"]
|
|
|
|
forKey: @"subject"];
|
2010-09-01 22:27:45 +02:00
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _sendEMailReceiptTo: (NSString *) email
|
2007-07-30 20:40:25 +02:00
|
|
|
{
|
2010-09-01 22:27:45 +02:00
|
|
|
NGMimeMultipartBody *body;
|
|
|
|
NGMimeMessage *message;
|
|
|
|
NGMimeMessageGenerator *generator;
|
|
|
|
SOGoDomainDefaults *dd;
|
|
|
|
|
|
|
|
message = [NGMimeMessage
|
|
|
|
messageWithHeader: [self _receiptMessageHeaderTo: email]];
|
|
|
|
body = [[NGMimeMultipartBody alloc] initWithPart: message];
|
|
|
|
[self _appendReceiptTextToBody: body];
|
|
|
|
[self _appendMDNToBody: body];
|
|
|
|
[self _appendHeadersToBody: body];
|
|
|
|
[message setBody: body];
|
|
|
|
[body release];
|
|
|
|
|
|
|
|
dd = [[context activeUser] domainDefaults];
|
|
|
|
|
|
|
|
generator = [NGMimeMessageGenerator new];
|
|
|
|
[generator autorelease];
|
|
|
|
|
|
|
|
if (![[SOGoMailer mailerWithDomainDefaults: dd]
|
2012-10-16 22:56:48 +02:00
|
|
|
sendMailData: [generator generateMimeFromPart: message]
|
|
|
|
toRecipients: [NSArray arrayWithObject: email]
|
|
|
|
sender: [self _matchingIdentityEMail]
|
|
|
|
withAuthenticator: [self authenticatorInContext: context]
|
|
|
|
inContext: context])
|
2010-09-01 22:27:45 +02:00
|
|
|
[self _flagMessageWithMDNSent];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
2014-11-21 21:50:01 +01:00
|
|
|
- (NSNumber *) shouldAskReceipt
|
2010-09-01 22:27:45 +02:00
|
|
|
{
|
2013-06-04 14:51:38 +02:00
|
|
|
NGMailAddress *mailAddress;
|
2010-09-01 22:27:45 +02:00
|
|
|
NSDictionary *mailHeaders;
|
|
|
|
NSString *email, *action;
|
|
|
|
|
|
|
|
if (!shouldAskReceipt)
|
|
|
|
{
|
2014-11-21 21:50:01 +01:00
|
|
|
shouldAskReceipt = [NSNumber numberWithBool: NO];
|
2010-09-01 22:27:45 +02:00
|
|
|
mailHeaders = [[self clientObject] mailHeaders];
|
|
|
|
email = [mailHeaders objectForKey: @"disposition-notification-to"];
|
|
|
|
if (!email)
|
|
|
|
{
|
|
|
|
email = [mailHeaders objectForKey: @"x-confirm-reading-to"];
|
|
|
|
if (!email)
|
|
|
|
email = [mailHeaders objectForKey: @"return-receipt-to"];
|
|
|
|
}
|
|
|
|
|
2013-06-04 14:51:38 +02:00
|
|
|
// email here can be "foo@bar.com" or "Foo Bar <foo@bar.com>"
|
|
|
|
// we must extract the actual email address
|
|
|
|
mailAddress = [[NGMailAddressParser mailAddressParserWithString: email] parse];
|
|
|
|
|
|
|
|
if ([mailAddress isKindOfClass: [NGMailAddress class]])
|
|
|
|
email = [mailAddress address];
|
|
|
|
else
|
|
|
|
email = nil;
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
if (email)
|
|
|
|
{
|
|
|
|
if (![self _userHasEMail: email]
|
|
|
|
&& ![self _messageHasDraftOrMDNSentFlag])
|
|
|
|
{
|
|
|
|
action = [self _receiptAction];
|
|
|
|
if ([action isEqualToString: @"ask"])
|
|
|
|
{
|
2014-11-21 21:50:01 +01:00
|
|
|
shouldAskReceipt = [NSNumber numberWithBool: YES];
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
|
|
|
else if ([action isEqualToString: @"send"])
|
|
|
|
[self _sendEMailReceiptTo: email];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return shouldAskReceipt;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (WOResponse *) sendMDNAction
|
|
|
|
{
|
|
|
|
WOResponse *response;
|
2015-12-22 04:17:40 +01:00
|
|
|
NSDictionary *mailHeaders, *jsonResponse;
|
2010-09-01 22:27:45 +02:00
|
|
|
NSString *email, *action;
|
|
|
|
|
|
|
|
mailHeaders = [[self clientObject] mailHeaders];
|
|
|
|
email = [mailHeaders objectForKey: @"disposition-notification-to"];
|
|
|
|
if (!email)
|
|
|
|
{
|
|
|
|
email = [mailHeaders objectForKey: @"x-confirm-reading-to"];
|
|
|
|
if (!email)
|
|
|
|
email = [mailHeaders objectForKey: @"return-receipt-to"];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We perform most of the validation steps that were done in
|
|
|
|
-shouldAskReceipt in order to enforce consistency. */
|
|
|
|
if (email)
|
|
|
|
{
|
|
|
|
if ([self _userHasEMail: email])
|
2015-12-22 04:17:40 +01:00
|
|
|
{
|
|
|
|
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"One cannot send an MDN to oneself."]
|
|
|
|
forKey: @"message"];
|
|
|
|
response = [self responseWithStatus: 403 andJSONRepresentation: jsonResponse];
|
|
|
|
}
|
2010-09-01 22:27:45 +02:00
|
|
|
else
|
|
|
|
{
|
2010-09-01 22:36:05 +02:00
|
|
|
action = [self _receiptAction];
|
|
|
|
if ([action isEqualToString: @"ask"])
|
2010-09-01 22:27:45 +02:00
|
|
|
{
|
2010-09-01 22:36:05 +02:00
|
|
|
[self _sendEMailReceiptTo: email];
|
|
|
|
response = [self responseWithStatus: 204];
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
2010-09-01 22:36:05 +02:00
|
|
|
else
|
2015-12-22 04:17:40 +01:00
|
|
|
{
|
|
|
|
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"No notification header found in original message."]
|
|
|
|
forKey: @"message"];
|
|
|
|
response = [self responseWithStatus: 403 andJSONRepresentation: jsonResponse];
|
|
|
|
}
|
2010-09-01 22:27:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2015-12-22 04:17:40 +01:00
|
|
|
{
|
|
|
|
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"No notification header found in original message."]
|
|
|
|
forKey: @"message"];
|
|
|
|
response = [self responseWithStatus: 403 andJSONRepresentation: jsonResponse];
|
|
|
|
}
|
|
|
|
|
2010-09-01 22:27:45 +02:00
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* /MDN */
|
|
|
|
|
2007-08-15 22:34:11 +02:00
|
|
|
- (BOOL) mailIsDraft
|
|
|
|
{
|
|
|
|
return [[self clientObject] isInDraftsFolder];
|
|
|
|
}
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (id) redirectToParentFolder
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
id url;
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
url = [[[self clientObject] container] baseURLInContext: context];
|
|
|
|
|
|
|
|
return [self redirectToLocation: url];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* generating response */
|
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
- (void) appendToResponse: (WOResponse *) _response
|
|
|
|
inContext: (WOContext *) _ctx
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
UIxMailRenderingContext *mctx;
|
2008-09-30 21:34:31 +02:00
|
|
|
|
2009-11-29 05:19:32 +01:00
|
|
|
[[_ctx response] setHeader:mailETag forKey:@"etag"];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2007-07-30 20:40:25 +02:00
|
|
|
mctx = [[UIxMailRenderingContext alloc] initWithViewer: self
|
|
|
|
context: _ctx];
|
|
|
|
[_ctx pushMailRenderingContext: mctx];
|
2006-06-15 21:34:10 +02:00
|
|
|
[mctx release];
|
2007-07-30 20:40:25 +02:00
|
|
|
|
|
|
|
[super appendToResponse: _response inContext: _ctx];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
[[_ctx popMailRenderingContext] reset];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* UIxMailView */
|