diff --git a/Source/ComposeBackEnd+GPGMail.m b/Source/ComposeBackEnd+GPGMail.m index 3da68942..06ae81c8 100644 --- a/Source/ComposeBackEnd+GPGMail.m +++ b/Source/ComposeBackEnd+GPGMail.m @@ -1025,7 +1025,8 @@ - (void)MAUpdateSMIMEStatus:(CDUnknownBlockType)onComplete { // If there are already preferredSecurityProperties determined, copy the userShouldSignMessage and // userShouldEncryptMessage on to the new security properties. GMComposeMessagePreferredSecurityProperties *currentSecurityProperties = self.preferredSecurityProperties; - GMComposeMessagePreferredSecurityProperties *preferredSecurityProperties = [[GMComposeMessagePreferredSecurityProperties alloc] initWithSender:sender recipients:recipients userShouldSignMessage:currentSecurityProperties ? currentSecurityProperties.userShouldSignMessage : ThreeStateBooleanUndetermined userShouldEncryptMessage:currentSecurityProperties ? currentSecurityProperties.userShouldEncryptMessage : ThreeStateBooleanUndetermined]; + GPGKey *signingKey = [currentSecurityProperties.signingSender isEqualToString:sender] ? currentSecurityProperties.signingKey : nil; + GMComposeMessagePreferredSecurityProperties *preferredSecurityProperties = [[GMComposeMessagePreferredSecurityProperties alloc] initWithSender:sender signingKey:signingKey recipients:recipients userShouldSignMessage:currentSecurityProperties ? currentSecurityProperties.userShouldSignMessage : ThreeStateBooleanUndetermined userShouldEncryptMessage:currentSecurityProperties ? currentSecurityProperties.userShouldEncryptMessage : ThreeStateBooleanUndetermined]; preferredSecurityProperties.cachedSigningIdentities = [self valueForKey:@"_signingIdentities"]; preferredSecurityProperties.cachedEncryptionCertificates = [self valueForKey:@"_encryptionCertificates"]; diff --git a/Source/GMComposeMessagePreferredSecurityProperties.h b/Source/GMComposeMessagePreferredSecurityProperties.h index f846c089..6aaeb645 100644 --- a/Source/GMComposeMessagePreferredSecurityProperties.h +++ b/Source/GMComposeMessagePreferredSecurityProperties.h @@ -34,17 +34,22 @@ typedef enum { ThreeStateBoolean _userShouldSignMessage; ThreeStateBoolean _userShouldEncryptMessage; + + GPGKey *_signingKey; + NSString *_signingSender; } + (GPGMAIL_SECURITY_METHOD)defaultSecurityMethod; - (id)initWithSender:(NSString *)sender recipients:(NSArray *)recipients; -- (id)initWithSender:(NSString *)sender recipients:(NSArray *)recipients userShouldSignMessage:(ThreeStateBoolean)userShouldSignMessage userShouldEncryptMessage:(ThreeStateBoolean)userShouldEncryptMessage; +- (id)initWithSender:(NSString *)sender signingKey:(GPGKey *)signingKey recipients:(NSArray *)recipients userShouldSignMessage:(ThreeStateBoolean)userShouldSignMessage userShouldEncryptMessage:(ThreeStateBoolean)userShouldEncryptMessage; - (void)addHintsFromBackEnd:(ComposeBackEnd *)backEnd; - (void)computePreferredSecurityPropertiesForSecurityMethod:(GPGMAIL_SECURITY_METHOD)securityMethod; +- (void)updateSigningKey:(GPGKey *)signingKey forSender:(NSString *)sender; + - (GPGKey *)encryptionKeyForDraft; @property (nonatomic, readonly, assign) BOOL canPGPSign; @@ -81,6 +86,9 @@ typedef enum { @property (nonatomic, copy) NSString *sender; @property (nonatomic, copy) NSArray *recipients; +@property (nonatomic, readonly, retain) GPGKey *signingKey; +@property (nonatomic, readonly, retain) NSString *signingSender; + @property (nonatomic, copy) NSDictionary *cachedSigningIdentities; @property (nonatomic, copy) NSDictionary *cachedEncryptionCertificates; diff --git a/Source/GMComposeMessagePreferredSecurityProperties.m b/Source/GMComposeMessagePreferredSecurityProperties.m index 3c578cde..f2af1b8f 100644 --- a/Source/GMComposeMessagePreferredSecurityProperties.m +++ b/Source/GMComposeMessagePreferredSecurityProperties.m @@ -57,10 +57,12 @@ - (id)initWithSender:(NSString *)sender recipients:(NSArray *)recipients { return self; } -- (id)initWithSender:(NSString *)sender recipients:(NSArray *)recipients userShouldSignMessage:(ThreeStateBoolean)userShouldSign userShouldEncryptMessage:(ThreeStateBoolean)userShouldEncrypt { +- (id)initWithSender:(NSString *)sender signingKey:(GPGKey *)signingKey recipients:(NSArray *)recipients userShouldSignMessage:(ThreeStateBoolean)userShouldSign userShouldEncryptMessage:(ThreeStateBoolean)userShouldEncrypt { if((self = [self initWithSender:sender recipients:recipients])) { _userShouldSignMessage = userShouldSign; _userShouldEncryptMessage = userShouldEncrypt; + _signingKey = [signingKey copy]; + _signingSender = [sender copy]; } return self; @@ -125,6 +127,13 @@ - (GPGKey *)encryptionKeyForDraft { return [[GPGMailBundle sharedInstance] anyPersonalPublicKeyWithPreferenceAddress:senderAddress]; } +- (void)updateSigningKey:(GPGKey *)signingKey forSender:(NSString *)sender { + // Once a signing key is set, the keyring is no longer queried for a key matching + // the signer address, but the set signing key is always used. (#895) + _signingKey = [signingKey copy]; + _signingSender = [sender copy]; +} + - (void)computePreferredSecurityPropertiesForSecurityMethod:(GPGMAIL_SECURITY_METHOD)securityMethod { // Load signing identity and certificates for S/MIME. NSMutableDictionary *signingIdentities = [self.cachedSigningIdentities mutableCopy]; @@ -187,14 +196,23 @@ - (void)computePreferredSecurityPropertiesForSecurityMethod:(GPGMAIL_SECURITY_ME signingKey = nil; } if(!signingKey) { - NSArray *signingKeyList = [[[GPGMailBundle sharedInstance] signingKeyListForAddress:senderAddress] allObjects]; - // TODO: Consider pereferring the default key if one is configured. - signingKeys[senderAddress] = [signingKeyList count] > 0 ? signingKeyList[0] : [NSNull null]; - // In order to trick mail into accepting the key, it has to be added under the sender - // with email and full name as well. Otherwise, the last check before calling - // -[ComposeBackEnd _makeMessageWithContents:isDraft:shouldSign:shouldEncrypt:shouldSkipSignature:shouldBePlainText:] - // will disable signing, since no key for the full address can be found in the _signingIdentities dictionary. - signingKeys[sender] = signingKeys[senderAddress]; + // If a particular signing key has been chosen from the "From" field, + // always use that key. Otherwise query all signing keys matching the address. + // (#895) + if(_signingKey && [_signingSender isEqualToString:sender]) { + signingKeys[senderAddress] = _signingKey; + signingKeys[sender] = signingKeys[senderAddress]; + } + else { + NSArray *signingKeyList = [[[GPGMailBundle sharedInstance] signingKeyListForAddress:senderAddress] allObjects]; + // TODO: Consider pereferring the default key if one is configured. + signingKeys[senderAddress] = [signingKeyList count] > 0 ? signingKeyList[0] : [NSNull null]; + // In order to trick mail into accepting the key, it has to be added under the sender + // with email and full name as well. Otherwise, the last check before calling + // -[ComposeBackEnd _makeMessageWithContents:isDraft:shouldSign:shouldEncrypt:shouldSkipSignature:shouldBePlainText:] + // will disable signing, since no key for the full address can be found in the _signingIdentities dictionary. + signingKeys[sender] = signingKeys[senderAddress]; + } } canPGPSign = signingKeys[senderAddress] && signingKeys[senderAddress] != [NSNull null] ? YES : NO; } diff --git a/Source/HeadersEditor+GPGMail.m b/Source/HeadersEditor+GPGMail.m index ae9adbb0..db2f44a9 100644 --- a/Source/HeadersEditor+GPGMail.m +++ b/Source/HeadersEditor+GPGMail.m @@ -704,9 +704,16 @@ - (void)MAChangeFromHeader:(NSPopUpButton *)sender { else [button addItemWithTitle:(parentItem ? parentItem : item).title]; - // Set the selected key in the back-end. - ComposeBackEnd *backEnd = [GPGMailBundle backEndFromObject:self]; - [backEnd setIvar:@"gpgKeyForSigning" value:[item getIvar:kHeadersEditorFromControlGPGKeyKey]]; + ComposeBackEnd *backEnd = [GPGMailBundle backEndFromObject:self]; + GMComposeMessagePreferredSecurityProperties *securityProperties = [(ComposeBackEnd_GPGMail *)backEnd preferredSecurityProperties]; + GPGKey *signingKey = [item getIvar:kHeadersEditorFromControlGPGKeyKey]; + if(signingKey) { + // Configure the security properties to always used the specified key for signing, + // instead of looking up a matching key using the signers email address. + // This is especially necessary in case multiple signing keys are available for the + // same email address. (#895) + [securityProperties updateSigningKey:signingKey forSender:item.representedObject]; + } [self MAChangeFromHeader:button]; }