From c99170b9bc7f8b89cafb17f0643a2ff39c6835f2 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Mon, 6 Jul 2020 12:17:11 -0400 Subject: [PATCH] fix(mail): pick proper "from" address when replying/forwarding Fixes #5056 --- SoObjects/Mailer/SOGoDraftObject.m | 80 ++++++++++++++++--- SoObjects/Mailer/SOGoMailAccount.h | 1 + SoObjects/Mailer/SOGoMailAccount.m | 24 ++++++ SoObjects/Mailer/SOGoMailForward.m | 49 +++++++++--- UI/MailerUI/UIxMailAccountActions.m | 61 +++++++++----- UI/MailerUI/UIxMailEditor.m | 34 +------- .../js/Mailer/Message.service.js | 2 +- 7 files changed, 173 insertions(+), 78 deletions(-) diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index e9a143eeb..fb4ebf2bf 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -50,6 +50,7 @@ #import #import +#import #import #import #import @@ -661,6 +662,53 @@ static NSString *userAgent = nil; } } +// +// +// +- (NSString *) _emailFromIdentity: (NSDictionary *) identity +{ + NSString *fullName, *format; + + fullName = [identity objectForKey: @"fullName"]; + if ([fullName length]) + format = @"%{fullName} <%{email}>"; + else + format = @"%{email}"; + + return [identity keysWithFormat: format]; +} + +- (void) _fillInFromAddress: (NSMutableDictionary *) _info + fromSentMailbox: (BOOL) _fromSentMailbox + envelope: (NGImap4Envelope *) _envelope +{ + NSDictionary *identity; + NSMutableArray *addrs; + NSString *email; + int i; + + /* Pick the first email matching one of the account's identities */ + addrs = [NSMutableArray array]; + if (_fromSentMailbox) + [self _addRecipients: [_envelope from] toArray: addrs]; + else + [self _addRecipients: [_envelope to] toArray: addrs]; + + if ([addrs count]) + { + identity = nil; + for (i = 0; !identity && i < [addrs count]; i++) + { + email = [addrs objectAtIndex: i]; + identity = [[[self container] mailAccountFolder] identityForEmail: email]; + } + if (identity) + { + [_info setObject: [self _emailFromIdentity: identity] forKey: @"from"]; + } + } +} + // // // @@ -705,11 +753,9 @@ static NSString *userAgent = nil; int i; identities = [[[self container] mailAccountFolder] identities]; - for (i = 0; i < [identities count]; i++) { email = [[identities objectAtIndex: i] objectForKey: @"email"]; - if (email) [allRecipients addObject: email]; } @@ -726,7 +772,7 @@ static NSString *userAgent = nil; else [addrs setArray: [_envelope from]]; - [self _purgeRecipients: allRecipients fromAddresses: addrs]; + [self _purgeRecipients: allRecipients fromAddresses: addrs]; // addrs contain the recipient addresses without the any of the sender's addresses [self _addEMailsOfAddresses: addrs toArray: to]; [self _addRecipients: addrs toArray: allRecipients]; [_info setObject: to forKey: @"to"]; @@ -741,6 +787,11 @@ static NSString *userAgent = nil; [self _addEMailsOfAddresses: [_envelope from] toArray: to]; } + /* Pick the first email matching one of the account's identities */ + [self _fillInFromAddress: _info + fromSentMailbox: _fromSentMailbox + envelope: _envelope]; + /* If we have no To but we have Cc recipients, let's move the Cc to the To bucket... */ if ([[_info objectForKey: @"to"] count] == 0 && [_info objectForKey: @"cc"]) @@ -954,7 +1005,6 @@ static NSString *userAgent = nil; { BOOL fromSentMailbox; NSString *msgID; - NSMutableArray *addresses; NSMutableDictionary *info; NGImap4Envelope *sourceEnvelope; SOGoUserDefaults *ud; @@ -974,11 +1024,6 @@ static NSString *userAgent = nil; if ([msgID length] > 0) [self setInReplyTo: msgID]; - addresses = [NSMutableArray array]; - [self _addEMailsOfAddresses: [sourceEnvelope to] toArray: addresses]; - if ([addresses count]) - [info setObject: [addresses objectAtIndex: 0] forKey: @"from"]; - ud = [[context activeUser] userDefaults]; [self setText: [sourceMail contentForReply]]; @@ -994,19 +1039,28 @@ static NSString *userAgent = nil; - (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail { - NSDictionary *info, *attachment; + BOOL fromSentMailbox; + NGImap4Envelope *sourceEnvelope; + NSDictionary *attachment; + NSMutableDictionary *info; NSString *signature, *nl, *space; SOGoUserDefaults *ud; + fromSentMailbox = [[sourceMail container] isKindOfClass: [SOGoSentFolder class]]; [sourceMail fetchCoreInfos]; + sourceEnvelope = [sourceMail envelope]; + info = [NSMutableDictionary dictionaryWithCapacity: 2]; if ([sourceMail subjectForForward]) { - info = [NSDictionary dictionaryWithObject: [sourceMail subjectForForward] - forKey: @"subject"]; - [self setHeaders: info]; + [info setObject: [sourceMail subjectForForward] forKey: @"subject"]; } + [self _fillInFromAddress: info + fromSentMailbox: fromSentMailbox + envelope: sourceEnvelope]; + [self setHeaders: info]; + [self setSourceURL: [sourceMail imap4URLString]]; [self setSourceFlag: @"$Forwarded"]; [self setSourceIMAP4ID: [[sourceMail nameInContainer] intValue]]; diff --git a/SoObjects/Mailer/SOGoMailAccount.h b/SoObjects/Mailer/SOGoMailAccount.h index c886ec9f9..bc250cbde 100644 --- a/SoObjects/Mailer/SOGoMailAccount.h +++ b/SoObjects/Mailer/SOGoMailAccount.h @@ -88,6 +88,7 @@ typedef enum { - (NSArray *) identities; - (NSDictionary *) defaultIdentity; +- (NSDictionary *) identityForEmail: (NSString *) email; - (NSString *) signature; - (NSString *) encryption; diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index 129de0a97..f2a5ce827 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -694,6 +694,30 @@ static NSString *inboxFolderName = @"INBOX"; return defaultIdentity; // can be nil } +- (NSDictionary *) identityForEmail: (NSString *) email +{ + NSDictionary *identity, *currentIdentity; + NSString *currentEmail; + unsigned int count, max; + + identity = nil; + [self identities]; + + max = [identities count]; + for (count = 0; count < max; count++) + { + currentIdentity = [identities objectAtIndex: count]; + currentEmail = [currentIdentity objectForKey: @"email"]; + if ([currentEmail caseInsensitiveCompare: email] == NSOrderedSame) + { + identity = currentIdentity; + break; + } + } + + return identity; // can be nil +} + - (NSString *) signature { NSDictionary *identity; diff --git a/SoObjects/Mailer/SOGoMailForward.m b/SoObjects/Mailer/SOGoMailForward.m index 4e80aa823..5353715b9 100644 --- a/SoObjects/Mailer/SOGoMailForward.m +++ b/SoObjects/Mailer/SOGoMailForward.m @@ -1,6 +1,6 @@ /* SOGoMailForward.m - this file is part of SOGo * - * Copyright (C) 2007-2017 Inverse inc. + * Copyright (C) 2007-2020 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 @@ -20,6 +20,7 @@ #import +#import #import #import @@ -30,6 +31,7 @@ #import "SOGoMailAccount.h" #import "SOGoMailObject+Draft.h" #import "SOGoMailForward.h" +#import "SOGoSentFolder.h" @implementation SOGoMailForward @@ -234,18 +236,43 @@ - (NSString *) signature { - NSString *signature, *mailSignature, *nl, *space; - - signature = [[sourceMail mailAccountFolder] signature]; + BOOL fromSentMailbox; + NGImap4EnvelopeAddress *address; + NSArray *addresses; + NSDictionary *identity; + NSString *email, *signature, *mailSignature, *nl, *space; + int count, max; - if ([signature length]) - { - nl = (htmlComposition ? @"
" : @"\n"); - space = (htmlComposition ? @" " : @" "); - mailSignature = [NSString stringWithFormat: @"--%@%@%@", space, nl, signature]; - } + fromSentMailbox = [[sourceMail container] isKindOfClass: [SOGoSentFolder class]]; + if (fromSentMailbox) + addresses = [sourceMail fromEnvelopeAddresses]; else - mailSignature = @""; + addresses = [sourceMail toEnvelopeAddresses]; + identity = nil; + mailSignature = @""; + max = [addresses count]; + + if (max) + { + // Pick the first email matching one of the account's identities + for (count = 0; !identity && count < max; count++) + { + address = [addresses objectAtIndex: count]; + email = [address baseEMail]; + identity = [[sourceMail mailAccountFolder] identityForEmail: email]; + } + } + + if (identity) + { + signature = [identity objectForKey: @"signature"]; + if ([signature length]) + { + nl = (htmlComposition ? @"
" : @"\n"); + space = (htmlComposition ? @" " : @" "); + mailSignature = [NSString stringWithFormat: @"%@--%@%@%@", nl, space, nl, signature]; + } + } return mailSignature; } diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index 22080b9da..b3337d9e0 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -50,6 +50,7 @@ #import #import #import +#import #import #import #import @@ -98,19 +99,34 @@ /* compose */ +- (NSString *) _emailFromIdentity: (NSDictionary *) identity +{ + NSString *fullName, *format; + + fullName = [identity objectForKey: @"fullName"]; + if ([fullName length]) + format = @"%{fullName} <%{email}>"; + else + format = @"%{email}"; + + return [identity keysWithFormat: format]; +} + - (WOResponse *) composeAction { + BOOL save, isHTML; + NSDictionary *data, *identity; + NSMutableDictionary *headers; + NSString *accountName, *mailboxName, *messageName; NSString *value, *signature, *nl, *space; SOGoDraftObject *newDraftMessage; - NSMutableDictionary *headers; - NSDictionary *data; - NSString *accountName, *mailboxName, *messageName; SOGoDraftsFolder *drafts; + SOGoMailAccount *co; SOGoUserDefaults *ud; id mailTo; - BOOL save, isHTML; - drafts = [[self clientObject] draftsFolderInContext: context]; + co = [self clientObject]; + drafts = [co draftsFolderInContext: context]; newDraftMessage = [drafts newDraft]; headers = [NSMutableDictionary dictionary]; @@ -134,25 +150,30 @@ save = YES; } - if (save) - [newDraftMessage setHeaders: headers]; - - signature = [[self clientObject] signature]; - if ([signature length]) + identity = [co defaultIdentity]; + if (identity) { - ud = [[context activeUser] userDefaults]; - [newDraftMessage setIsHTML: [[ud mailComposeMessageType] isEqualToString: @"html"]]; - isHTML = [newDraftMessage isHTML]; - nl = (isHTML? @"
" : @"\n"); - space = (isHTML ? @" " : @" "); - - [newDraftMessage setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]]; + [headers setObject: [self _emailFromIdentity: identity] forKey: @"from"]; + signature = [identity objectForKey: @"signature"]; + if ([signature length]) + { + ud = [[context activeUser] userDefaults]; + [newDraftMessage setIsHTML: [[ud mailComposeMessageType] isEqualToString: @"html"]]; + isHTML = [newDraftMessage isHTML]; + nl = (isHTML? @"
" : @"\n"); + space = (isHTML ? @" " : @" "); + [newDraftMessage setText: [NSString stringWithFormat: @"%@%@--%@%@%@", nl, nl, space, nl, signature]]; + } save = YES; } - if (save) - [newDraftMessage storeInfo]; - accountName = [[self clientObject] nameInContainer]; + if (save) + { + [newDraftMessage setHeaders: headers]; + [newDraftMessage storeInfo]; + } + + accountName = [co nameInContainer]; mailboxName = [drafts absoluteImap4Name]; // Ex: /INBOX/Drafts/ mailboxName = [mailboxName substringWithRange: NSMakeRange(1, [mailboxName length] -2)]; messageName = [newDraftMessage nameInContainer]; diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index a5d5d61bf..35f4a2c6b 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -65,7 +65,6 @@ NSString *sourceUID; NSString *sourceFolder; NSString *text; - NSMutableArray *fromEMails; NSString *from; SOGoMailFolder *sentFolder; BOOL isHTML; @@ -122,7 +121,6 @@ static NSArray *infoKeys = nil; [priority release]; [receipt release]; [sentFolder release]; - [fromEMails release]; [from release]; [text release]; [subject release]; @@ -256,7 +254,7 @@ static NSArray *infoKeys = nil; identities = [[[self clientObject] mailAccountFolder] identities]; if ([identities count]) { - if (from) + if ([from length]) { allIdentities = [identities objectEnumerator]; valid = NO; @@ -278,11 +276,6 @@ static NSArray *infoKeys = nil; from = nil; } } - if (!from) - { - from = [self _emailFromIdentity: [[context activeUser] defaultIdentity]]; - [from retain]; - } } return from; @@ -413,31 +406,6 @@ static NSArray *infoKeys = nil; return [UIxMailSizeFormatter sharedMailSizeFormatter]; } -/* from addresses */ - -- (NSArray *) fromEMails -{ - NSArray *identities; - int count, max; - NSString *email; - SOGoMailAccount *account; - - if (!fromEMails) - { - account = [[self clientObject] mailAccountFolder]; - identities = [account identities]; - max = [identities count]; - fromEMails = [[NSMutableArray alloc] initWithCapacity: max]; - for (count = 0; count < max; count++) - { - email = [self _emailFromIdentity: [identities objectAtIndex: count]]; - [fromEMails addObjectUniquely: email]; - } - } - - return fromEMails; -} - /* info loading */ - (void) loadInfo: (NSDictionary *) _info diff --git a/UI/WebServerResources/js/Mailer/Message.service.js b/UI/WebServerResources/js/Mailer/Message.service.js index 22704afdd..6a258592f 100644 --- a/UI/WebServerResources/js/Mailer/Message.service.js +++ b/UI/WebServerResources/js/Mailer/Message.service.js @@ -443,7 +443,7 @@ return Message.$$resource.fetch(_this.$absolutePath({asDraft: true}), 'edit').then(function(data) { // Try to match a known account identity from the specified "from" address var identity = _.find(_this.$mailbox.$account.identities, function(identity) { - return data.from.toLowerCase().indexOf(identity.email) !== -1; + return data.from && data.from.toLowerCase().indexOf(identity.email) !== -1; }); if (identity) data.from = identity.full;