<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>FiSHEncryptionPrefs.h</filename>
    </added>
    <added>
      <filename>FiSHEncryptionPrefs.m</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -21,7 +21,7 @@
 #import &quot;FiSHKeyExchanger.h&quot;;
 
 @class FiSHBlowfish;
-
+@class FiSHEncryptionPrefs;
 
 /// Central controller. Reacts to incoming and outgoing messages, and uses the other classes to encrypt/decrypt these.
 @interface FiSHController : NSObject &lt;MVChatPlugin, FiSHKeyExchangerDelegate&gt;
@@ -29,9 +29,8 @@
    FiSHKeyExchanger *keyExchanger_;
    FiSHBlowfish *blowFisher_;
    
-   NSMutableDictionary *chatEncryptionPreferences_;
+   FiSHEncryptionPrefs *encPrefs_;
    
    NSMutableDictionary *urlToConnectionCache_;
 }
-
 @end</diff>
      <filename>FiSHController.h</filename>
    </modified>
    <modified>
      <diff>@@ -21,24 +21,23 @@
 #import &quot;Controllers/JVChatWindowController.h&quot;
 #import &quot;Controllers/JVChatController.h&quot;
 #import &quot;Panels/JVChatRoomPanel.h&quot;
+#import &quot;Panels/JVDirectChatPanel.h&quot;
 #import &quot;Chat Core/MVChatUser.h&quot;
 #import &quot;Chat Core/MVChatConnection.h&quot;
 #import &quot;Models/JVChatTranscript.h&quot;
 
 #import &quot;FiSHSecretStore.h&quot;
 #import &quot;FiSHBlowfish.h&quot;
+#import &quot;FiSHEncryptionPrefs.h&quot;
 #import &quot;NSString+FiSHyExtensions.h&quot;
 
 
 #define FiSHYDummyConnection @&quot;FiSHYDummyConnection&quot;
 
-// Notifications defined in MVChatConnection.m
-//   We define these here manually to avoid importing the implementation of MVChatConnection, as the notifications in its header-file are only extern'ed.
-NSString *MVChatConnectionGotPrivateMessageNotification = @&quot;MVChatConnectionGotPrivateMessageNotification&quot;;
-
 // Postfix of encrypted messages to mark them visibly for the user.
 // TODO: Make this user-configurable.
-NSString *FiSHEncryptedMessageMarker = @&quot;:*&quot;;
+#define FiSHEncryptedMessageMarker NSLocalizedString(@&quot;Encrypted message&quot;, @&quot;Encrypted message&quot;)
+#define FiSHExpectedEncryptionMarker NSLocalizedString(@&quot;Unencrypted message in encrypted room!&quot;, @&quot;Unencrypted message in encrypted room!&quot;)
 
 // Prefix of outgoing messages which we should not encrypt.
 // TODO: Make this user-configurable.
@@ -48,9 +47,13 @@ NSString *FiSHNonEncryptingPrefix = @&quot;+p&quot;;
 NSString *FiSHKeyExchangeCommand = @&quot;keyx&quot;;
 // Command used to set a key manually. Syntax: /setkey [#channel/nick] newkey. Contrary to keys from automated key exchange, these will be saved to Keychain. If only one argument is given, will use it as key, and will try to deduce the #channel/nick from current view's target.
 NSString *FiSHSetKeyCommand = @&quot;setkey&quot;;
+// Commands to set encryption preference for a chat-room/query
+NSString *FiSHPreferEncCommand = @&quot;enableEnc&quot;;
+NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
 
 
 @interface FiSHController (FiSHyPrivate)
+- (void) joinedDirectChat:(JVDirectChatPanel *)directChat;
 @end
 
 
@@ -66,15 +69,22 @@ NSString *FiSHSetKeyCommand = @&quot;setkey&quot;;
       urlToConnectionCache_ = [[NSMutableDictionary alloc] init];
       blowFisher_ = [[FiSHBlowfish alloc] init];
       
-      chatEncryptionPreferences_ = [[NSMutableDictionary alloc] init];
+      encPrefs_ = [[FiSHEncryptionPrefs alloc] init];
+      
+      // Add encryption setting notices to rooms and queries already open when we were loaded.
+      NSSet *openChatPanels = [[NSClassFromString(@&quot;JVChatController&quot;) defaultController] chatViewControllersKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)];
+      NSEnumerator *chatPanelsEnum = [openChatPanels objectEnumerator];
+      JVDirectChatPanel *aChatPanel = nil;
+      while ((aChatPanel = [chatPanelsEnum nextObject]))
+      {
+         [self joinedDirectChat:aChatPanel];
+      }
    }
    return self;
 }
 
 - (void)dealloc;
 {
-   [chatEncryptionPreferences_ release];
-   
    [blowFisher_ release];
    [urlToConnectionCache_ release];
    [keyExchanger_ release];
@@ -100,6 +110,17 @@ NSString *FiSHSetKeyCommand = @&quot;setkey&quot;;
    [thePanel addEventMessageToDisplay:statusInfo withName:@&quot;KeyExchangeInfo&quot; andAttributes:nil];
 }
 
+- (void)keyExchanger:(FiSHKeyExchanger *)keyExchanger finishedKeyExchangeFor:(NSString *)nickname onConnection:(id)connection succesfully:(BOOL)succesfully;
+{
+   // If the key exchange was succesfull, prefer encryption for that query.
+   if (succesfully)
+   {
+      [self outputStatusInformation:NSLocalizedString(@&quot;Encryption is enabled for this room.&quot;, @&quot;Encryption is enabled for this room.&quot;) forContext:nickname on:connection];
+
+      // TODO: Handle service correclty.
+      [encPrefs_ setTemporaryPreference:FiSHEncPrefPreferEncrypted forService:nil account:nickname];
+}
+}
 
 #pragma mark MVIRCChatConnectionPlugin
 
@@ -151,24 +172,33 @@ NSString *FiSHSetKeyCommand = @&quot;setkey&quot;;
 
 - (void) processOutgoingMessageAsData:(NSMutableData *) message to:(id) receiver attributes:(NSDictionary *)msgAttributes;
 {
+   NSString *errorMessage = nil;
+   
    if ([[msgAttributes objectForKey:@&quot;sendUnencrypted&quot;] boolValue])
       return;
 
    if (![[msgAttributes objectForKey:@&quot;shouldEncrypt&quot;] boolValue])
       return;
 
-   // Check if we have a secret for the current room/nick.
    NSString *accountName = [receiver isKindOfClass:NSClassFromString(@&quot;MVChatRoom&quot;)] ? [receiver name] : [receiver nickname];
+   
+   // Check if we have a secret for the current room/nick.
    // TODO: Handle service/connection correctly.
    NSString *secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
    if (!secret)
+   {
+      errorMessage = NSLocalizedString(@&quot;Encryption is enabled, but you don't have a key. The message was not sent.&quot;, @&quot;Encryption is enabled, but you don't have a key. The message was not sent.&quot;);
       goto bail;
+   }
    
    NSData *encryptedData = nil;
    [blowFisher_ encodeData:message intoData:&amp;encryptedData key:secret];
    // TODO: Handle return value of encodeData.
    if (!encryptedData)
+   {
+      errorMessage = NSLocalizedString(@&quot;Encrypting the message failed. The message was not sent.&quot;, @&quot;Encrypting the message failed. The message was not sent.&quot;);
       goto bail;
+   }
    
    [message setData:encryptedData];
    return;
@@ -185,7 +215,7 @@ bail:
       thePanel = [[NSClassFromString(@&quot;JVChatController&quot;) defaultController] chatViewControllerForUser:receiver 
                                                                                               ifExists:NO
                                                                                          userInitiated:NO];
-   [thePanel addEventMessageToDisplay:NSLocalizedString(@&quot;Encrypting the message failed. The message was not sent&quot;, @&quot;EncryptionFailedInfo&quot;) withName:@&quot;EncryptionFailedInfo&quot; andAttributes:nil];
+   [thePanel addEventMessageToDisplay:errorMessage withName:@&quot;EncryptionFailedInfo&quot; andAttributes:nil];
    
    return;
 }
@@ -194,129 +224,93 @@ bail:
 
 - (void)processIncomingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   if ([[[message attributes] objectForKey:@&quot;decrypted&quot;] boolValue])
+   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
    {
-      NSTextStorage *body = [message body];
-      [body appendAttributedString:[[[NSAttributedString alloc] initWithString:FiSHEncryptedMessageMarker 
-                                                                    attributes:[NSDictionary dictionaryWithObjectsAndKeys:
-                                                                       [NSSet setWithObjects:@&quot;error&quot;, @&quot;encoding&quot;, nil], @&quot;CSSClasses&quot;,
-                                                                       nil]
-         ] autorelease]];
+      NSLog(@&quot;Unexpected view class encountered.&quot;);
       return;
    }
-   
-   if ([[[message attributes] objectForKey:@&quot;shouldEncrypt&quot;] boolValue])
+   // It's now safe to typecast view, to prevent compiler-warnings later.
+   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
+
+   NSString *targetName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
+
+   // The following checks for attributes regarding messages from the local user.
+   if ([message senderIsLocalUser])
    {
-      NSTextStorage *body = [message body];
-      [body appendAttributedString:[[[NSAttributedString alloc] initWithString:FiSHEncryptedMessageMarker 
-                                                                    attributes:[NSDictionary dictionaryWithObjectsAndKeys:
-                                                                       [NSSet setWithObjects:@&quot;error&quot;, @&quot;encoding&quot;, nil], @&quot;CSSClasses&quot;,
-                                                                       nil]
-         ] autorelease]];
-      return;
+      if ([[[message attributes] objectForKey:@&quot;shouldEncrypt&quot;] boolValue] &amp;&amp; [encPrefs_ preferenceForService:nil account:targetName] == FiSHEncPrefAvoidEncrypted)
+      {
+         NSTextStorage *body = [message body];
+         [body appendAttributedString:[[[NSAttributedString alloc] initWithString:FiSHEncryptedMessageMarker 
+                                                                       attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+                                                                          [NSSet setWithObjects:@&quot;error&quot;, @&quot;encoding&quot;, nil], @&quot;CSSClasses&quot;,
+                                                                          nil]
+            ] autorelease]];
+      }
+   } else
+   {
+      // The following checks for attributes regarding messages not from the local user.
+      BOOL decrypted = [[[message attributes] objectForKey:@&quot;decrypted&quot;] boolValue];
+      FiSHEncPrefKey encPref = [encPrefs_ preferenceForService:nil account:targetName];
+      NSString *errorMsg = nil;
+      if (decrypted &amp;&amp; encPref == FiSHEncPrefAvoidEncrypted)
+      {
+         errorMsg = FiSHEncryptedMessageMarker;
+      } else if (!decrypted &amp;&amp; encPref == FiSHEncPrefPreferEncrypted)
+      {
+         errorMsg = FiSHExpectedEncryptionMarker;
+      }
+      
+      if (errorMsg)
+      {
+         NSTextStorage *body = [message body];
+         [body appendAttributedString:[[[NSAttributedString alloc] initWithString:errorMsg 
+                                                                       attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+                                                                          [NSSet setWithObjects:@&quot;error&quot;, @&quot;encoding&quot;, nil], @&quot;CSSClasses&quot;,
+                                                                          nil]
+            ] autorelease]];
+      }
    }
 }
 
 - (void)processOutgoingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   // TODO: Decide here, if a message should be encrypted based on user's preferences for the current room/query.
-   NSMutableDictionary *attributes = [[message attributes] mutableCopy];
-   if (!attributes)
-      attributes = [NSMutableDictionary dictionary];
-   [attributes setObject:[NSNumber numberWithBool:YES] forKey:@&quot;shouldEncrypt&quot;];
-   [message setAttributes:attributes];
+   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   {
+      NSLog(@&quot;Unexpected view class encountered.&quot;);
+      return;
+   }
+   // It's now safe to typecast view, to prevent compiler-warnings later.
+   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
+
+   NSString *targetName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
+
+   // Check, if the user prefers encryption for this target. If so, mark the message, so that we can encrypt it later.
+   // TODO: Handle service.
+   if ([encPrefs_ preferenceForService:nil account:targetName] == FiSHEncPrefPreferEncrypted)
+   {
+      NSMutableDictionary *attributes = [[message attributes] mutableCopy];
+      if (!attributes)
+         attributes = [NSMutableDictionary dictionary];
+      [attributes setObject:[NSNumber numberWithBool:YES] forKey:@&quot;shouldEncrypt&quot;];
+      [message setAttributes:attributes];
+   } else if ([encPrefs_ preferenceForService:nil account:targetName] == FiSHEncPrefAvoidEncrypted)
+   {
+      NSMutableDictionary *attributes = [[message attributes] mutableCopy];
+      if (!attributes)
+         attributes = [NSMutableDictionary dictionary];
+      [attributes setObject:[NSNumber numberWithBool:YES] forKey:@&quot;sendUnencrypted&quot;];
+      [message setAttributes:attributes];
+   }
+}
+
+
+#pragma mark MVChatPluginRoomSupport
+
+- (void) joinedRoom:(JVChatRoomPanel *) room;
+{
+   [self joinedDirectChat:room];
 }
 
-//- (void)processIncomingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
-//{
-//   return;
-//   
-//   // Neither JVMutableChatMessage nor the JVChatViewController protocol allow us to get the context of the received message (ie. the corresponding channel/query)
-//   // It seems that most, if not all, views we get here are of a subclass of JVDirectChatPanel, which do allow us to get above context.
-//   // So check first for the correct class-membership before proceeding.
-//   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
-//   {
-//      NSLog(@&quot;Unexpected view class encountered.&quot;);
-//      return;
-//   }
-//   // It's now safe to typecast view, to prevent compiler-warnings later.
-//   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
-//   
-//   NSLog(@&quot;processIncomingMessage:%@&quot;, [message bodyAsHTML]);
-//
-//   // Get the secret for the current room/nick and connection. If we don't have one, just display the message without changes.
-//   // DEBUG!!! Hardcoded service and account
-//   // We have to differentiate between a message of a query or a chat room, as the method of getting at the account name is named slightly different.
-//   NSString *accountName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
-//   // TODO: Handle service/connection correctly.
-//   NSString *secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
-//   if (!secret)
-//      return;
-//
-//   // Try to decrypt the raw encrypted text.
-//   NSData *decryptedData = nil;
-//   [blowFisher_ decodeData:[[message bodyAsPlainText] dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO] 
-//                    intoData:&amp;decryptedData
-//                         key:secret];
-//   if (!decryptedData)
-//      return;
-//   // TODO: Handle cases where decryption was only partial succesfull, and display a corresponding note to the user.
-//
-//   NSString *decryptedMessage = [[[NSString alloc] initWithData:decryptedData encoding:[view encoding]] autorelease];
-//   // Update the message with the decrypted text, marking it, so the user knows it was encrypted.
-//   [message setBodyAsPlainText:[decryptedMessage stringByAppendingString:FiSHEncryptedMessageMarker]];
-//}
-//
-//- (void)processOutgoingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
-//{
-//   return;
-//   
-//   // Neither JVMutableChatMessage nor the JVChatViewController protocol allow us to get the context of the received message (ie. the corresponding channel/query)
-//   // It seems that most, if not all, views we get here are of a subclass of JVDirectChatPanel, which do allow us to get above context.
-//   // So check first for the correct class-membership before proceeding.
-//   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
-//   {
-//      NSLog(@&quot;Unexpected view class encountered.&quot;);
-//      return;
-//   }
-//   // It's now safe to typecast view, to prevent compiler-warnings later.
-//   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
-//
-//   NSLog(@&quot;processOutgoingMessage:%@&quot;, message);
-//   
-//   NSString *plaintextBody = [message bodyAsPlainText];
-//   
-//   if ([plaintextBody hasPrefix:FiSHNonEncryptingPrefix])
-//   {
-//      // User override of encryption. Remove the prefix, and transmit the rest of the message unencrypted.
-//      [message setBodyAsPlainText:[plaintextBody substringFromIndex:[FiSHNonEncryptingPrefix length]]];
-//      return;
-//   }
-//   
-//   // Check if we have a secret for the current room/nick.
-//   NSString *accountName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
-//   // TODO: Handle service/connection correctly.
-//   NSString *secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
-//   if (!secret)
-//      return;
-//   
-//   NSData *encryptedData = nil;
-//   [blowFisher_ encodeData:[plaintextBody dataUsingEncoding:[view encoding] allowLossyConversion:YES] intoData:&amp;encryptedData key:secret];
-//   // TODO: Handle return value of encodeData.
-//   if (!encryptedData)
-//      return;
-//   
-//   NSString *encryptedMessage = [[[NSString alloc] initWithData:encryptedData encoding:NSASCIIStringEncoding] autorelease];
-//   [message setBodyAsPlainText:encryptedMessage];
-//}
-
-
-//#pragma mark MVChatPluginRoomSupport
-//- (void) joinedRoom:(JVChatRoomPanel *) room;
-//{
-//   NSLog(@&quot;joinedRoom:%@&quot;, room);
-//}
-   
 
 #pragma mark Command handlers
 
@@ -413,7 +407,16 @@ bail:
    
    // Everything's fine, set the key.
    // TODO: Handle service/connection correctly.
-   [[FiSHSecretStore sharedSecretStore] storeSecret:secret forService:nil account:account isTemporary:NO];
+   if (![[FiSHSecretStore sharedSecretStore] storeSecret:secret forService:nil account:account isTemporary:NO])
+   {
+      [view addEventMessageToDisplay:NSLocalizedString(@&quot;Failed to save the key.&quot;, @&quot;Failed to save the key.&quot;) withName:@&quot;KeySaveError&quot; andAttributes:nil];   
+      return NO;
+   }
+      
+   [view addEventMessageToDisplay:NSLocalizedString(@&quot;Key saved to Keychain.&quot;, @&quot;Key saved to Keychain.&quot;) withName:@&quot;KeySavedToKeychain&quot; andAttributes:nil];   
+   
+   [encPrefs_ setPreference:FiSHEncPrefPreferEncrypted forService:nil account:account];
+   [view addEventMessageToDisplay:NSLocalizedString(@&quot;Encryption is enabled for this room.&quot;, @&quot;Encryption is enabled for this room.&quot;) withName:@&quot;EncryptionEnabledForRoom&quot; andAttributes:nil];   
    
    return YES;
 }
@@ -436,6 +439,37 @@ bail:
    return YES;
 }
 
+- (BOOL) processEncryptionPreferenceCommandWithArguments:(NSAttributedString *)arguments toConnection:(MVChatConnection *)connection inView:(id &lt;JVChatViewController&gt;)aView pref:(FiSHEncPrefKey)encPref;
+{
+   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   {
+      NSLog(@&quot;Unexpected view class encountered.&quot;);
+      return NO;
+   }
+   // It's now safe to typecast view, to prevent compiler-warnings later.
+   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
+   
+   NSArray *argumentList = [[arguments string] FiSH_arguments];
+   NSString *targetName = nil;
+   if ([argumentList count] == 1)
+   {
+      targetName = [argumentList objectAtIndex:0];
+   } else if ([argumentList count] == 0)
+   {
+      targetName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
+   } else
+   {
+      // TODO: Put out notice to user.
+      NSLog(@&quot;Command expects exactly 1 argument&quot;);
+      return NO;
+   }
+   
+   // TODO: Differenciate between services here.
+   [encPrefs_ setPreference:encPref forService:nil account:targetName];
+   
+   return YES;
+}
+
 #pragma mark MVChatPluginCommandSupport
 
 - (BOOL)processUserCommand:(NSString *) command withArguments:(NSAttributedString *) arguments toConnection:(MVChatConnection *) connection inView:(id &lt;JVChatViewController&gt;)aView;
@@ -458,6 +492,10 @@ bail:
       return [self processSetKeyCommandWithArguments:arguments toConnection:connection inView:view];
    if ([command isEqualToString:FiSHNonEncryptingPrefix])
       return [self processSendUnecryptedCommandWithArguments:arguments toConnection:connection inView:view];
+   if ([command isEqualToString:FiSHPreferEncCommand])
+      return [self processEncryptionPreferenceCommandWithArguments:arguments toConnection:connection inView:view pref:FiSHEncPrefPreferEncrypted];
+   if ([command isEqualToString:FiSHAvoidEncCommand])
+      return [self processEncryptionPreferenceCommandWithArguments:arguments toConnection:connection inView:view pref:FiSHEncPrefAvoidEncrypted];
    
    return NO;
 }
@@ -466,4 +504,20 @@ bail:
 @end
 
 @implementation FiSHController (FiSHyPrivate)
+
+// TODO: implement the following in colloquy
+#pragma mark MVDirectChatSupport (not yet implemented in Colloquy)
+
+// TODO: Move this out of private category when implemented in colloquy.
+- (void) joinedDirectChat:(JVDirectChatPanel *)directChat;
+{
+   JVDirectChatPanel *thePanel = [[NSClassFromString(@&quot;JVChatController&quot;) defaultController] chatViewControllerForRoom:[directChat target]
+                                                                                                              ifExists:NO];
+   
+   if ([encPrefs_ preferenceForService:nil account:[[directChat target] name]] == FiSHEncPrefPreferEncrypted)
+      [thePanel addEventMessageToDisplay:NSLocalizedString(@&quot;Encryption is enabled for this room.&quot;, @&quot;Encryption is enabled for this room.&quot;) withName:@&quot;EncryptionEnabledForRoom&quot; andAttributes:nil];
+   if ([encPrefs_ preferenceForService:nil account:[[directChat target] name]] == FiSHEncPrefAvoidEncrypted)
+      [thePanel addEventMessageToDisplay:NSLocalizedString(@&quot;Encryption is disabled for this room.&quot;, @&quot;Encryption is disabled for this room.&quot;) withName:@&quot;EncryptionDisabledForRoom&quot; andAttributes:nil];
+}
+
 @end</diff>
      <filename>FiSHController.m</filename>
    </modified>
    <modified>
      <diff>@@ -18,10 +18,7 @@
 #import &lt;Cocoa/Cocoa.h&gt;
 
 
-@protocol FiSHKeyExchangerDelegate
-- (void)sendPrivateMessage:(NSString *)message to:(NSString *)receiver on:(id)connection;
-- (void)outputStatusInformation:(NSString *)statusInfo forContext:(NSString *)chatContext on:(id)connection;
-@end
+@protocol FiSHKeyExchangerDelegate;
 
 
 @interface FiSHKeyExchanger : NSObject
@@ -38,3 +35,11 @@
 
 - (BOOL)processPrivateMessageAsData:(NSData *)message from:(NSString *)sender on:(id)connection;
 @end
+
+
+@protocol FiSHKeyExchangerDelegate
+- (void)sendPrivateMessage:(NSString *)message to:(NSString *)receiver on:(id)connection;
+- (void)outputStatusInformation:(NSString *)statusInfo forContext:(NSString *)chatContext on:(id)connection;
+// TODO: Call the following for unsuccesfully exchanges, too.
+- (void)keyExchanger:(FiSHKeyExchanger *)keyExchanger finishedKeyExchangeFor:(NSString *)nickname onConnection:(id)connection succesfully:(BOOL)succesfully;
+@end</diff>
      <filename>FiSHKeyExchanger.h</filename>
    </modified>
    <modified>
      <diff>@@ -77,8 +77,10 @@ const NSString *FiSHKeyExchangeInfoRemoveOldTempKeyPairTimerKey = @&quot;FiSHKeyExcha
    if (!dhKeyExchanger-&gt;generate())
    {
       free(dhKeyExchanger);
-      // TODO: Inform user about this.
-      NSLog(@&quot;Error initiating DH key exchange.&quot;);
+
+      [delegate_ outputStatusInformation:NSLocalizedString(@&quot;Unknown error during key exchange.&quot;, &quot;Unknown error during key exchange.&quot;)
+                              forContext:nickname
+                                      on:connection];
       return;
    }
    
@@ -247,6 +249,8 @@ const NSString *FiSHKeyExchangeInfoRemoveOldTempKeyPairTimerKey = @&quot;FiSHKeyExcha
    [delegate_ outputStatusInformation:NSLocalizedString(@&quot;Key exchange completed.&quot;, &quot;Key exchange completed&quot;)
                            forContext:nickname
                                    on:connection];
+
+   [delegate_ keyExchanger:self finishedKeyExchangeFor:nickname onConnection:connection succesfully:YES];
 }
 
 - (void)handleFiSHKeyExchangeResponseFrom:(NSString *)nickname on:(id)connection withRemotePublicKeyData:(NSString *)remotePublicKeyData;
@@ -297,10 +301,12 @@ const NSString *FiSHKeyExchangeInfoRemoveOldTempKeyPairTimerKey = @&quot;FiSHKeyExcha
    
    // TODO: Handle service/connection correctly.
    [[FiSHSecretStore sharedSecretStore] storeSecret:theSecret forService:nil account:nickname isTemporary:YES];
-
+   
    [delegate_ outputStatusInformation:NSLocalizedString(@&quot;Key exchange completed.&quot;, &quot;Key exchange completed&quot;)
                            forContext:nickname
                                    on:connection];
+   
+   [delegate_ keyExchanger:self finishedKeyExchangeFor:nickname onConnection:connection succesfully:YES];
 }
 
 </diff>
      <filename>FiSHKeyExchanger.mm</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,7 @@
 
 + (FiSHSecretStore *)sharedSecretStore;
 
-- (void)storeSecret:(NSString *)secret forService:(NSString *)serviceName account:(NSString *)accountName isTemporary:(BOOL)isTemporary;
+- (BOOL)storeSecret:(NSString *)secret forService:(NSString *)serviceName account:(NSString *)accountName isTemporary:(BOOL)isTemporary;
 - (NSString *)secretForService:(NSString *)serviceName account:(NSString *)accountName;
 
 @end</diff>
      <filename>FiSHSecretStore.h</filename>
    </modified>
    <modified>
      <diff>@@ -55,7 +55,7 @@ FiSHSecretStore *sharedSecretStore = nil;
 /**
   If isTemporary is set, the secret won't be saved in the Keychain. Use this for keys from automatic key exchanges in queries, for example.
  */
-- (void)storeSecret:(NSString *)secret forService:(NSString *)serviceName account:(NSString *)accountName isTemporary:(BOOL)isTemporary;
+- (BOOL)storeSecret:(NSString *)secret forService:(NSString *)serviceName account:(NSString *)accountName isTemporary:(BOOL)isTemporary;
 {
    serviceName = [serviceName lowercaseString];
    accountName = [accountName lowercaseString];
@@ -78,7 +78,7 @@ FiSHSecretStore *sharedSecretStore = nil;
       
       [secretsForService setObject:secret forKey:accountName];
       
-      return;
+      return YES;
    } else
    {
       // The secret is not temporary, so delete any prior temporary secrets for this connection/nick
@@ -144,6 +144,8 @@ FiSHSecretStore *sharedSecretStore = nil;
       // Something went wrong looking for/chaning/adding an entry in Keychain, just bail out.
       NSLog(@&quot;Can't access Keychain.&quot;);
    }
+   
+   return (status != noErr);
 }
 
 /// Returns the secret used to communicate with accountName on serviceName from the default Keychain, nil if no secret is stored currently.</diff>
      <filename>FiSHSecretStore.m</filename>
    </modified>
    <modified>
      <diff>@@ -20,6 +20,7 @@
 		D45D05180B7EFA76001DF87B /* FiSHSecretStore.m in Sources */ = {isa = PBXBuildFile; fileRef = D45D05170B7EFA76001DF87B /* FiSHSecretStore.m */; };
 		D45D051B0B7EFAAC001DF87B /* FiSHKeyExchanger.mm in Sources */ = {isa = PBXBuildFile; fileRef = D45D051A0B7EFAAC001DF87B /* FiSHKeyExchanger.mm */; };
 		D496F3DB0B80BDB60015B1F2 /* NSString+FiSHyExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D496F3DA0B80BDB60015B1F2 /* NSString+FiSHyExtensions.m */; };
+		D4A6B3840B8FB881001816EB /* FiSHEncryptionPrefs.m in Sources */ = {isa = PBXBuildFile; fileRef = D4A6B3830B8FB881001816EB /* FiSHEncryptionPrefs.m */; };
 		D4AFBC4A0B8A2F070086B38A /* NSData+FiSHyExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AFBC490B8A2F070086B38A /* NSData+FiSHyExtensions.m */; };
 		D4BD4B080B8CD64A0016D3B9 /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = D4BD4B070B8CD64A0016D3B9 /* LICENSE.txt */; };
 /* End PBXBuildFile section */
@@ -94,6 +95,8 @@
 		D477672E0B83F7870099B4B8 /* libcrypto.0.9.7.dylib */ = {isa = PBXFileReference; lastKnownFileType = &quot;compiled.mach-o.dylib&quot;; name = libcrypto.0.9.7.dylib; path = /usr/lib/libcrypto.0.9.7.dylib; sourceTree = &quot;&lt;absolute&gt;&quot;; };
 		D496F3D90B80BDB50015B1F2 /* NSString+FiSHyExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = &quot;NSString+FiSHyExtensions.h&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D496F3DA0B80BDB60015B1F2 /* NSString+FiSHyExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = &quot;NSString+FiSHyExtensions.m&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
+		D4A6B3820B8FB881001816EB /* FiSHEncryptionPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FiSHEncryptionPrefs.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+		D4A6B3830B8FB881001816EB /* FiSHEncryptionPrefs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FiSHEncryptionPrefs.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4AFBC480B8A2F070086B38A /* NSData+FiSHyExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = &quot;NSData+FiSHyExtensions.h&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4AFBC490B8A2F070086B38A /* NSData+FiSHyExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = &quot;NSData+FiSHyExtensions.m&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4BD4B070B8CD64A0016D3B9 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = &quot;&lt;group&gt;&quot;; };
@@ -170,6 +173,8 @@
 				D45D05170B7EFA76001DF87B /* FiSHSecretStore.m */,
 				D45D05190B7EFAAC001DF87B /* FiSHKeyExchanger.h */,
 				D45D051A0B7EFAAC001DF87B /* FiSHKeyExchanger.mm */,
+				D4A6B3820B8FB881001816EB /* FiSHEncryptionPrefs.h */,
+				D4A6B3830B8FB881001816EB /* FiSHEncryptionPrefs.m */,
 				D496F3D90B80BDB50015B1F2 /* NSString+FiSHyExtensions.h */,
 				D496F3DA0B80BDB60015B1F2 /* NSString+FiSHyExtensions.m */,
 				D4AFBC480B8A2F070086B38A /* NSData+FiSHyExtensions.h */,
@@ -431,6 +436,7 @@
 				D4423E1A0B84B8630039DCB9 /* dh1080.cpp in Sources */,
 				D4423EA00B84BDFA0039DCB9 /* misc.cpp in Sources */,
 				D4AFBC4A0B8A2F070086B38A /* NSData+FiSHyExtensions.m in Sources */,
+				D4A6B3840B8FB881001816EB /* FiSHEncryptionPrefs.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};</diff>
      <filename>FiSHy.xcodeproj/project.pbxproj</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>5d541f12126ddf63bf2948b4ba4d7b7ddfc832e6</id>
    </parent>
  </parents>
  <author>
    <name>henningkiel</name>
    <email>henningkiel</email>
  </author>
  <url>http://github.com/hennk/fishy/commit/23b016d5dc30232fffc77643b395bf0c9078919e</url>
  <id>23b016d5dc30232fffc77643b395bf0c9078919e</id>
  <committed-date>2007-02-23T18:28:57-08:00</committed-date>
  <authored-date>2007-02-23T18:28:57-08:00</authored-date>
  <message>One can now enable or disable encryption on a per room/nick basis. This influences if sent messages will be encrypted, and if received clear-text messages will be flagged with a warning.

A nick for which a keyx has been done will be flagged as encrypted until the program quits. Rooms/nicks for which a setkey has been done will be flagged as encrypted permanently. This can be changed with /enableEnc and /disableEnc

A message in an encrypted room can be send as clear-text via /+p</message>
  <tree>f6af98430b2ed002d0e802dff4b25fd51fb586d9</tree>
  <committer>
    <name>henningkiel</name>
    <email>henningkiel</email>
  </committer>
</commit>
