adium/adium
Clone
Summary
Browse
Changes
Graph
1.5.10.3b1
adium-1.5.10.3
2017-03-23, Evan D. Schoenberg, M.D.
6d760b7b1d69
1.5.10.3b1
/*
* Adium is the legal property of its developers, whose names are listed in the copyright file included
* with this source distribution.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import "ESSecureMessagingPlugin.h"
#import "AdiumOTREncryption.h"
#import <Adium/AIChatControllerProtocol.h>
#import <Adium/AIContentControllerProtocol.h>
#import <Adium/AIInterfaceControllerProtocol.h>
#import <Adium/AIMenuControllerProtocol.h>
#import <Adium/AIToolbarControllerProtocol.h>
#import <AIUtilities/AIMenuAdditions.h>
#import <AIUtilities/AIToolbarUtilities.h>
#import <AIUtilities/AIImageAdditions.h>
#import <AIUtilities/AIImageAdditions.h>
#import <AIUtilities/MVMenuButton.h>
#import <AIUtilities/AIStringAdditions.h>
#import <Adium/AIAccount.h>
#import <Adium/AIChat.h>
#import <Adium/AIListContact.h>
#define TITLE_MAKE_SECURE AILocalizedString(@"Initiate Encrypted OTR Chat",nil)
#define TITLE_MAKE_INSECURE AILocalizedString(@"Cancel Encrypted Chat",nil)
#define TITLE_SHOW_DETAILS [AILocalizedString(@"Show Details",nil) stringByAppendingEllipsis]
#define TITLE_VERIFY [AILocalizedString(@"Verify",nil) stringByAppendingEllipsis]
#define TITLE_ENCRYPTION_OPTIONS AILocalizedString(@"Encryption Settings",nil)
#define TITLE_ABOUT_ENCRYPTION [AILocalizedString(@"About Encryption",nil) stringByAppendingEllipsis]
#define TITLE_ENCRYPTION AILocalizedString(@"Encryption",nil)
#define CHAT_NOW_SECURE AILocalizedString(@"Encrypted OTR chat initiated.", nil)
#define CHAT_NOW_SECURE_UNVERIFIED AILocalizedString(@"Encrypted OTR chat initiated. %@'s identity not verified.", nil)
#define CHAT_NO_LONGER_SECURE AILocalizedString(@"Ended encrypted OTR chat.", nil)
@interface
ESSecureMessagingPlugin
()
-
(
void
)
configureMenuItems
;
-
(
void
)
registerToolbarItem
;
-
(
NSMenu
*
)
_secureMessagingMenu
;
-
(
void
)
_updateToolbarIconOfChat:
(
AIChat
*
)
inChat
inWindow:
(
NSWindow
*
)
window
;
-
(
void
)
_updateToolbarItem:
(
NSToolbarItem
*
)
item
forChat:
(
AIChat
*
)
chat
;
-
(
void
)
toolbarDidAddItem:
(
NSToolbarItem
*
)
item
;
-
(
IBAction
)
toggleSecureMessaging:
(
id
)
sender
;
-
(
void
)
chatDidBecomeVisible:
(
NSNotification
*
)
notification
;
-
(
void
)
dummyAction:
(
id
)
sender
;
@end
@implementation
ESSecureMessagingPlugin
-
(
void
)
installPlugin
{
//Muy imporatante: Set OTR as our encryption method
[
adium
.
contentController
setEncryptor
:
[[[
AdiumOTREncryption
alloc
]
init
]
autorelease
]];
_secureMessagingMenu
=
nil
;
lockImage_Locked
=
[[
NSImage
imageNamed
:
@"lock-locked"
forClass
:
[
self
class
]]
retain
];
lockImage_Unlocked
=
[[
NSImage
imageNamed
:
@"lock-unlocked"
forClass
:
[
self
class
]]
retain
];
[
self
registerToolbarItem
];
[
self
configureMenuItems
];
[
adium
.
chatController
registerChatObserver
:
self
];
}
-
(
void
)
uninstallPlugin
{
[
adium
.
chatController
unregisterChatObserver
:
self
];
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
];
}
-
(
void
)
configureMenuItems
{
NSMenu
*
menu
=
[
self
_secureMessagingMenu
];
//Add menu to toolbar item (for text mode)
menuItem_encryption
=
[[
NSMenuItem
allocWithZone
:
[
NSMenu
menuZone
]]
initWithTitle
:
AILocalizedString
(
@"Encryption"
,
nil
)
target
:
self
action
:
@selector
(
dummyAction
:
)
keyEquivalent
:
@""
];
[
menuItem_encryption
setSubmenu
:
menu
];
[
menuItem_encryption
setTag
:
AISecureMessagingMenu_Root
];
[
adium
.
menuController
addMenuItem
:
menuItem_encryption
toLocation
:
LOC_Contact_Additions
];
menuItem_encryptionContext
=
[
menuItem_encryption
copy
];
[
adium
.
menuController
addContextualMenuItem
:
menuItem_encryptionContext
toLocation
:
Context_Contact_ChatAction
];
}
-
(
void
)
registerToolbarItem
{
toolbarItems
=
[[
NSMutableSet
alloc
]
init
];
//Toolbar item registration
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
toolbarWillAddItem
:
)
name
:
NSToolbarWillAddItemNotification
object
:
nil
];
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
toolbarDidRemoveItem
:
)
name
:
NSToolbarDidRemoveItemNotification
object
:
nil
];
//Register our toolbar item
NSToolbarItem
*
toolbarItem
;
MVMenuButton
*
button
;
button
=
[[[
MVMenuButton
alloc
]
initWithFrame
:
NSMakeRect
(
0
,
0
,
32
,
32
)]
autorelease
];
[
button
setImage
:
lockImage_Locked
];
toolbarItem
=
[
AIToolbarUtilities
toolbarItemWithIdentifier
:
@"Encryption"
label
:
TITLE_ENCRYPTION
paletteLabel
:
AILocalizedString
(
@"Encrypted Messaging"
,
nil
)
toolTip
:
AILocalizedString
(
@"Toggle encrypted messaging. Shows a closed lock when secure and an open lock when insecure."
,
nil
)
target
:
self
settingSelector
:
@selector
(
setView
:
)
itemContent
:
button
action
:
@selector
(
toggleSecureMessaging
:
)
menu
:
nil
];
[
toolbarItem
setMinSize
:
NSMakeSize
(
32
,
32
)];
[
toolbarItem
setMaxSize
:
NSMakeSize
(
32
,
32
)];
[
button
setToolbarItem
:
toolbarItem
];
//Register our toolbar item
[
adium
.
toolbarController
registerToolbarItem
:
toolbarItem
forToolbarType
:
@"MessageWindow"
];
}
//After the toolbar has added the item we can set up the submenus
-
(
void
)
toolbarWillAddItem:
(
NSNotification
*
)
notification
{
NSToolbarItem
*
item
=
[[
notification
userInfo
]
objectForKey
:
@"item"
];
if
([[
item
itemIdentifier
]
isEqualToString
:
@"Encryption"
])
{
[
item
setEnabled
:
YES
];
//If this is the first item added, start observing for chats becoming visible so we can update the icon
if
([
toolbarItems
count
]
==
0
)
{
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
chatDidBecomeVisible
:
)
name
:
@"AIChatDidBecomeVisible"
object
:
nil
];
}
NSMenu
*
menu
=
[
self
_secureMessagingMenu
];
//Add menu to view
[[
item
view
]
setMenu
:
menu
];
//Add menu to toolbar item (for text mode)
NSMenuItem
*
mItem
=
[[[
NSMenuItem
allocWithZone
:
[
NSMenu
menuZone
]]
init
]
autorelease
];
[
mItem
setSubmenu
:
menu
];
[
mItem
setTitle
:
[
menu
title
]];
[
item
setMenuFormRepresentation
:
mItem
];
[
toolbarItems
addObject
:
item
];
[
self
performSelector
:
@selector
(
toolbarDidAddItem
:
)
withObject
:
item
afterDelay
:
0
];
}
}
-
(
void
)
toolbarDidAddItem:
(
NSToolbarItem
*
)
item
{
/* Only need to take action if we haven't already validated the initial state of this item.
* This will only be true when the toolbar is revealed for the first time having been hidden when window opened.
*/
if
(
!
[
validatedItems
containsObject
:
item
])
{
NSEnumerator
*
enumerator
=
[[
NSApp
windows
]
objectEnumerator
];
NSWindow
*
window
;
NSToolbar
*
thisItemsToolbar
=
[
item
toolbar
];
//Look at each window to find the toolbar we are in
while
((
window
=
[
enumerator
nextObject
]))
{
if
([
window
toolbar
]
==
thisItemsToolbar
)
break
;
}
if
(
window
)
{
[
self
_updateToolbarItem
:
item
forChat
:[
adium
.
interfaceController
activeChatInWindow
:
window
]];
}
}
}
-
(
void
)
toolbarDidRemoveItem:
(
NSNotification
*
)
notification
{
NSToolbarItem
*
item
=
[[
notification
userInfo
]
objectForKey
:
@"item"
];
if
([
toolbarItems
containsObject
:
item
])
{
[
item
setView
:
nil
];
[
toolbarItems
removeObject
:
item
];
[
validatedItems
removeObject
:
item
];
if
([
toolbarItems
count
]
==
0
)
{
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
name
:
@"AIChatDidBecomeVisible"
object
:
nil
];
}
}
}
//A chat became visible in a window. Update the item with the @"Encryption" identifier to show the IsSecure state for this chat
-
(
void
)
chatDidBecomeVisible:
(
NSNotification
*
)
notification
{
[
self
_updateToolbarIconOfChat
:
[
notification
object
]
inWindow
:[[
notification
userInfo
]
objectForKey
:
@"NSWindow"
]];
}
//When the IsSecure key of a chat changes, update the @"Encryption" item immediately
-
(
NSSet
*
)
updateChat:
(
AIChat
*
)
inChat
keys:
(
NSSet
*
)
inModifiedKeys
silent:
(
BOOL
)
silent
{
if
([
inModifiedKeys
containsObject
:
@"securityDetails"
])
{
[
self
_updateToolbarIconOfChat
:
inChat
inWindow
:[
adium
.
interfaceController
windowForChat
:
inChat
]];
/* Add a status message to the chat */
BOOL
chatIsSecure
=
[
inChat
isSecure
];
if
(
chatIsSecure
!=
[
inChat
boolValueForProperty
:
@"secureMessagingLastEncryptedState"
])
{
NSString
*
message
;
NSString
*
type
;
[
inChat
setValue
:
[
NSNumber
numberWithBool
:
chatIsSecure
]
forProperty
:
@"secureMessagingLastEncryptedState"
notify
:
NotifyNever
];
if
(
chatIsSecure
)
{
if
([
inChat
encryptionStatus
]
==
EncryptionStatus_Unverified
)
{
AIListObject
*
listObject
=
[
inChat
listObject
];
NSString
*
displayName
=
(
listObject
?
listObject
.
formattedUID
:
inChat
.
displayName
);
message
=
[
NSString
stringWithFormat
:
CHAT_NOW_SECURE_UNVERIFIED
,
displayName
];
type
=
@"encryptionStartedUnverified"
;
}
else
{
message
=
CHAT_NOW_SECURE
;
type
=
@"encryptionStarted"
;
}
}
else
{
message
=
CHAT_NO_LONGER_SECURE
;
type
=
@"encryptionEnded"
;
}
if
([
inChat
isOpen
])
{
[
adium
.
contentController
displayEvent
:
message
ofType
:
type
inChat
:
inChat
];
}
}
}
return
nil
;
}
-
(
void
)
_updateToolbarItem:
(
NSToolbarItem
*
)
item
forChat:
(
AIChat
*
)
chat
{
NSImage
*
image
;
if
([
chat
isSecure
])
{
image
=
lockImage_Locked
;
}
else
{
image
=
lockImage_Unlocked
;
}
[
item
setEnabled
:
[
chat
supportsSecureMessagingToggling
]];
[(
MVMenuButton
*
)[
item
view
]
setImage
:
image
];
[
validatedItems
addObject
:
item
];
}
-
(
void
)
_updateToolbarIconOfChat:
(
AIChat
*
)
chat
inWindow:
(
NSWindow
*
)
window
{
NSToolbar
*
toolbar
=
[
window
toolbar
];
NSEnumerator
*
enumerator
=
[[
toolbar
items
]
objectEnumerator
];
NSToolbarItem
*
item
;
while
((
item
=
[
enumerator
nextObject
]))
{
if
([[
item
itemIdentifier
]
isEqualToString
:
@"Encryption"
])
{
[
self
_updateToolbarItem
:
item
forChat
:
chat
];
break
;
}
}
}
-
(
IBAction
)
toggleSecureMessaging:
(
id
)
sender
{
AIChat
*
chat
=
adium
.
interfaceController
.
activeChat
;
[
chat
.
account
requestSecureMessaging
:!
chat
.
isSecure
inChat
:
chat
];
}
-
(
IBAction
)
showDetails:
(
id
)
sender
{
NSRunInformationalAlertPanel
(
AILocalizedString
(
@"Details"
,
nil
),
[[
adium
.
interfaceController
.
activeChat
securityDetails
]
objectForKey
:
@"Description"
],
AILocalizedString
(
@"OK"
,
nil
),
nil
,
nil
);
}
-
(
IBAction
)
verify:
(
id
)
sender
{
AIChat
*
chat
=
adium
.
interfaceController
.
activeChat
;
[
chat
.
account
promptToVerifyEncryptionIdentityInChat
:
chat
];
}
-
(
IBAction
)
showAbout:
(
id
)
sender
{
NSString
*
aboutEncryption
;
aboutEncryption
=
adium
.
interfaceController
.
activeChat
.
account
.
aboutEncryption
;
if
(
aboutEncryption
)
{
NSRunInformationalAlertPanel
(
AILocalizedString
(
@"About Encryption"
,
nil
),
aboutEncryption
,
AILocalizedString
(
@"OK"
,
nil
),
nil
,
nil
);
}
}
-
(
IBAction
)
selectedEncryptionPreference:
(
id
)
sender
{
AIListContact
*
listContact
=
adium
.
interfaceController
.
activeChat
.
listObject
.
parentContact
;
[
listContact
setPreference
:
[
NSNumber
numberWithInteger
:
[
sender
tag
]]
forKey
:
KEY_ENCRYPTED_CHAT_PREFERENCE
group
:
GROUP_ENCRYPTION
];
}
//Disable the insertion if a text field is not active
-
(
BOOL
)
validateMenuItem:
(
NSMenuItem
*
)
menuItem
{
AIChat
*
chat
;
if
(
menuItem
==
menuItem_encryptionContext
)
{
chat
=
adium
.
menuController
.
currentContextMenuChat
;
}
else
{
chat
=
adium
.
interfaceController
.
activeChat
;
}
if
(
!
chat
)
return
NO
;
if
([[[
menuItem
menu
]
title
]
isEqualToString
:
ENCRYPTION_MENU_TITLE
])
{
/* Options submenu */
AIEncryptedChatPreference
tag
=
(
AIEncryptedChatPreference
)[
menuItem
tag
];
AIListContact
*
listContact
=
chat
.
listObject
.
parentContact
;
AIEncryptedChatPreference
userPreference
=
[[
listContact
preferenceForKey
:
KEY_ENCRYPTED_CHAT_PREFERENCE
group
:
GROUP_ENCRYPTION
]
intValue
];
switch
(
tag
)
{
case
EncryptedChat_Default
:
{
if
(
listContact
)
{
//Set the state (checked or unchecked) as appropriate. Default = no pref or the actual 'default' value.
[
menuItem
setState
:
(
tag
==
userPreference
||
!
[
listContact
preferenceForKey
:
KEY_ENCRYPTED_CHAT_PREFERENCE
group
:
GROUP_ENCRYPTION
])];
}
return
YES
;
break
;
}
case
EncryptedChat_Never
:
case
EncryptedChat_Manually
:
case
EncryptedChat_Automatically
:
case
EncryptedChat_RejectUnencryptedMessages
:
{
if
(
listContact
)
{
//Set the state (checked or unchecked) as appropriate
[
menuItem
setState
:
(
tag
==
userPreference
)];
}
return
YES
;
break
;
}
}
}
else
{
/* Items on the main menu */
AISecureMessagingMenuTag
tag
=
(
AISecureMessagingMenuTag
)[
menuItem
tag
];
switch
(
tag
)
{
case
AISecureMessagingMenu_Root
:
return
[
chat
supportsSecureMessagingToggling
];
break
;
case
AISecureMessagingMenu_Toggle
:
// The menu item should indicate what will happen if it is selected.. the opposite of our secure state
if
([
chat
isSecure
])
{
[
menuItem
setTitle
:
TITLE_MAKE_INSECURE
];
}
else
{
[
menuItem
setTitle
:
TITLE_MAKE_SECURE
];
AIListContact
*
listContact
=
chat
.
listObject
.
parentContact
;
AIEncryptedChatPreference
userPreference
=
[[
listContact
preferenceForKey
:
KEY_ENCRYPTED_CHAT_PREFERENCE
group
:
GROUP_ENCRYPTION
]
intValue
];
// Disable 'Initiate Encrypted OTR Chat' menu item if chat encryption is disabled
if
(
userPreference
==
EncryptedChat_Never
)
{
return
NO
;
}
}
return
YES
;
break
;
case
AISecureMessagingMenu_ShowDetails
:
case
AISecureMessagingMenu_Verify
:
//Only enable show details if the chat is secure
return
[
chat
isSecure
];
break
;
case
AISecureMessagingMenu_Options
:
//Only enable options if the chat is with a single person
return
([
chat
supportsSecureMessagingToggling
]
&&
chat
.
listObject
&&
!
chat
.
isGroupChat
);
break
;
case
AISecureMessagingMenu_ShowAbout
:
return
[
chat
supportsSecureMessagingToggling
];
break
;
}
}
return
YES
;
}
-
(
NSMenu
*
)
_secureMessagingMenu
{
if
(
!
_secureMessagingMenu
)
{
NSMenuItem
*
item
;
_secureMessagingMenu
=
[[
NSMenu
allocWithZone
:
[
NSMenu
menuZone
]]
init
];
[
_secureMessagingMenu
setTitle
:
TITLE_ENCRYPTION
];
item
=
[[[
NSMenuItem
alloc
]
initWithTitle
:
TITLE_MAKE_SECURE
target
:
self
action
:
@selector
(
toggleSecureMessaging
:
)
keyEquivalent
:
@""
]
autorelease
];
[
item
setTag
:
AISecureMessagingMenu_Toggle
];
[
_secureMessagingMenu
addItem
:
item
];
item
=
[[[
NSMenuItem
alloc
]
initWithTitle
:
TITLE_SHOW_DETAILS
target
:
self
action
:
@selector
(
showDetails
:
)
keyEquivalent
:
@""
]
autorelease
];
[
item
setTag
:
AISecureMessagingMenu_ShowDetails
];
[
_secureMessagingMenu
addItem
:
item
];
item
=
[[[
NSMenuItem
alloc
]
initWithTitle
:
TITLE_VERIFY
target
:
self
action
:
@selector
(
verify
:
)
keyEquivalent
:
@""
]
autorelease
];
[
item
setTag
:
AISecureMessagingMenu_Verify
];
[
_secureMessagingMenu
addItem
:
item
];
item
=
[[[
NSMenuItem
alloc
]
initWithTitle
:
TITLE_ENCRYPTION_OPTIONS
target
:
nil
action
:
nil
keyEquivalent
:
@""
]
autorelease
];
[
item
setTag
:
AISecureMessagingMenu_Options
];
[
item
setSubmenu
:
[
adium
.
contentController
encryptionMenuNotifyingTarget
:
self
withDefault
:
YES
]];
[
_secureMessagingMenu
addItem
:
item
];
[
_secureMessagingMenu
addItem
:
[
NSMenuItem
separatorItem
]];
item
=
[[[
NSMenuItem
alloc
]
initWithTitle
:
TITLE_ABOUT_ENCRYPTION
target
:
self
action
:
@selector
(
showAbout
:
)
keyEquivalent
:
@""
]
autorelease
];
[
item
setTag
:
AISecureMessagingMenu_ShowAbout
];
[
_secureMessagingMenu
addItem
:
item
];
}
return
[[
_secureMessagingMenu
copy
]
autorelease
];
}
-
(
void
)
dummyAction:
(
id
)
sender
{};
@end