<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>FiSHMisc.h</filename>
    </added>
    <added>
      <filename>FiSHMisc.m</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -32,7 +32,7 @@ typedef enum
 {
 
 }
-- (FiSHCypherResult)encodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;
+- (void)encodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;
 - (FiSHCypherResult)decodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;
 
 @end</diff>
      <filename>FiSHBlowfish.h</filename>
    </modified>
    <modified>
      <diff>@@ -34,28 +34,18 @@ using namespace fish;
    return self;
 }
 
-- (FiSHCypherResult)encodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;
+- (void)encodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;
 {
    string inputString((const char*)[inputData bytes], [inputData length]);
    stringstream outputStream;
    // TODO: Is utf8 the best option for the string? probably better to make sure keys are always pure ascii.
    string keyString([theKey UTF8String]);
-   FiSHCypherResult result;
-   switch (encode(inputString, outputStream, keyString, false))
-   {
-      case success: result = FiSHCypherSuccess; break;
-      case cut: result = FiSHCypherTextCut; break;
-      case plain_text: result = FiSHCypherPlainText; break;
-      case bad_chars: result = FiSHCypherBadCharacters; break;
-      default:
-         // Invalid return code.
-         return FiSHCypherUnknownError;
-   }
+   encode(inputString, outputStream, keyString, false);
    // TODO: The following assumes that during encoding no splitting took place.
    string tempOutputString;
    getline(outputStream, tempOutputString);
    *outputData = [NSData dataWithBytes:tempOutputString.data() length:tempOutputString.size()];
-   return result;
+   return;
 }
 
 - (FiSHCypherResult)decodeData:(NSData *)inputData intoData:(NSData **)outputData key:(NSString *)theKey;</diff>
      <filename>FiSHBlowfish.mm</filename>
    </modified>
    <modified>
      <diff>@@ -30,6 +30,7 @@
 #import &quot;FiSHBlowfish.h&quot;
 #import &quot;FiSHEncryptionPrefs.h&quot;
 #import &quot;NSString+FiSHyExtensions.h&quot;
+#import &quot;FiSHMisc.h&quot;
 
 
 #define FiSHYDummyConnection @&quot;FiSHYDummyConnection&quot;
@@ -38,6 +39,7 @@
 // TODO: Make this user-configurable.
 #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;)
+#define FiSHEncryptionOverriddenMarker NSLocalizedString(@&quot;Encryption overridden&quot;, @&quot;Encryption overridden&quot;)
 
 // Prefix of outgoing messages which we should not encrypt.
 // TODO: Make this user-configurable.
@@ -52,6 +54,7 @@ NSString *FiSHPreferEncCommand = @&quot;enableEnc&quot;;
 NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
 
 
+
 @interface FiSHController (FiSHyPrivate)
 - (void) joinedDirectChat:(JVDirectChatPanel *)directChat;
 @end
@@ -64,7 +67,7 @@ NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
    if (self = [super init])
    {
       DLog(@&quot;Loading FiSHy.&quot;);
-      
+
       keyExchanger_ = [[FiSHKeyExchanger alloc] initWithDelegate:self];
       urlToConnectionCache_ = [[NSMutableDictionary alloc] init];
       blowFisher_ = [[FiSHBlowfish alloc] init];
@@ -126,6 +129,11 @@ NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
 
 - (void) processIncomingMessageAsData:(NSMutableData *) message from:(MVChatUser *) sender to:(id) receiver attributes:(NSMutableDictionary *)msgAttributes;
 {
+   // We only support IRC connections.
+   if (!FiSHIsIRCConnection([sender connection]))
+      return;
+
+   
    // If we received a notice, it is possible, that it's a key exchange request/response. Let the FiSHKeyExchanger decide this.
    if ([[msgAttributes objectForKey:@&quot;notice&quot;] boolValue])
    {
@@ -136,17 +144,28 @@ NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
                                                                           on:[[theConnection url] absoluteString]];
       if (isKeyExchangeMessage)
       {
+         // FiSHKeyExchanger handled the notice for us, so make Colloquy ignore it.
          [message setLength:0];
          return;
       }
    }
    
+   
    // Get the secret for the current room/nick and connection. If we don't have one, just display the message without changes.
+   NSString *accountName = nil;
+   NSString *secret = nil;
+   // The receiver is either the local user, or a chat room.
+   if ([receiver isKindOfClass:NSClassFromString(@&quot;MVChatRoom&quot;)])
+      accountName = FiSHNameForChatObject(receiver);
+   else
+      accountName = FiSHNameForChatObject(sender);
+   if (!accountName)
+   {
+      DLog(@&quot;Ignoring unsupported chat object type.&quot;);
+      return;
+   }
    // TODO: Handle service/connection correctly.
-   // 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 = [receiver isKindOfClass:NSClassFromString(@&quot;MVChatRoom&quot;)] ? [receiver name] : [sender nickname];
-   // TODO: Handle service/connection correctly.
-   NSString *secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
+   secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
    if (!secret)
       return;
    
@@ -172,17 +191,30 @@ NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
 
 - (void) processOutgoingMessageAsData:(NSMutableData *) message to:(id) receiver attributes:(NSDictionary *)msgAttributes;
 {
-   NSString *errorMessage = nil;
+   // We only support IRC connection. As we don't know what receiver we get passed here, extra checks are performed.
+   if (![receiver respondsToSelector:@selector(connection)])
+      return;
+   if (!FiSHIsIRCConnection([receiver connection]))
+      return;
    
+
+   // Check if the user has overridden encryption temporarily.
    if ([[msgAttributes objectForKey:@&quot;sendUnencrypted&quot;] boolValue])
       return;
-
+   // Check if the message is for a target for which encryption is enabled.
    if (![[msgAttributes objectForKey:@&quot;shouldEncrypt&quot;] boolValue])
       return;
 
-   NSString *accountName = [receiver isKindOfClass:NSClassFromString(@&quot;MVChatRoom&quot;)] ? [receiver name] : [receiver nickname];
+   
+   NSString *errorMessage = nil;
    
    // Check if we have a secret for the current room/nick.
+   NSString *accountName = FiSHNameForChatObject(receiver);
+   if (!accountName)
+   {
+      DLog(@&quot;Ignoring unsupported chat object type.&quot;);
+      return;
+   }
    // TODO: Handle service/connection correctly.
    NSString *secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName];
    if (!secret)
@@ -191,15 +223,14 @@ NSString *FiSHAvoidEncCommand = @&quot;disableEnc&quot;;
       goto bail;
    }
    
+   // Try to encrypt the raw outgoing message.
    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;
    
@@ -222,74 +253,92 @@ bail:
 
 #pragma mark MVChatPluginDirectChatSupport
 
+/// Called whenever a message gets added to a chat panel.
+/**
+ Based on the attributes the incoming message has, we change its appearance to the user here. For received messages we already had a chance to decrypt it, and if so, have set the @&quot;decrypted&quot; attribute. For messages of the local user the message already went through processOutgoingMessage:inView:, were attributes were set if the message should be encrypted later, or not.
+ Received messages which were encrypted in a room for which encryption is avoided are marked, as well as unencrypted messages in a room for which encryption is preferred.
+ Encrypted messages of the local user are marked if sent in a room for which encryption is avoided. 
+ */
 - (void)processIncomingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   // We only support IRC connection.
+   if (!FiSHIsIRCConnection([aView connection]))
+      return;
+   
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
+      DLog(@&quot;Ignoring unsupported chat controller type.&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];
+   
 
-   // The following checks for attributes regarding messages from the local user.
-   if ([message senderIsLocalUser])
+   // Get the name of the chat object the message is directed at.
+   NSString *accountName = FiSHNameForChatObject([view target]);
+   if (!accountName)
    {
-      if ([[message 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 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]];
-      }
+      DLog(@&quot;Ignoring unsupported chat object type.&quot;);
+      return;
    }
+   
+   // Prepare a string to mark the message if it either was encrypted in an encryption-avoiding context or unencrypted in an encryption-preferring context.
+   // At two places we have to differentiate between received messages and messages from the local user. Received messages can have a @&quot;decrypted&quot;-attribute while local messages can have a @&quot;shouldEncrypt&quot;-attribute. The marker for unencrypted remote messages in an encryption-preferring context is different than for user-overridden encryption for local messages.
+   NSString *messageMarker = nil;
+   BOOL hasBeenOrWillBeEncrypted = [message senderIsLocalUser] ? [[message objectForKey:@&quot;shouldEncrypt&quot;] boolValue] : [[message objectForKey:@&quot;decrypted&quot;] boolValue];
+   FiSHEncPrefKey encPref = [encPrefs_ preferenceForService:nil account:accountName];
+   if (hasBeenOrWillBeEncrypted &amp;&amp; encPref == FiSHEncPrefAvoidEncrypted)
+   {
+      messageMarker = FiSHEncryptedMessageMarker;
+   } else if (!hasBeenOrWillBeEncrypted &amp;&amp; encPref == FiSHEncPrefPreferEncrypted)
+   {
+      messageMarker = [message senderIsLocalUser] ? FiSHEncryptionOverriddenMarker : FiSHExpectedEncryptionMarker;
+   } else
+      return;
+   
+   NSTextStorage *body = [message body];
+   [body appendAttributedString:[[[NSAttributedString alloc] initWithString:messageMarker 
+                                                                 attributes:[NSDictionary dictionaryWithObjectsAndKeys:
+                                                                    [NSSet setWithObjects:@&quot;error&quot;, @&quot;encoding&quot;, nil], @&quot;CSSClasses&quot;,
+                                                                    nil]
+      ] autorelease]];
 }
 
+/// Called whenever a message gets sent over a ChatViewController.
+/**
+ This is the first place we get in contact with an outgoing message. Depending on user's preferences regarding the channel/query the message was sent in we set its attributes, so that at the lower layer, in processOutgoingMessageAsData::: we can encrypt it.
+ This method won't be called for message sent with one of the sendMessage: methods in Chat Core.
+ */
 - (void)processOutgoingMessage:(JVMutableChatMessage *)message inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   // We only support IRC connection.
+   if (!FiSHIsIRCConnection([aView connection]))
+      return;
+   
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
+      DLog(@&quot;Ignoring unsupported chat controller type.&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];
 
+   
+   // Get the name of the chat object the message is directed at.
+   NSString *accountName = FiSHNameForChatObject([view target]);
+   if (!accountName)
+   {
+      DLog(@&quot;Ignoring unsupported chat object type.&quot;);
+      return;
+   }
+   
    // 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)
+   FiSHEncPrefKey encPref = [encPrefs_ preferenceForService:nil account:accountName];
+   if (encPref == FiSHEncPrefPreferEncrypted)
    {
       [message setObject:[NSNumber numberWithBool:YES] forKey:@&quot;shouldEncrypt&quot;];
-   } else if ([encPrefs_ preferenceForService:nil account:targetName] == FiSHEncPrefAvoidEncrypted)
+   } else if (encPref == FiSHEncPrefAvoidEncrypted)
    {
       [message setObject:[NSNumber numberWithBool:YES] forKey:@&quot;sendUnencrypted&quot;];
    }
@@ -300,6 +349,11 @@ bail:
 
 - (void) joinedRoom:(JVChatRoomPanel *) room;
 {
+   // We only support IRC connection.
+   if (!FiSHIsIRCConnection([[room target] connection]))
+      return;
+   
+   
    [self joinedDirectChat:room];
 }
 
@@ -308,17 +362,15 @@ bail:
 
 - (BOOL)processKeyExchangeCommandWithArguments:(NSAttributedString *)arguments toConnection:(MVChatConnection *)connection inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   // 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;)])
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
-      return NO;
+      DLog(@&quot;Ignoring unsupported chat controller type.&quot;);
+      return;
    }
-   // It's now safe to typecast view, to prevent compiler-warnings later.
-   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
 
+   
    NSString *argumentString = [arguments string];
    // If no argument has been given, try to deduce it from the current view. This only works for queries, as key exchange for channels is not supported.
    if (!argumentString || [argumentString length] &lt;= 0)
@@ -327,17 +379,16 @@ bail:
          argumentString = [[view target] nickname];
       else
       {
-         // TODO: Put out notice to user.
          DLog(@&quot;No argument provided to /keyx, aborting.&quot;);
-         return NO;
+         return YES;
       }
    }
    // Check if argument is a channel-name. If so, cancel.
    if ([argumentString hasPrefix:@&quot;#&quot;] || [argumentString hasPrefix:@&quot;&amp;&quot;])
    {
-      // TODO: Put out notice to user.
+      [view addEventMessageToDisplay:@&quot;Key exchange is not supported for channels&quot; withName:@&quot;KeyExchangeUnsupportedForChannels&quot; andAttributes:nil];
       DLog(@&quot;Key exchange is only supported for nicknames, not for channels, aborting.&quot;);
-      return NO;
+      return YES;
    }
    
    // Everything's fine, start the key exchange.
@@ -349,38 +400,30 @@ bail:
 
 - (BOOL)processSetKeyCommandWithArguments:(NSAttributedString *)arguments toConnection:(MVChatConnection *)connection inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   NSString *argumentString = [arguments string];
-   // If no argument has been given, abort.
-   if (!argumentString || [argumentString length] &lt;= 0)
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      // TODO: Put out notice to user.
-      DLog(@&quot;No arguments provided to /setkey, aborting.&quot;);
-      return NO;
+      DLog(@&quot;Ignoring unsupported chat controller type.&quot;);
+      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;)])
-   {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
-      return NO;
-   }
-   // It's now safe to typecast view, to prevent compiler-warnings later.
-   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
    
-   // Check number of arguments. If two, proceed. If one, try to deduce target by current view's target. If anything else, abort.
+   NSString *argumentString = [arguments string];
    NSArray *argumentList = [[arguments string] FiSH_arguments];
    NSString *secret = nil;
    NSString *account = nil;
-   if ([argumentList count] == 1)
+   // If two arguments, proceed. If one, try to deduce target by current view's target. If anything else, abort.
+   if (!argumentList || [argumentList count] &lt;= 0)
+   {
+      DLog(@&quot;SetKey expects exactly 2 arguments, aborting.&quot;);
+      return YES;
+   } else if ([argumentList count] == 1)
    {
       // If only one argument has been given, use that as secret, and try to deduce the account from the current view.
-      if ([[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)])
-         account = [[view target] nickname];
-      else if ([[view target] isKindOfClass:NSClassFromString(@&quot;MVChatRoom&quot;)])
-         account = [[view target] name];
-      else {
+      account = FiSHNameForChatObject([view target]);
+      if (!account)
+      {
          // TODO: Put out notice to user.
          DLog(@&quot;SetKey expects exactly 2 arguments, aborting.&quot;);
          return NO;
@@ -390,11 +433,6 @@ bail:
    {
       secret = [argumentList objectAtIndex:1];
       account = [argumentList objectAtIndex:0];
-   } else
-   {
-      // TODO: Put out notice to user.
-      DLog(@&quot;SetKey expects exactly 2 arguments, aborting.&quot;);
-      return NO;
    }
    
    // Everything's fine, set the key.
@@ -404,7 +442,6 @@ bail:
       [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];
@@ -415,13 +452,14 @@ bail:
 
 - (BOOL)processSendUnecryptedCommandWithArguments:(NSAttributedString *)arguments toConnection:(MVChatConnection *)connection inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
-      return NO;
+      DLog(@&quot;Ignoring unsupported chat controller type.&quot;);
+      return;
    }
-   // It's now safe to typecast view, to prevent compiler-warnings later.
-   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
+
    
    JVMutableChatMessage *msg = [[[NSClassFromString(@&quot;JVMutableChatMessage&quot;) alloc] initWithText:arguments sender:[connection localUser]] autorelease];
    [msg setObject:[NSNumber numberWithBool:YES] forKey:@&quot;sendUnencrypted&quot;];
@@ -433,13 +471,14 @@ bail:
 
 - (BOOL) processEncryptionPreferenceCommandWithArguments:(NSAttributedString *)arguments toConnection:(MVChatConnection *)connection inView:(id &lt;JVChatViewController&gt;)aView pref:(FiSHEncPrefKey)encPref;
 {
-   if (![aView isKindOfClass:NSClassFromString(@&quot;JVDirectChatPanel&quot;)])
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
    {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
-      return NO;
+      DLog(@&quot;Ignoring unsupported chat controller type.&quot;);
+      return;
    }
-   // 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;
@@ -451,7 +490,6 @@ bail:
       targetName = [[view target] isKindOfClass:NSClassFromString(@&quot;MVChatUser&quot;)] ? [[view target] nickname] : [[view target] name];
    } else
    {
-      // TODO: Put out notice to user.
       DLog(@&quot;Command expects exactly 1 argument&quot;);
       return NO;
    }
@@ -478,16 +516,18 @@ bail:
 
 - (BOOL)processUserCommand:(NSString *) command withArguments:(NSAttributedString *) arguments toConnection:(MVChatConnection *) connection inView:(id &lt;JVChatViewController&gt;)aView;
 {
-   // 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;)])
-   {
-      DLog(@&quot;Unexpected view class encountered.&quot;);
+   // We only support IRC connection.
+   if (!FiSHIsIRCConnection(connection))
       return NO;
+   
+   // Make sure that aView really is a direct chat panel.
+   JVDirectChatPanel *view = FiSHDirectChatPanelForChatViewController(aView);
+   if (!view)
+   {
+      DLog(@&quot;Ignoring unsupported chat controller type.&quot;);
+      return;
    }
-   // It's now safe to typecast view, to prevent compiler-warnings later.
-   JVDirectChatPanel *view = (JVDirectChatPanel *)aView;
+   
    
    // Check for correct command string.
    if ([command isEqualToString:FiSHKeyExchangeCommand])</diff>
      <filename>FiSHController.m</filename>
    </modified>
    <modified>
      <diff>@@ -23,6 +23,7 @@
 		D4A6B3840B8FB881001816EB /* FiSHEncryptionPrefs.m in Sources */ = {isa = PBXBuildFile; fileRef = D4A6B3830B8FB881001816EB /* FiSHEncryptionPrefs.m */; };
 		D4AFBC4A0B8A2F070086B38A /* NSData+FiSHyExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AFBC490B8A2F070086B38A /* NSData+FiSHyExtensions.m */; };
 		D4B700750B91F87900E6FF65 /* CHANGELOG.txt in Resources */ = {isa = PBXBuildFile; fileRef = D4B700740B91F87900E6FF65 /* CHANGELOG.txt */; };
+		D4B702160B9230E400E6FF65 /* FiSHMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = D4B702150B9230E400E6FF65 /* FiSHMisc.m */; };
 		D4BD4B080B8CD64A0016D3B9 /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = D4BD4B070B8CD64A0016D3B9 /* LICENSE.txt */; };
 		D4EF67C10B91CC900018A60E /* DLog.m in Sources */ = {isa = PBXBuildFile; fileRef = D4EF67C00B91CC900018A60E /* DLog.m */; };
 /* End PBXBuildFile section */
@@ -78,7 +79,7 @@
 		089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = &quot;&lt;absolute&gt;&quot;; };
 		1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = &quot;&lt;absolute&gt;&quot;; };
 		32DBCF630370AF2F00C91783 /* FiSHy_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FiSHy_Prefix.pch; sourceTree = &quot;&lt;group&gt;&quot;; };
-		8D5B49B6048680CD000E48DA /* FiSHy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FiSHy.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+		8D5B49B6048680CD000E48DA /* FiSHy.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FiSHy.plugin; sourceTree = BUILT_PRODUCTS_DIR; };
 		8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Info.plist; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = &quot;&lt;absolute&gt;&quot;; };
 		D4423DD70B84AE2B0039DCB9 /* FiSHBlowfish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FiSHBlowfish.h; sourceTree = &quot;&lt;group&gt;&quot;; };
@@ -102,6 +103,8 @@
 		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;; };
 		D4B700740B91F87900E6FF65 /* CHANGELOG.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG.txt; sourceTree = &quot;&lt;group&gt;&quot;; };
+		D4B702140B9230E400E6FF65 /* FiSHMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FiSHMisc.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+		D4B702150B9230E400E6FF65 /* FiSHMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FiSHMisc.m; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4BD4B070B8CD64A0016D3B9 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4D8D8930B824922000FA9F8 /* fish.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = fish.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
 		D4D8D8940B824922000FA9F8 /* fish.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = fish.h; sourceTree = &quot;&lt;group&gt;&quot;; };
@@ -185,6 +188,8 @@
 				D496F3DA0B80BDB60015B1F2 /* NSString+FiSHyExtensions.m */,
 				D4AFBC480B8A2F070086B38A /* NSData+FiSHyExtensions.h */,
 				D4AFBC490B8A2F070086B38A /* NSData+FiSHyExtensions.m */,
+				D4B702140B9230E400E6FF65 /* FiSHMisc.h */,
+				D4B702150B9230E400E6FF65 /* FiSHMisc.m */,
 			);
 			name = Classes;
 			sourceTree = &quot;&lt;group&gt;&quot;;
@@ -211,7 +216,7 @@
 		19C28FB8FE9D52D311CA2CBB /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				8D5B49B6048680CD000E48DA /* FiSHy.bundle */,
+				8D5B49B6048680CD000E48DA /* FiSHy.plugin */,
 			);
 			name = Products;
 			sourceTree = &quot;&lt;group&gt;&quot;;
@@ -291,7 +296,7 @@
 			name = FiSHy;
 			productInstallPath = &quot;$(HOME)/Library/Bundles&quot;;
 			productName = FiSHy;
-			productReference = 8D5B49B6048680CD000E48DA /* FiSHy.bundle */;
+			productReference = 8D5B49B6048680CD000E48DA /* FiSHy.plugin */;
 			productType = &quot;com.apple.product-type.bundle&quot;;
 		};
 /* End PBXNativeTarget section */
@@ -447,6 +452,7 @@
 				D4AFBC4A0B8A2F070086B38A /* NSData+FiSHyExtensions.m in Sources */,
 				D4A6B3840B8FB881001816EB /* FiSHEncryptionPrefs.m in Sources */,
 				D4EF67C10B91CC900018A60E /* DLog.m in Sources */,
+				D4B702160B9230E400E6FF65 /* FiSHMisc.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -475,8 +481,14 @@
 		1DEB913B08733D840010E9CD /* Development */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				BUNDLE_LOADER = &quot;${CONFIGURATION_BUILD_DIR}/Colloquy.app/Contents/MacOS/Colloquy&quot;;
 				COPY_PHASE_STRIP = NO;
 				DEPLOYMENT_LOCATION = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					&quot;$(inherited)&quot;,
+					&quot;$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)&quot;,
+				);
+				FRAMEWORK_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)/../colloquy/Frameworks\&quot;&quot;;
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_ENABLE_FIX_AND_CONTINUE = YES;
 				GCC_MODEL_TUNING = G5;
@@ -490,8 +502,9 @@
 					&quot;$(LIBRARY_SEARCH_PATHS_QUOTED_1)&quot;,
 				);
 				LIBRARY_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)\&quot;&quot;;
+				OTHER_LDFLAGS = &quot;&quot;;
 				PRODUCT_NAME = FiSHy;
-				WRAPPER_EXTENSION = bundle;
+				WRAPPER_EXTENSION = plugin;
 				ZERO_LINK = NO;
 			};
 			name = Development;
@@ -503,8 +516,14 @@
 					ppc,
 					i386,
 				);
+				BUNDLE_LOADER = &quot;${CONFIGURATION_BUILD_DIR}/Colloquy.app/Contents/MacOS/Colloquy&quot;;
 				DEPLOYMENT_LOCATION = NO;
 				DSTROOT = /tmp/FiSHy.dst;
+				FRAMEWORK_SEARCH_PATHS = (
+					&quot;$(inherited)&quot;,
+					&quot;$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)&quot;,
+				);
+				FRAMEWORK_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)/../colloquy/Frameworks\&quot;&quot;;
 				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
 				GCC_MODEL_TUNING = G5;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -516,8 +535,12 @@
 					&quot;$(LIBRARY_SEARCH_PATHS_QUOTED_1)&quot;,
 				);
 				LIBRARY_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)\&quot;&quot;;
+				OTHER_LDFLAGS = (
+					&quot;-undefined&quot;,
+					dynamic_lookup,
+				);
 				PRODUCT_NAME = FiSHy;
-				WRAPPER_EXTENSION = bundle;
+				WRAPPER_EXTENSION = plugin;
 			};
 			name = &quot;Release (Universal)&quot;;
 		};
@@ -526,6 +549,7 @@
 			buildSettings = {
 				APPLICATION_VERSION = 0.3.0b1;
 				COLLOQUY_SOURCE_FOLDER = ../colloquy;
+				DEBUG_INFORMATION_FORMAT = dwarf;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				HEADER_SEARCH_PATHS = (
@@ -544,6 +568,7 @@
 			buildSettings = {
 				APPLICATION_VERSION = 0.3.0b1;
 				COLLOQUY_SOURCE_FOLDER = ../colloquy;
+				DEBUG_INFORMATION_FORMAT = dwarf;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				HEADER_SEARCH_PATHS = (
@@ -563,8 +588,14 @@
 					ppc,
 					i386,
 				);
+				BUNDLE_LOADER = &quot;${CONFIGURATION_BUILD_DIR}/Colloquy.app/Contents/MacOS/Colloquy&quot;;
 				COPY_PHASE_STRIP = NO;
 				DEPLOYMENT_LOCATION = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					&quot;$(inherited)&quot;,
+					&quot;$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)&quot;,
+				);
+				FRAMEWORK_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)/../colloquy/Frameworks\&quot;&quot;;
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_ENABLE_FIX_AND_CONTINUE = YES;
 				GCC_MODEL_TUNING = G5;
@@ -578,8 +609,12 @@
 					&quot;$(LIBRARY_SEARCH_PATHS_QUOTED_1)&quot;,
 				);
 				LIBRARY_SEARCH_PATHS_QUOTED_1 = &quot;\&quot;$(SRCROOT)\&quot;&quot;;
+				OTHER_LDFLAGS = (
+					&quot;-undefined&quot;,
+					dynamic_lookup,
+				);
 				PRODUCT_NAME = FiSHy;
-				WRAPPER_EXTENSION = bundle;
+				WRAPPER_EXTENSION = plugin;
 				ZERO_LINK = NO;
 			};
 			name = &quot;Development (Universal)&quot;;
@@ -589,6 +624,7 @@
 			buildSettings = {
 				APPLICATION_VERSION = 0.3.0b1;
 				COLLOQUY_SOURCE_FOLDER = ../colloquy;
+				DEBUG_INFORMATION_FORMAT = dwarf;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				HEADER_SEARCH_PATHS = (</diff>
      <filename>FiSHy.xcodeproj/project.pbxproj</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>4f7f6a6d1eda32690188d77111d9241eba00f794</id>
    </parent>
  </parents>
  <author>
    <name>henningkiel</name>
    <email>henningkiel</email>
  </author>
  <url>http://github.com/hennk/fishy/commit/d5c67cb4bfe541ece49bb1699cb36e8d42d3446d</url>
  <id>d5c67cb4bfe541ece49bb1699cb36e8d42d3446d</id>
  <committed-date>2007-02-25T15:18:26-08:00</committed-date>
  <authored-date>2007-02-25T15:18:26-08:00</authored-date>
  <message>FiSHMisc:
Added FiSHMisc as a central place to store common code. Right now, functions for casting JVChatViewController-conformant objects to JVDirectChatPanel (if possible), for getting the nickname/name of users/rooms, and for checking if a connection is IRC, are implemented. This saves a lot of duplicate code in FiSHController.


FiSHController:
Completly restructured processIncomingMessage:inView:. We now mark messages sent with encryption overridden as such.

We now check for each plugin method if we are working on a IRC connection, as this is the only type we support.


FiSHBlowfish:
Made encodeData: returning void, as the underlying dirt-functions always return success.

Project:
Set &quot;Bundle Loader&quot; to the Colloquy app. This way, we can at least use symbols from Colloquy. Symbols from Chat Core are still a no-go.</message>
  <tree>debbfe720569e62fb5c9aa0ed8de637bf5a85b32</tree>
  <committer>
    <name>henningkiel</name>
    <email>henningkiel</email>
  </committer>
</commit>
