diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5bfaf9e..f04192f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +- Added support for decrypting encrypted topics. As of now no visible feedback exists, though. +- Added /encTopic command to set an encrypted topic. +- Added /aboutFiSHy command. + 0.3.0 2007/03/15 - Made FiSHy localizable. - Fixes a crash after a key-exchange timed out. diff --git a/FiSHController.m b/FiSHController.m index 5f818d1..38e5fe0 100644 --- a/FiSHController.m +++ b/FiSHController.m @@ -22,7 +22,9 @@ #import "Controllers/JVChatController.h" #import "Panels/JVChatRoomPanel.h" #import "Panels/JVDirectChatPanel.h" +#import "Panels/JVChatRoomPanel.h" #import "Chat Core/MVChatUser.h" +#import "Chat Core/MVChatRoom.h" #import "Chat Core/MVChatConnection.h" #import "Additions/NSStringAdditions.h" #import "Models/JVChatTranscript.h" @@ -53,7 +55,8 @@ NSString *FiSHOverrideEncCommand = @"+p"; // Command to print out FiSHys version. NSString *FiSHAboutFiSHyCommand = @"aboutfishy"; - +// Command to set an encrypted topic. +NSString *FiSHSetEncryptedTopicCommand = @"encTopic"; @interface FiSHController (FiSHyPrivate) @@ -62,7 +65,9 @@ - (void) joinedDirectChat:(JVDirectChatPanel *)directChat; @implementation FiSHController + #pragma mark MVChatPlugin + - (id)initWithManager:(MVChatPluginManager *) manager; { if (self = [super init]) @@ -82,7 +87,7 @@ - (id)initWithManager:(MVChatPluginManager *) manager; encPrefs_ = [[FiSHEncryptionPrefs alloc] init]; - // Add encryption setting notices to rooms and queries already open when we were loaded. + // Add encryption setting notices to rooms and queries already open when we were loaded, and try to decrypt topics. NSSet *openChatPanels = [[NSClassFromString(@"JVChatController") defaultController] chatViewControllersKindOfClass:NSClassFromString(@"JVDirectChatPanel")]; NSEnumerator *chatPanelsEnum = [openChatPanels objectEnumerator]; JVDirectChatPanel *aChatPanel = nil; @@ -142,6 +147,7 @@ - (void)keyExchanger:(FiSHKeyExchanger *)keyExchanger finishedKeyExchangeFor:(NS } } + #pragma mark MVIRCChatConnectionPlugin - (void) processIncomingMessageAsData:(NSMutableData *) message from:(MVChatUser *) sender to:(id) receiver attributes:(NSMutableDictionary *)msgAttributes; @@ -268,6 +274,44 @@ - (void) processOutgoingMessageAsData:(NSMutableData *) message to:(id) receiver return; } +- (void) processTopicAsData:(NSMutableData *) topic inRoom:(MVChatRoom *) room author:(MVChatUser *) author; +{ + // We only support IRC connection. + if (!FiSHIsIRCConnection([room connection])) + return; + + + // Get the secret for the room and connection. If we don't have one, just display the topic without changes. + NSString *accountName = FiSHNameForChatObject(room); + NSString *secret = nil; + // TODO: Handle service/connection correctly. + secret = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:accountName]; + if (!secret) + return; + + [room setAttribute:[NSNumber numberWithBool:NO] forKey:@"decryptedTopic"]; + + // Try to decrypt the raw encrypted topic. + NSData *decryptedTopicData = nil; + FiSHCypherResult decryptionResult = [blowFisher_ decodeData:topic intoData:&decryptedTopicData key:secret]; + switch (decryptionResult) + { + case FiSHCypherTextCut: + case FiSHCypherSuccess: + [topic setData:decryptedTopicData]; + [room setAttribute:[NSNumber numberWithBool:YES] forKey:@"decryptedTopic"]; + case FiSHCypherBadCharacters: + case FiSHCypherUnknownError: + case FiSHCypherPlainText: + [room setAttribute:[NSNumber numberWithInt:decryptionResult] forKey:@"FiSHyResult"]; + break; + default: + DLog(@"Unexpected/unknown blowfish result."); + break; + } +} + + #pragma mark MVChatPluginDirectChatSupport /// Called whenever a message gets added to a chat panel. @@ -362,17 +406,43 @@ - (void)processOutgoingMessage:(JVMutableChatMessage *)message inView:(id 2) + { + DLog(@"encTopic expects exactly 2 arguments, aborting."); + return YES; + } else if ([argumentList count] == 1) + { + // If only one argument has been given, use that as the new topic, and try to deduce the room from the current view. + room = [view target]; + if (!room || ![room isKindOfClass:NSClassFromString(@"MVChatRoom")]) + { + DLog(@"encTopic expects exactly 2 arguments, aborting."); + return YES; + } + newTopic = [argumentList objectAtIndex:0]; + } else if ([argumentList count] == 2) + { + room = [connection joinedChatRoomWithName:[argumentList objectAtIndex:0]]; + if (!room) + { + DLog(@"encTopic invoked for unknown room. You have to be in a room to set an encrypted topic."); + return YES; + } + newTopic = [argumentList objectAtIndex:1]; + } + + // Get the key for the room. + // Check if we have a secret for the current room/nick. + // TODO: Handle service/connection correctly. + NSString *theKey = [[FiSHSecretStore sharedSecretStore] secretForService:nil account:FiSHNameForChatObject(room)]; + if (!theKey) + { + NSString *errorMessage = NSLocalizedString(@"Trying to set a topic for a room without a key. The topic was not set.", @"Trying to set a topic for a room without a key. The topic was not set."); + DLog(errorMessage); + [view addEventMessageToDisplay:errorMessage withName:@"FiSHy" andAttributes:nil]; + + return YES; + } + + // Everything's fine, encrypt the topic. + NSData *newTopicData = nil; + [blowFisher_ encodeData:[newTopic dataUsingEncoding:[room encoding]] intoData:&newTopicData key:theKey]; + newTopic = [[[NSString alloc] initWithData:newTopicData encoding:NSASCIIStringEncoding] autorelease]; + + [room setTopic:[[[NSAttributedString alloc] initWithString:newTopic] autorelease]]; + + return YES; +} + + #pragma mark MVChatPluginCommandSupport /// Process user commands. @@ -543,6 +672,8 @@ - (BOOL)processUserCommand:(NSString *) command withArguments:(NSAttributedStrin return [self processEncryptionPreferenceCommandWithArguments:arguments toConnection:connection inDirectChatPanel:view pref:FiSHEncPrefPreferEncrypted]; if ([command isCaseInsensitiveEqualToString:FiSHAvoidEncCommand]) return [self processEncryptionPreferenceCommandWithArguments:arguments toConnection:connection inDirectChatPanel:view pref:FiSHEncPrefAvoidEncrypted]; + if ([command isCaseInsensitiveEqualToString:FiSHSetEncryptedTopicCommand]) + return [self processSetEncryptedTopicCommandWithArguments:arguments toConnection:connection inDirectChatPanel:view]; if ([command isCaseInsensitiveEqualToString:FiSHAboutFiSHyCommand]) { [self showAboutWindow:aView]; diff --git a/READ ME.rtf b/READ ME.rtf index 212818d..a0627bf 100644 --- a/READ ME.rtf +++ b/READ ME.rtf @@ -53,6 +53,8 @@ To install, delete any prior versions of FiSHy, then simply drag the FiSHy plugi /setkey [] Sets the encryption key\ /keyx [] Initiates a key-exchange\ /+p Sends message unencrypted\ +/encTopic [] Sets an encrypted topic\ +/aboutFiSHy Shows license and version\ \ \ @@ -85,6 +87,12 @@ If a user changes its nickname, you will have to exchange keys again, as FiSHy c To force sending a message unencrypted to a room/nickname for which encryption is enabled, use the /+p command. This command will send everything written after it in clear-text.\ /+p \ \ +To set an encrypted topic, use the /encTopic command.\ + /encTopic [] \ +If no channel has been supplied, it tries to deduce it from the currently shown channel. If no key has been set for the channel, this command will do nothing. Currently FiSHy does not provide visual feedback for encrypted topics.\ +\ +To see a small window containing the version and license of FiSHy, use the /aboutFiSHy command.\ +\ \ \f0\b\fs28 De-installation