From 3189b4969c7ee2bbec0b69b7400535f2de58c8f8 Mon Sep 17 00:00:00 2001 From: paweldomas Date: Mon, 25 May 2015 21:50:35 +0200 Subject: [PATCH] Restores OTR plugin version to 7f9f9ab73cb4fad7dcac16f1061b2e8baeb1e823 from 2014-01-02 --- lib/installer-exclude/otr4j.jar | Bin 94429 -> 86200 bytes .../communicator/plugin/otr/OtrActivator.java | 46 +- .../plugin/otr/OtrConfigurator.java | 27 - .../plugin/otr/OtrContactManager.java | 180 ------ .../plugin/otr/OtrContactMenu.java | 118 ++-- .../plugin/otr/OtrMetaContactButton.java | 123 +---- .../plugin/otr/OtrMetaContactMenu.java | 50 +- .../plugin/otr/OtrTransformLayer.java | 33 +- .../plugin/otr/OtrWeakListener.java | 29 +- .../communicator/plugin/otr/ScOtrEngine.java | 103 +--- .../plugin/otr/ScOtrEngineImpl.java | 513 +++++------------- .../plugin/otr/ScOtrEngineListener.java | 7 +- .../plugin/otr/ScOtrKeyManager.java | 18 +- .../plugin/otr/ScOtrKeyManagerImpl.java | 149 ++--- .../plugin/otr/ScOtrKeyManagerListener.java | 4 +- .../plugin/otr/SwingOtrActionHandler.java | 12 +- .../FingerprintAuthenticationPanel.java | 42 +- .../authdialog/KnownFingerprintsPanel.java | 48 +- .../KnownFingerprintsTableModel.java | 96 +--- .../OTRv3OutgoingSessionSwitcher.java | 438 --------------- .../OtrBuddyAuthenticationDialog.java | 19 +- .../otr/authdialog/OtrConfigurationPanel.java | 16 +- .../SmpAuthenticateBuddyDialog.java | 24 +- .../communicator/plugin/otr/otr.manifest.mf | 3 +- 24 files changed, 385 insertions(+), 1713 deletions(-) delete mode 100644 src/net/java/sip/communicator/plugin/otr/OtrContactManager.java delete mode 100644 src/net/java/sip/communicator/plugin/otr/authdialog/OTRv3OutgoingSessionSwitcher.java diff --git a/lib/installer-exclude/otr4j.jar b/lib/installer-exclude/otr4j.jar index 06dc4a7b1498b0d73649c586d793315a009e0292..fc229151d708df24d7da3207e46ef1145d196396 100644 GIT binary patch delta 43127 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 TOfeWSn0Dx6Y)u&Y)PwmyFw}-3 delta 51404 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 diff --git a/src/net/java/sip/communicator/plugin/otr/OtrActivator.java b/src/net/java/sip/communicator/plugin/otr/OtrActivator.java index d4745249f..ae56e3907 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrActivator.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrActivator.java @@ -8,15 +8,13 @@ import java.util.*; -import net.java.sip.communicator.plugin.otr.authdialog.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.msghistory.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.Logger; +import net.java.sip.communicator.util.Logger; import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; import org.jitsi.util.*; @@ -30,6 +28,12 @@ public class OtrActivator extends AbstractServiceDependentActivator implements ServiceListener { + /** + * A property specifying whether private messaging should be automatically + * initiated. + */ + public static final String AUTO_INIT_OTR_PROP = + "net.java.sip.communicator.plugin.otr.AUTO_INIT_PRIVATE_MESSAGING"; /** * The {@link BundleContext} of the {@link OtrActivator}. @@ -96,11 +100,6 @@ public class OtrActivator * The MetaContactListService reference. */ private static MetaContactListService metaCListService; - - /** - * The message history service. - */ - private static MessageHistoryService messageHistoryService; /** * Gets an {@link AccountID} by its UID. @@ -308,7 +307,6 @@ public void start(Object dependentService) // Register Transformation Layer bundleContext.addServiceListener(this); bundleContext.addServiceListener((ServiceListener) scOtrEngine); - bundleContext.addServiceListener(new OtrContactManager()); ServiceReference[] protocolProviderRefs = ServiceUtils.getServiceReferences( @@ -372,20 +370,6 @@ public void start(Object dependentService) bundleContext.registerService( OtrActionHandler.class.getName(), new SwingOtrActionHandler(), null); - - containerFilter.put(Container.CONTAINER_ID, - Container.CONTAINER_CHAT_WRITE_PANEL.getID()); - bundleContext.registerService( - PluginComponentFactory.class.getName(), - new PluginComponentFactory( Container.CONTAINER_CHAT_WRITE_PANEL) - { - protected PluginComponent getPluginInstance() - { - return new OTRv3OutgoingSessionSwitcher( - getContainer(), this); - } - }, - containerFilter); } // If the general configuration form is disabled don't register it. @@ -400,8 +384,7 @@ protected PluginComponent getPluginInstance() // Register the configuration form. bundleContext.registerService(ConfigurationForm.class.getName(), new LazyConfigurationForm( - "net.java.sip.communicator.plugin.otr.authdialog." + - "OtrConfigurationPanel", + "net.java.sip.communicator.plugin.otr.OtrConfigurationPanel", getClass().getClassLoader(), "plugin.otr.configform.ICON", "service.gui.CHAT", 1), @@ -465,19 +448,6 @@ public static MetaContactListService getContactListService() return metaCListService; } - /** - * Gets the service giving access to message history. - * - * @return the service giving access to message history. - */ - public static MessageHistoryService getMessageHistoryService() - { - if (messageHistoryService == null) - messageHistoryService = ServiceUtils.getService(bundleContext, - MessageHistoryService.class); - return messageHistoryService; - } - /** * The factory that will be registered in OSGi and will create * otr menu instances. diff --git a/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java b/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java index a4c47e4cf..8a1bf58f7 100755 --- a/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java @@ -144,31 +144,4 @@ public int getPropertyInt(String id, int defaultValue) { return OtrActivator.configService.getInt(getID(id), defaultValue); } - - /** - * Appends value to the old value of the property with the - * specified name. The two values will be comma separated. - * - * @param id the name of the property to append to - * @param value the value to append - */ - public void appendProperty(String id, Object value) - { - Object oldValue = OtrActivator.configService.getProperty(getID(id)); - - String newValue = - oldValue == null ? value.toString() : oldValue + "," + value; - - setProperty(id, newValue); - } - - public List getAppendedProperties(String id) - { - String listProperties = - (String) OtrActivator.configService.getProperty(getID(id)); - - if (listProperties == null) return new ArrayList(); - - return Arrays.asList(listProperties.split(",")); - } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrContactManager.java b/src/net/java/sip/communicator/plugin/otr/OtrContactManager.java deleted file mode 100644 index 789c8df86..000000000 --- a/src/net/java/sip/communicator/plugin/otr/OtrContactManager.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -package net.java.sip.communicator.plugin.otr; - -import java.util.*; -import java.util.concurrent.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.osgi.framework.*; - -/** - * The OtrContactManager is used for accessing OtrContacts in a static - * way. - * - * The OtrContact class is just a wrapper of [Contact, ContactResource] - * pairs. Its purpose is for the otr plugin to be able to create different - * Sessions for every ContactResource that a Contact has. - * - * Currently, only the Jabber protocol supports ContactResources. - * - * @author Marin Dzhigarov - * - */ -public class OtrContactManager implements ServiceListener -{ - - /** - * The logger - */ - private final Logger logger = Logger.getLogger(OtrContactManager.class); - - /** - * A map that caches OtrContacts to minimize memory usage. - */ - private static final Map> contactsMap = - new ConcurrentHashMap>(); - - /** - * The OtrContact class is just a wrapper of - * [Contact, ContactResource] pairs. Its purpose is for the otr plugin to be - * able to create different Sessions for every ContactResource that - * a Contact has. - * - * @author Marin Dzhigarov - * - */ - public static class OtrContact - { - public final Contact contact; - - public final ContactResource resource; - - private OtrContact(Contact contact, ContactResource resource) - { - this.contact = contact; - this.resource = resource; - } - - public boolean equals(Object obj) - { - if (this == obj) - return true; - - if (!(obj instanceof OtrContact)) - return false; - - OtrContact other = (OtrContact) obj; - - if (this.contact != null && this.contact.equals(other.contact)) - { - if (this.resource != null && resource.equals(other.resource)) - return true; - if (this.resource == null && other.resource == null) - return true; - return false; - } - return false; - } - - public int hashCode() - { - int result = 17; - - result = 31 * result + (contact == null ? 0 : contact.hashCode()); - result = 31 * result + (resource == null ? 0 : resource.hashCode()); - - return result; - } - } - - /** - * Gets the OtrContact that represents this - * [Contact, ContactResource] pair from the cache. If such pair does not - * still exist it is then created and cached for further usage. - * - * @param contact the Contact that the returned OtrContact - * represents. - * @param resource the ContactResource that the returned OtrContact - * represents. - * @return The OtrContact that represents this - * [Contact, ContactResource] pair. - */ - public static OtrContact getOtrContact( - Contact contact, ContactResource resource) - { - if (contact == null) - return null; - - List otrContactsList = contactsMap.get(contact); - if (otrContactsList != null) - { - for (OtrContact otrContact : otrContactsList) - { - if (resource != null && resource.equals(otrContact.resource)) - return otrContact; - } - OtrContact otrContact = new OtrContact(contact, resource); - synchronized (otrContactsList) - { - while (!otrContactsList.contains(otrContact)) - otrContactsList.add(otrContact); - } - return otrContact; - } - else - { - synchronized (contactsMap) - { - while (!contactsMap.containsKey(contact)) - { - otrContactsList = new ArrayList(); - contactsMap.put(contact, otrContactsList); - } - } - return getOtrContact(contact, resource); - } - } - - /** - * Cleans up unused cached up Contacts. - */ - public void serviceChanged(ServiceEvent event) - { - Object service - = OtrActivator.bundleContext.getService(event.getServiceReference()); - - if (!(service instanceof ProtocolProviderService)) - return; - - if (event.getType() == ServiceEvent.UNREGISTERING) - { - if (logger.isDebugEnabled()) - { - logger.debug( - "Unregistering a ProtocolProviderService, cleaning" - + " OTR's Contact to OtrContact map"); - } - - ProtocolProviderService provider - = (ProtocolProviderService) service; - - synchronized(contactsMap) - { - Iterator i = contactsMap.keySet().iterator(); - - while (i.hasNext()) - { - if (provider.equals(i.next().getProtocolProvider())) - i.remove(); - } - } - } - } -} diff --git a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java index 4332c4e73..040f93215 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java @@ -7,15 +7,12 @@ package net.java.sip.communicator.plugin.otr; import java.awt.event.*; -import java.security.*; import javax.swing.*; import net.java.otr4j.*; import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; /** * A special {@link JMenu} that holds the menu items for controlling the @@ -23,15 +20,12 @@ * * @author George Politis * @author Lyubomir Marinov - * @author Marin Dzhigarov */ class OtrContactMenu implements ActionListener, ScOtrEngineListener, ScOtrKeyManagerListener { - private final Logger logger = Logger.getLogger(OtrContactMenu.class); - private static final String ACTION_COMMAND_AUTHENTICATE_BUDDY = "AUTHENTICATE_BUDDY"; @@ -51,7 +45,7 @@ class OtrContactMenu private static final String ACTION_COMMAND_START_OTR = "START_OTR"; - private final OtrContact contact; + private final Contact contact; /** * The indicator which determines whether this JMenu is displayed @@ -75,27 +69,23 @@ class OtrContactMenu /** * The OtrContactMenu constructor. * - * @param otrContact the OtrContact this menu refers to. + * @param contact the Contact this menu refers to. * @param inMacOSXScreenMenuBar true if the new menu is to be * displayed in the Mac OS X screen menu bar; false, otherwise * @param menu the parent menu */ - public OtrContactMenu( OtrContact otrContact, + public OtrContactMenu( Contact contact, boolean inMacOSXScreenMenuBar, JMenu menu, boolean isSeparateMenu) { - this.contact = otrContact; + this.contact = contact; this.inMacOSXScreenMenuBar = inMacOSXScreenMenuBar; this.parentMenu = menu; - String resourceName = - otrContact.resource != null - ? "/" + otrContact.resource.getResourceName() - : ""; + separateMenu = isSeparateMenu - ? new SIPCommMenu(otrContact.contact.getDisplayName() - + resourceName) + ? new SIPCommMenu(contact.getDisplayName()) : null; /* @@ -113,10 +103,8 @@ public OtrContactMenu( OtrContact otrContact, this, OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); - setSessionStatus( - OtrActivator.scOtrEngine.getSessionStatus(this.contact)); - setOtrPolicy( - OtrActivator.scOtrEngine.getContactPolicy(otrContact.contact)); + setSessionStatus(OtrActivator.scOtrEngine.getSessionStatus(contact)); + setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact)); buildMenu(); } @@ -129,28 +117,12 @@ public void actionPerformed(ActionEvent e) String actionCommand = e.getActionCommand(); if (ACTION_COMMAND_END_OTR.equals(actionCommand)) - { - OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); - policy.setSendWhitespaceTag(false); - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); - // End session. OtrActivator.scOtrEngine.endSession(contact); - } else if (ACTION_COMMAND_START_OTR.equals(actionCommand)) - { - OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); - OtrPolicy globalPolicy = - OtrActivator.scOtrEngine.getGlobalPolicy(); - policy.setSendWhitespaceTag(globalPolicy.getSendWhitespaceTag()); - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); - // Start session. OtrActivator.scOtrEngine.startSession(contact); - } else if (ACTION_COMMAND_REFRESH_OTR.equals(actionCommand)) // Refresh session. @@ -163,22 +135,22 @@ else if (ACTION_COMMAND_AUTHENTICATE_BUDDY.equals(actionCommand)) else if (ACTION_COMMAND_CB_ENABLE.equals(actionCommand)) { OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); + OtrActivator.scOtrEngine.getContactPolicy(contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); policy.setEnableManual(state); - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); + OtrActivator.scOtrEngine.setContactPolicy(contact, policy); } else if (ACTION_COMMAND_CB_AUTO.equals(actionCommand)) { OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); + OtrActivator.scOtrEngine.getContactPolicy(contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); - policy.setSendWhitespaceTag(state); + policy.setEnableAlways(state); - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); + OtrActivator.scOtrEngine.setContactPolicy(contact, policy); } else if (ACTION_COMMAND_CB_AUTO_ALL.equals(actionCommand)) @@ -187,7 +159,10 @@ else if (ACTION_COMMAND_CB_AUTO_ALL.equals(actionCommand)) OtrActivator.scOtrEngine.getGlobalPolicy(); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); - globalPolicy.setSendWhitespaceTag(state); + globalPolicy.setEnableAlways(state); + OtrActivator.configService.setProperty( + OtrActivator.AUTO_INIT_OTR_PROP, + Boolean.toString(state)); OtrActivator.scOtrEngine.setGlobalPolicy(globalPolicy); } @@ -195,17 +170,17 @@ else if (ACTION_COMMAND_CB_AUTO_ALL.equals(actionCommand)) else if (ACTION_COMMAND_CB_REQUIRE.equals(actionCommand)) { OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); + OtrActivator.scOtrEngine.getContactPolicy(contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); policy.setRequireEncryption(state); OtrActivator.configService.setProperty( OtrActivator.OTR_MANDATORY_PROP, Boolean.toString(state)); - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); + OtrActivator.scOtrEngine.setContactPolicy(contact, policy); } else if (ACTION_COMMAND_CB_RESET.equals(actionCommand)) - OtrActivator.scOtrEngine.setContactPolicy(contact.contact, null); + OtrActivator.scOtrEngine.setContactPolicy(contact, null); } /* @@ -214,8 +189,7 @@ else if (ACTION_COMMAND_CB_RESET.equals(actionCommand)) public void contactPolicyChanged(Contact contact) { // Update the corresponding to the contact menu. - if (OtrContactMenu.this.contact != null && - contact.equals(OtrContactMenu.this.contact.contact)) + if (contact.equals(OtrContactMenu.this.contact)) setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact)); } @@ -223,11 +197,10 @@ public void contactPolicyChanged(Contact contact) * Implements ScOtrKeyManagerListener#contactVerificationStatusChanged( * Contact). */ - public void contactVerificationStatusChanged(OtrContact otrContact) + public void contactVerificationStatusChanged(Contact contact) { - if (otrContact.equals(OtrContactMenu.this.contact)) - setSessionStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); + if (contact.equals(OtrContactMenu.this.contact)) + setSessionStatus(OtrActivator.scOtrEngine.getSessionStatus(contact)); } /** @@ -247,7 +220,7 @@ void dispose() */ public void globalPolicyChanged() { - setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact.contact)); + setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact)); } /** @@ -259,8 +232,7 @@ private void buildMenu() if(separateMenu != null) separateMenu.removeAll(); - OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy(contact.contact); + OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); JMenuItem endOtr = new JMenuItem(); endOtr.setText(OtrActivator.resourceService @@ -354,8 +326,7 @@ private void buildMenu() OtrActivator.resourceService .getI18NString( "plugin.otr.menu.CB_AUTO", - new String[] - {contact.contact.getDisplayName()}))); + new String[] {contact.getDisplayName()}))); cbAlways.setEnabled(policy.getEnableManual()); cbAlways.setSelected(policy.getEnableAlways()); @@ -368,9 +339,13 @@ private void buildMenu() .getI18NString("plugin.otr.menu.CB_AUTO_ALL")); cbAlwaysAll.setEnabled(policy.getEnableManual()); + String autoInitPropValue + = OtrActivator.configService.getString( + OtrActivator.AUTO_INIT_OTR_PROP); boolean isAutoInit = OtrActivator.scOtrEngine.getGlobalPolicy().getEnableAlways(); - + if (autoInitPropValue != null) + isAutoInit = Boolean.parseBoolean(autoInitPropValue); cbAlwaysAll.setSelected(isAutoInit); cbAlwaysAll.setActionCommand(ACTION_COMMAND_CB_AUTO_ALL); @@ -432,11 +407,10 @@ else if (!isMandatory && defaultOtrPropValue != null) /* * Implements ScOtrEngineListener#sessionStatusChanged(Contact). */ - public void sessionStatusChanged(OtrContact otrContact) + public void sessionStatusChanged(Contact contact) { - if (otrContact.equals(OtrContactMenu.this.contact)) - setSessionStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); + if (contact.equals(OtrContactMenu.this.contact)) + setSessionStatus(OtrActivator.scOtrEngine.getSessionStatus(contact)); } /** @@ -450,9 +424,6 @@ private void setSessionStatus(ScSessionStatus sessionStatus) { if (sessionStatus != this.sessionStatus) { - logger.debug( - "Setting session status of contact " + contact.contact + - " to " + sessionStatus + ". Was " + this.sessionStatus); this.sessionStatus = sessionStatus; if (separateMenu != null) @@ -499,14 +470,8 @@ private void updateIcon() switch (sessionStatus) { case ENCRYPTED: - PublicKey pubKey = - OtrActivator.scOtrEngine.getRemotePublicKey(contact); - String fingerprint = - OtrActivator.scOtrKeyManager. - getFingerprintFromPublicKey(pubKey); imageID - = OtrActivator.scOtrKeyManager.isVerified( - contact.contact, fingerprint) + = OtrActivator.scOtrKeyManager.isVerified(contact) ? "plugin.otr.ENCRYPTED_ICON_16x16" : "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16"; break; @@ -525,15 +490,4 @@ private void updateIcon() separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID)); } - - @Override - public void multipleInstancesDetected(OtrContact contact) {} - - @Override - public void outgoingSessionChanged(OtrContact otrContact) - { - if (otrContact.equals(OtrContactMenu.this.contact)) - setSessionStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); - } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java index a6d4b2538..2984fb75f 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java @@ -9,14 +9,12 @@ import java.awt.*; import java.awt.event.*; import java.io.*; -import java.security.*; import javax.imageio.*; import net.java.otr4j.*; import net.java.otr4j.session.*; import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; @@ -28,7 +26,6 @@ * the main chat toolbar. * * @author George Politis - * @author Marin Dzhigarov */ public class OtrMetaContactButton extends AbstractPluginComponent @@ -42,7 +39,7 @@ public class OtrMetaContactButton private SIPCommButton button; - private OtrContact otrContact; + private Contact contact; private AnimatedImage animatedPadlockImage; @@ -56,21 +53,25 @@ public class OtrMetaContactButton private Image timedoutPadlockImage; - public void sessionStatusChanged(OtrContact otrContact) + /** + * The timer task that changes the padlock icon to "loading" and + * then to "broken" if the specified timeout passed + */ + public void sessionStatusChanged(Contact contact) { // OtrMetaContactButton.this.contact can be null. - if (otrContact.equals(OtrMetaContactButton.this.otrContact)) + if (contact.equals(OtrMetaContactButton.this.contact)) { + setStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); + OtrActivator.scOtrEngine.getSessionStatus(contact)); } } public void contactPolicyChanged(Contact contact) { // OtrMetaContactButton.this.contact can be null. - if (OtrMetaContactButton.this.otrContact != null && - contact.equals(OtrMetaContactButton.this.otrContact.contact)) + if (contact.equals(OtrMetaContactButton.this.contact)) { setPolicy( OtrActivator.scOtrEngine.getContactPolicy(contact)); @@ -79,18 +80,18 @@ public void contactPolicyChanged(Contact contact) public void globalPolicyChanged() { - if (OtrMetaContactButton.this.otrContact != null) + if (OtrMetaContactButton.this.contact != null) setPolicy( - OtrActivator.scOtrEngine.getContactPolicy(otrContact.contact)); + OtrActivator.scOtrEngine.getContactPolicy(contact)); } - public void contactVerificationStatusChanged(OtrContact otrContact) + public void contactVerificationStatusChanged(Contact contact) { // OtrMetaContactButton.this.contact can be null. - if (otrContact.equals(OtrMetaContactButton.this.otrContact)) + if (contact.equals(OtrMetaContactButton.this.contact)) { setStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); + OtrActivator.scOtrEngine.getSessionStatus(contact)); } } @@ -171,38 +172,23 @@ private SIPCommButton getButton() { public void actionPerformed(ActionEvent e) { - if (otrContact == null) + if (contact == null) return; - switch (OtrActivator.scOtrEngine.getSessionStatus(otrContact)) + switch (OtrActivator.scOtrEngine.getSessionStatus(contact)) { case ENCRYPTED: - OtrPolicy policy = - OtrActivator.scOtrEngine.getContactPolicy( - otrContact.contact); - policy.setSendWhitespaceTag(false); - OtrActivator.scOtrEngine.setContactPolicy( - otrContact.contact, policy); case FINISHED: case LOADING: // Default action for finished, encrypted and loading // sessions is end session. - OtrActivator.scOtrEngine.endSession(otrContact); + OtrActivator.scOtrEngine.endSession(contact); break; case TIMED_OUT: case PLAINTEXT: - policy = - OtrActivator.scOtrEngine.getContactPolicy( - otrContact.contact); - OtrPolicy globalPolicy = - OtrActivator.scOtrEngine.getGlobalPolicy(); - policy.setSendWhitespaceTag( - globalPolicy.getSendWhitespaceTag()); - OtrActivator.scOtrEngine.setContactPolicy( - otrContact.contact, policy); // Default action for timed_out and plaintext sessions // is start session. - OtrActivator.scOtrEngine.startSession(otrContact); + OtrActivator.scOtrEngine.startSession(contact); break; } } @@ -235,49 +221,20 @@ public String getName() @Override public void setCurrentContact(Contact contact) { - setCurrentContact(contact, null); - } - - public void setCurrentContact(Contact contact, String resourceName) - { - if (contact == null) - { - this.otrContact = null; - this.setPolicy(null); - this.setStatus(ScSessionStatus.PLAINTEXT); + if (this.contact == contact) return; - } - if (resourceName == null) + this.contact = contact; + if (contact != null) { - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, null); - if (this.otrContact == otrContact) - return; - this.otrContact = otrContact; - this.setStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); - this.setPolicy( - OtrActivator.scOtrEngine.getContactPolicy(contact)); - return; + this.setStatus(OtrActivator.scOtrEngine.getSessionStatus(contact)); + this.setPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact)); } - for (ContactResource resource : contact.getResources()) + else { - if (resource.getResourceName().equals(resourceName)) - { - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, resource); - if (this.otrContact == otrContact) - return; - this.otrContact = otrContact; - this.setStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); - this.setPolicy( - OtrActivator.scOtrEngine.getContactPolicy(contact)); - return; - } + this.setStatus(ScSessionStatus.PLAINTEXT); + this.setPolicy(null); } - logger.debug("Could not find resource for contact " + contact); } /* @@ -315,19 +272,12 @@ private void setStatus(ScSessionStatus status) switch (status) { case ENCRYPTED: - PublicKey pubKey = - OtrActivator.scOtrEngine.getRemotePublicKey(otrContact); - String fingerprint = - OtrActivator.scOtrKeyManager. - getFingerprintFromPublicKey(pubKey); image - = OtrActivator.scOtrKeyManager.isVerified( - otrContact.contact, fingerprint) + = OtrActivator.scOtrKeyManager.isVerified(contact) ? verifiedLockedPadlockImage : unverifiedLockedPadlockImage; tipKey = - OtrActivator.scOtrKeyManager.isVerified( - otrContact.contact, fingerprint) + OtrActivator.scOtrKeyManager.isVerified(contact) ? "plugin.otr.menu.VERIFIED" : "plugin.otr.menu.UNVERIFIED"; break; @@ -358,19 +308,4 @@ private void setStatus(ScSessionStatus status) .getI18NString(tipKey)); button.repaint(); } - - @Override - public void multipleInstancesDetected(OtrContact contact) - {} - - @Override - public void outgoingSessionChanged(OtrContact otrContact) - { - // OtrMetaContactButton.this.contact can be null. - if (otrContact.equals(OtrMetaContactButton.this.otrContact)) - { - setStatus( - OtrActivator.scOtrEngine.getSessionStatus(otrContact)); - } - } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java index 024ec5649..1602cf8f4 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactMenu.java @@ -13,11 +13,11 @@ import javax.swing.*; import javax.swing.event.*; -import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.plugin.desktoputil.*; /** * @author George Politis @@ -95,54 +95,14 @@ private void createOtrContactMenus(MetaContact metaContact) if (metaContact.getContactCount() == 1) { - Contact contact = contacts.next(); - Collection resources = contact.getResources(); - if (contact.supportResources() && - resources != null && - resources.size() > 0) - { - for (ContactResource resource : resources) - { - new OtrContactMenu( - OtrContactManager.getOtrContact(contact, resource), - inMacOSXScreenMenuBar, - menu, - true); - } - } - else - new OtrContactMenu( - OtrContactManager.getOtrContact(contact, null), - inMacOSXScreenMenuBar, - menu, - false); + new OtrContactMenu( + contacts.next(), inMacOSXScreenMenuBar, menu, false); } else while (contacts.hasNext()) { - Contact contact = contacts.next(); - Collection resources = - contact.getResources(); - if (contact.supportResources() && - resources != null && - resources.size() > 0) - { - for (ContactResource resource : resources) - { - new OtrContactMenu( - OtrContactManager.getOtrContact( - contact, resource), - inMacOSXScreenMenuBar, - menu, - true); - } - } - else - new OtrContactMenu( - OtrContactManager.getOtrContact(contact, null), - inMacOSXScreenMenuBar, - menu, - true); + new OtrContactMenu( + contacts.next(), inMacOSXScreenMenuBar, menu, true); } } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java index f447de029..44e0f0e25 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java @@ -7,8 +7,6 @@ package net.java.sip.communicator.plugin.otr; import net.java.otr4j.*; -import net.java.otr4j.io.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; @@ -26,12 +24,10 @@ public class OtrTransformLayer public MessageDeliveredEvent messageDelivered(MessageDeliveredEvent evt) { Contact contact = evt.getDestinationContact(); - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, evt.getContactResource()); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); ScSessionStatus sessionStatus = - OtrActivator.scOtrEngine.getSessionStatus(otrContact); + OtrActivator.scOtrEngine.getSessionStatus(contact); // If OTR is disabled and we are not over an encrypted session, don't // process anything. if (!policy.getEnableManual() @@ -65,12 +61,10 @@ public MessageDeliveredEvent messageDeliveryPending( MessageDeliveredEvent evt) { Contact contact = evt.getDestinationContact(); - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, evt.getContactResource()); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); ScSessionStatus sessionStatus = - OtrActivator.scOtrEngine.getSessionStatus(otrContact); + OtrActivator.scOtrEngine.getSessionStatus(contact); // If OTR is disabled and we are not over an encrypted session, don't // process anything. if (!policy.getEnableManual() @@ -86,7 +80,7 @@ public MessageDeliveredEvent messageDeliveryPending( // Process the outgoing message. String msgContent = evt.getSourceMessage().getContent(); String processedMessageContent = - OtrActivator.scOtrEngine.transformSending(otrContact, msgContent); + OtrActivator.scOtrEngine.transformSending(contact, msgContent); if (processedMessageContent == null || processedMessageContent.length() < 1) @@ -100,22 +94,13 @@ public MessageDeliveredEvent messageDeliveryPending( contact.getProtocolProvider().getOperationSet( OperationSetBasicInstantMessaging.class); Message processedMessage = - imOpSet.createMessage( - processedMessageContent, - evt.getSourceMessage().getContentType(), - evt.getSourceMessage().getEncoding(), - evt.getSourceMessage().getSubject()); + imOpSet.createMessage(processedMessageContent); // Create a new event and return. MessageDeliveredEvent processedEvent = new MessageDeliveredEvent(processedMessage, contact, evt .getTimestamp()); - if(processedMessage.getContent().contains(SerializationConstants.HEAD)) - { - processedEvent.setMessageEncrypted(true); - } - return processedEvent; } @@ -125,12 +110,10 @@ public MessageDeliveredEvent messageDeliveryPending( public MessageReceivedEvent messageReceived(MessageReceivedEvent evt) { Contact contact = evt.getSourceContact(); - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, evt.getContactResource()); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); ScSessionStatus sessionStatus = - OtrActivator.scOtrEngine.getSessionStatus(otrContact); + OtrActivator.scOtrEngine.getSessionStatus(contact); // If OTR is disabled and we are not over an encrypted session, don't // process anything. if (!policy.getEnableManual() @@ -142,7 +125,7 @@ public MessageReceivedEvent messageReceived(MessageReceivedEvent evt) String msgContent = evt.getSourceMessage().getContent(); String processedMessageContent = - OtrActivator.scOtrEngine.transformReceiving(otrContact, msgContent); + OtrActivator.scOtrEngine.transformReceiving(contact, msgContent); if (processedMessageContent == null || processedMessageContent.length() < 1) @@ -157,9 +140,7 @@ public MessageReceivedEvent messageReceived(MessageReceivedEvent evt) OperationSetBasicInstantMessaging.class); Message processedMessage = imOpSet.createMessageWithUID( - processedMessageContent, - evt.getSourceMessage().getContentType(), - evt.getSourceMessage().getMessageUID()); + processedMessageContent, evt.getSourceMessage().getMessageUID()); // Create a new event and return. MessageReceivedEvent processedEvent = diff --git a/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java index 4c55f248d..423cf23f2 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java @@ -8,7 +8,6 @@ import java.lang.ref.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; /** @@ -94,7 +93,7 @@ public void contactPolicyChanged(Contact contact) * Forwards the event/notification to the associated * T if it is still needed by the application. */ - public void contactVerificationStatusChanged(OtrContact contact) + public void contactVerificationStatusChanged(Contact contact) { ScOtrKeyManagerListener l = getListener(); @@ -146,35 +145,11 @@ public void globalPolicyChanged() * Forwards the event/notification to the associated * T if it is still needed by the application. */ - public void sessionStatusChanged(OtrContact contact) + public void sessionStatusChanged(Contact contact) { ScOtrEngineListener l = getListener(); if (l != null) l.sessionStatusChanged(contact); } - - /** - * Forwards the event/notification to the associated - * T if it is still needed by the application. - */ - public void multipleInstancesDetected(OtrContact contact) - { - ScOtrEngineListener l = getListener(); - - if (l != null) - l.multipleInstancesDetected(contact); - } - - /** - * Forwards the event/notification to the associated - * T if it is still needed by the application. - */ - public void outgoingSessionChanged(OtrContact contact) - { - ScOtrEngineListener l = getListener(); - - if (l != null) - l.outgoingSessionChanged(contact); - } } diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java index f9ee9098b..853c5fc5b 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java @@ -6,12 +6,7 @@ */ package net.java.sip.communicator.plugin.otr; -import java.security.*; -import java.util.*; - import net.java.otr4j.*; -import net.java.otr4j.session.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; /** @@ -34,7 +29,7 @@ public interface ScOtrEngine * @param secret The secret answer for the question. */ public abstract void initSmp( - OtrContact contact, String question, String secret); + Contact contact, String question, String secret); /** * Responds to a question that is asked during the Smp negotiation process. @@ -42,16 +37,12 @@ public abstract void initSmp( * >http://en.wikipedia.org/wiki/Socialist_Millionaire_Problem * * @param contact The contact for whom we want to respond to a question - * during the Smp negotiation process. - * @param receiverTag The instance tag of the intended receiver of the SMP - * response + * during the Smp negotiation process. * @param question The question that is asked during the Smp negotiation. * @param secret The secret answer for the question. */ - public abstract void respondSmp(OtrContact contact, - InstanceTag receiverTag, - String question, - String secret); + public abstract void respondSmp( + Contact contact, String question, String secret); /** * Aborts the Smp negotiation process. @@ -61,111 +52,61 @@ public abstract void respondSmp(OtrContact contact, * @param contact The contact with whom we want to abort the * Smp negotiation process. */ - public abstract void abortSmp(OtrContact contact); + public abstract void abortSmp(Contact contact); /** * Transforms an outgoing message. * - * @param contact the destination {@link OtrContact}. + * @param contact the destination {@link Contact}. * @param content the original message content. * @return the transformed message content. */ - public abstract String transformSending(OtrContact contact, String content); + public abstract String transformSending(Contact contact, String content); /** * Transforms an incoming message. * - * @param contact the source {@link OtrContact}. + * @param contact the source {@link Contact}. * @param content the original message content. * @return the transformed message content. */ - public abstract String transformReceiving(OtrContact contact, String content); + public abstract String transformReceiving(Contact contact, String content); /** - * Starts the Off-the-Record session for the given {@link OtrContact}, if it's + * Starts the Off-the-Record session for the given {@link Contact}, if it's * not already started. * - * @param contact the {@link OtrContact} with whom we want to start an OTR + * @param contact the {@link Contact} with whom we want to start an OTR * session. */ - public abstract void startSession(OtrContact contact); + public abstract void startSession(Contact contact); /** - * Ends the Off-the-Record session for the given {@link OtrContact}, if it is + * Ends the Off-the-Record session for the given {@link Contact}, if it is * not already started. * - * @param contact the {@link OtrContact} with whom we want to end the OTR + * @param contact the {@link Contact} with whom we want to end the OTR * session. */ - public abstract void endSession(OtrContact contact); + public abstract void endSession(Contact contact); /** - * Refreshes the Off-the-Record session for the given {@link OtrContact}. If + * Refreshes the Off-the-Record session for the given {@link Contact}. If * the session does not exist, a new session is established. * - * @param contact the {@link OtrContact} with whom we want to refresh the OTR + * @param contact the {@link Contact} with whom we want to refresh the OTR * session. */ - public abstract void refreshSession(OtrContact contact); + public abstract void refreshSession(Contact contact); /** - * Get the outgoing OTRv3 Session. This could be the 'master' - * session as well as a 'slave' session. - * This method could also be safely used for OTRv2 sessions. In the case of - * version 2 the master session itself will always be returned. - * - * @param contact the {@link OtrContact} for whom we want to get the - * outgoing OTR session. + * Gets the {@link ScSessionStatus} for the given {@link Contact}. * - * @return the Session that is currently transforming outgoing all - * messages. - */ - public abstract Session getOutgoingSession(OtrContact contact); - - /** - * Some IM networks always relay all messages to all sessions of a client - * who is logged in multiple times. OTR version 3 deals with this problem - * with introducing instance tags. - * - * https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html - *

- * Returns a list containing all instances of a session. The 'master' - * session is always first in the list. - * - * @param contact the {@link OtrContact} for whom we want to get the instances - * - * @return A list of all instances of the session for the specified contact. - */ - public abstract List getSessionInstances(OtrContact contact); - - /** - * Some IM networks always relay all messages to all sessions of a client - * who is logged in multiple times. OTR version 3 deals with this problem - * with introducing instance tags. - * - * https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html - *

- * When the client wishes to start sending OTRv3 encrypted messages to a - * specific session of his buddy who is logged in multiple times, he can set - * the outgoing instance of his buddy by specifying his InstanceTag. - * - * @param contact the {@link OtrContact} to whom we want to set the outgoing - * instance tag. - * @param tag the outgoing {@link InstanceTag} - * - * @return true if an outgoing session with such {@link InstanceTag} exists - * . Otherwise false - */ - public abstract boolean setOutgoingSession(OtrContact contact, InstanceTag tag); - - /** - * Gets the {@link ScSessionStatus} for the given {@link OtrContact}. - * - * @param contact the {@link OtrContact} whose {@link ScSessionStatus} we are + * @param contact the {@link Contact} whose {@link ScSessionStatus} we are * interested in. * @return the {@link ScSessionStatus}. */ - public abstract ScSessionStatus getSessionStatus(OtrContact contact); + public abstract ScSessionStatus getSessionStatus(Contact contact); // New Methods (Misc) @@ -194,8 +135,6 @@ public abstract void respondSmp(OtrContact contact, */ public abstract void removeListener(ScOtrEngineListener listener); - public abstract PublicKey getRemotePublicKey(OtrContact otrContact); - // New Methods (Policy management) /** * Gets the global {@link OtrPolicy}. diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java index 171fc7810..09be3990f 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java @@ -12,9 +12,7 @@ import java.util.concurrent.*; import net.java.otr4j.*; -import net.java.otr4j.crypto.*; import net.java.otr4j.session.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.plugin.otr.authdialog.*; import net.java.sip.communicator.service.browserlauncher.*; import net.java.sip.communicator.service.contactlist.*; @@ -53,60 +51,21 @@ public KeyPair getLocalKeyPair(SessionID sessionID) public OtrPolicy getSessionPolicy(SessionID sessionID) { - return getContactPolicy(getOtrContact(sessionID).contact); + return getContactPolicy(getContact(sessionID)); } public void injectMessage(SessionID sessionID, String messageText) { - OtrContact otrContact = getOtrContact(sessionID); - Contact contact = otrContact.contact; - ContactResource resource = null; - - if (contact.supportResources()) - { - Collection resources = contact.getResources(); - if (resources != null) - { - for (ContactResource r : resources) - { - if (r.equals(otrContact.resource)) - { - resource = r; - break; - } - } - } - } - + Contact contact = getContact(sessionID); OperationSetBasicInstantMessaging imOpSet = contact .getProtocolProvider() .getOperationSet( OperationSetBasicInstantMessaging.class); - - // This is a dirty way of detecting whether the injected message - // contains HTML markup. If this is the case then we should create - // the message with the appropriate content type so that the remote - // party can properly display the HTML. - // When otr4j injects QueryMessages it calls - // OtrEngineHost.getFallbackMessage() which is currently the only - // host method that uses HTML so we can simply check if the injected - // message contains the string that getFallbackMessage() returns. - String otrHtmlFallbackMessage - = ""; - String contentType - = messageText.contains(otrHtmlFallbackMessage) - ? OperationSetBasicInstantMessaging.HTML_MIME_TYPE - : OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE; - Message message - = imOpSet.createMessage( - messageText, - contentType, - OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, - null); + Message message = imOpSet.createMessage(messageText); injectedMessageUIDs.add(message.getMessageUID()); - imOpSet.sendInstantMessage(contact, resource, message); + imOpSet.sendInstantMessage(contact, message); } public void showError(SessionID sessionID, String err) @@ -116,11 +75,10 @@ public void showError(SessionID sessionID, String err) public void showWarning(SessionID sessionID, String warn) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; OtrActivator.uiService.getChat(contact).addMessage( contact.getDisplayName(), new Date(), Chat.SYSTEM_MESSAGE, warn, @@ -131,16 +89,14 @@ public void showWarning(SessionID sessionID, String warn) public void unreadableMessageReceived(SessionID sessionID) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; + Contact contact = getContact(sessionID); + if (contact == null) + return; - Contact contact = otrContact.contact; String error = OtrActivator.resourceService.getI18NString( "plugin.otr.activator.unreadablemsgreceived", - new String[] - {contact.getDisplayName() + resourceName}); + new String[] {contact.getDisplayName()}); OtrActivator.uiService.getChat(contact).addMessage( contact.getDisplayName(), new Date(), Chat.ERROR_MESSAGE, error, @@ -151,11 +107,10 @@ public void unreadableMessageReceived(SessionID sessionID) public void unencryptedMessageReceived(SessionID sessionID, String msg) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; String warn = OtrActivator.resourceService.getI18NString( "plugin.otr.activator.unencryptedmsgreceived"); @@ -169,11 +124,10 @@ public void unencryptedMessageReceived(SessionID sessionID, String msg) public void smpError(SessionID sessionID, int tlvType, boolean cheated) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; logger.debug("SMP error occurred" + ". Contact: " + contact.getDisplayName() + ". TLV type: " + tlvType @@ -187,11 +141,11 @@ public void smpError(SessionID sessionID, int tlvType, boolean cheated) Chat.ERROR_MESSAGE, error, OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialogMap.put(contact, progressDialog); } progressDialog.setProgressFail(); @@ -201,11 +155,10 @@ public void smpError(SessionID sessionID, int tlvType, boolean cheated) @Override public void smpAborted(SessionID sessionID) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; Session session = otrEngine.getSession(sessionID); if (session.isSmpInProgress()) { @@ -218,12 +171,11 @@ public void smpAborted(SessionID sessionID) throws OtrException Chat.SYSTEM_MESSAGE, warn, OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - SmpProgressDialog progressDialog = - progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialogMap.put(contact, progressDialog); } progressDialog.setProgressFail(); @@ -232,21 +184,17 @@ public void smpAborted(SessionID sessionID) throws OtrException } @Override - public void finishedSessionMessage(SessionID sessionID, String msgText) + public void finishedSessionMessage(SessionID sessionID) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; String error = OtrActivator.resourceService.getI18NString( "plugin.otr.activator.sessionfinishederror", - new String[] - {msgText, contact.getDisplayName() + resourceName}); + new String[] {contact.getDisplayName()}); OtrActivator.uiService.getChat(contact).addMessage( contact.getDisplayName(), new Date(), Chat.ERROR_MESSAGE, error, @@ -257,16 +205,14 @@ public void finishedSessionMessage(SessionID sessionID, String msgText) public void requireEncryptedMessage(SessionID sessionID, String msgText) throws OtrException { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; String error = OtrActivator.resourceService.getI18NString( "plugin.otr.activator.requireencryption", - new String[] - {msgText}); + new String[] {contact.getDisplayName()}); OtrActivator.uiService.getChat(contact).addMessage( contact.getDisplayName(), new Date(), Chat.ERROR_MESSAGE, error, @@ -283,24 +229,21 @@ public byte[] getLocalFingerprintRaw(SessionID sessionID) } @Override - public void askForSecret( - SessionID sessionID, InstanceTag receiverTag, String question) + public void askForSecret(SessionID sessionID, String question) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; SmpAuthenticateBuddyDialog dialog = - new SmpAuthenticateBuddyDialog( - otrContact, receiverTag, question); + new SmpAuthenticateBuddyDialog(contact, question); dialog.setVisible(true); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialogMap.put(contact, progressDialog); } progressDialog.init(); @@ -308,21 +251,19 @@ public void askForSecret( } @Override - public void verify( - SessionID sessionID, String fingerprint, boolean approved) + public void verify(SessionID sessionID, boolean approved) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; - OtrActivator.scOtrKeyManager.verify(otrContact, fingerprint); + OtrActivator.scOtrKeyManager.verify(contact); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialogMap.put(contact, progressDialog); } progressDialog.setProgressSuccess(); @@ -330,20 +271,19 @@ public void verify( } @Override - public void unverify(SessionID sessionID, String fingerprint) + public void unverify(SessionID sessionID) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; - OtrActivator.scOtrKeyManager.unverify(otrContact, fingerprint); + OtrActivator.scOtrKeyManager.unverify(contact); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialogMap.put(contact, progressDialog); } progressDialog.setProgressFail(); @@ -372,50 +312,6 @@ public String getFallbackMessage(SessionID sessionID) "plugin.otr.activator.fallbackmessage", new String[] {accountID.getDisplayName()}); } - - @Override - public void multipleInstancesDetected(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - String message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.multipleinstancesdetected", - new String[] - {contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - } - - @Override - public void messageFromAnotherInstanceReceived(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - String message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.msgfromanotherinstance", - new String[] - {contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - } } /** @@ -439,13 +335,13 @@ public void messageFromAnotherInstanceReceived(SessionID sessionID) private Map scSessionStatusMap = new ConcurrentHashMap(); - private static final Map contactsMap = - new Hashtable(); + private static final Map contactsMap = + new Hashtable(); - private static final Map progressDialogMap = - new ConcurrentHashMap(); + private static final Map progressDialogMap = + new ConcurrentHashMap(); - public static OtrContact getOtrContact(SessionID sessionID) + public static Contact getContact(SessionID sessionID) { return contactsMap.get(new ScSessionID(sessionID)); } @@ -468,15 +364,13 @@ public static ScSessionID getScSessionForGuid(UUID guid) return null; } - public static SessionID getSessionID(OtrContact otrContact) + public static SessionID getSessionID(Contact contact) { - ProtocolProviderService pps = otrContact.contact.getProtocolProvider(); - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; + ProtocolProviderService pps = contact.getProtocolProvider(); SessionID sessionID = new SessionID( pps.getAccountID().getAccountUniqueID(), - otrContact.contact.getAddress() + resourceName, + contact.getAddress(), pps.getProtocolName()); synchronized (contactsMap) @@ -486,7 +380,7 @@ public static SessionID getSessionID(OtrContact otrContact) ScSessionID scSessionID = new ScSessionID(sessionID); - contactsMap.put(scSessionID, otrContact); + contactsMap.put(scSessionID, contact); } return sessionID; @@ -521,18 +415,15 @@ public ScOtrEngineImpl() { public void sessionStatusChanged(SessionID sessionID) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; // Cancels any scheduled tasks that will change the // ScSessionStatus for this Contact - scheduler.cancel(otrContact); + scheduler.cancel(contact); - ScSessionStatus scSessionStatus = getSessionStatus(otrContact); + ScSessionStatus scSessionStatus = getSessionStatus(contact); String message = ""; switch (otrEngine.getSessionStatus(sessionID)) { @@ -542,38 +433,15 @@ public void sessionStatusChanged(SessionID sessionID) PublicKey remotePubKey = otrEngine.getRemotePublicKey(sessionID); - String remoteFingerprint = null; - try - { - remoteFingerprint = - new OtrCryptoEngineImpl(). - getFingerprint(remotePubKey); - } - catch (OtrCryptoException e) - { - logger.debug( - "Could not get the fingerprint from the " - + "public key of contact: " + contact); - } + PublicKey storedPubKey = + OtrActivator.scOtrKeyManager.loadPublicKey(contact); - List allFingerprintsOfContact = - OtrActivator.scOtrKeyManager. - getAllRemoteFingerprints(contact); - if (allFingerprintsOfContact != null) - { - if (!allFingerprintsOfContact.contains( - remoteFingerprint)) - { - OtrActivator.scOtrKeyManager.saveFingerprint( - contact, remoteFingerprint); - } - } + if (!remotePubKey.equals(storedPubKey)) + OtrActivator.scOtrKeyManager.savePublicKey(contact, + remotePubKey); - if (!OtrActivator.scOtrKeyManager.isVerified( - contact, remoteFingerprint)) + if (!OtrActivator.scOtrKeyManager.isVerified(contact)) { - OtrActivator.scOtrKeyManager.unverify( - otrContact, remoteFingerprint); UUID sessionGuid = null; for(ScSessionID scSessionID : contactsMap.keySet()) { @@ -593,7 +461,7 @@ public void sessionStatusChanged(SessionID sessionID) + ".unverifiedsessionwarning", new String[] { - contact.getDisplayName() + resourceName, + contact.getDisplayName(), this.getClass().getName(), "AUTHENTIFICATION", sessionGuid.toString() @@ -608,9 +476,8 @@ public void sessionStatusChanged(SessionID sessionID) // show info whether history is on or off String otrAndHistoryMessage; - if(!OtrActivator.getMessageHistoryService() - .isHistoryLoggingEnabled() || - !isHistoryLoggingEnabled(contact)) + if(!ConfigurationUtils.isHistoryLoggingEnabled() + || !isHistoryLoggingEnabled(contact)) { otrAndHistoryMessage = OtrActivator.resourceService.getI18NString( @@ -642,30 +509,13 @@ public void sessionStatusChanged(SessionID sessionID) otrAndHistoryMessage, OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.multipleinstancesdetected", - new String[] - {contact.getDisplayName()}); - - if (contact.supportResources() - && contact.getResources() != null - && contact.getResources().size() > 1) - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - message = OtrActivator.resourceService.getI18NString( - OtrActivator.scOtrKeyManager.isVerified( - contact, remoteFingerprint) + OtrActivator.scOtrKeyManager.isVerified(contact) ? "plugin.otr.activator.sessionstared" : "plugin.otr.activator" + ".unverifiedsessionstared", - new String[] - {contact.getDisplayName() + resourceName}); + new String[] { contact.getDisplayName() }); break; case FINISHED: @@ -675,7 +525,7 @@ public void sessionStatusChanged(SessionID sessionID) OtrActivator.resourceService.getI18NString( "plugin.otr.activator.sessionfinished", new String[] - {contact.getDisplayName() + resourceName}); + { contact.getDisplayName() }); break; case PLAINTEXT: scSessionStatus = ScSessionStatus.PLAINTEXT; @@ -683,7 +533,7 @@ public void sessionStatusChanged(SessionID sessionID) message = OtrActivator.resourceService.getI18NString( "plugin.otr.activator.sessionlost", new String[] - {contact.getDisplayName() + resourceName}); + { contact.getDisplayName() }); break; } @@ -693,27 +543,7 @@ public void sessionStatusChanged(SessionID sessionID) OperationSetBasicInstantMessaging.HTML_MIME_TYPE); for (ScOtrEngineListener l : getListeners()) - l.sessionStatusChanged(otrContact); - } - - public void multipleInstancesDetected(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - for (ScOtrEngineListener l : getListeners()) - l.multipleInstancesDetected(otrContact); - } - - public void outgoingSessionChanged(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - for (ScOtrEngineListener l : getListeners()) - l.outgoingSessionChanged(otrContact); + l.sessionStatusChanged(contact); } }); } @@ -730,8 +560,8 @@ private boolean isHistoryLoggingEnabled(Contact contact) MetaContact metaContact = OtrActivator .getContactListService().findMetaContactByContact(contact); if(metaContact != null) - return OtrActivator.getMessageHistoryService() - .isHistoryLoggingEnabled(metaContact.getMetaUID()); + return ConfigurationUtils.isHistoryLoggingEnabled( + metaContact.getMetaUID()); else return true; } @@ -773,12 +603,12 @@ public void chatLinkClicked(URI url) } } - public void endSession(OtrContact otrContact) + public void endSession(Contact contact) { - SessionID sessionID = getSessionID(otrContact); + SessionID sessionID = getSessionID(contact); try { - setSessionStatus(otrContact, ScSessionStatus.PLAINTEXT); + setSessionStatus(contact, ScSessionStatus.PLAINTEXT); otrEngine.endSession(sessionID); } @@ -790,14 +620,8 @@ public void endSession(OtrContact otrContact) public OtrPolicy getContactPolicy(Contact contact) { - ProtocolProviderService pps = contact.getProtocolProvider(); - SessionID sessionID - = new SessionID( - pps.getAccountID().getAccountUniqueID(), - contact.getAddress(), - pps.getProtocolName()); int policy = - this.configurator.getPropertyInt(sessionID + "contact_policy", + this.configurator.getPropertyInt(getSessionID(contact) + "policy", -1); if (policy < 0) return getGlobalPolicy(); @@ -807,13 +631,8 @@ public OtrPolicy getContactPolicy(Contact contact) public OtrPolicy getGlobalPolicy() { - /* - * SEND_WHITESPACE_TAG bit will be lowered until we stabilize the OTR. - */ - int defaultScOtrPolicy = - OtrPolicy.OTRL_POLICY_DEFAULT & ~OtrPolicy.SEND_WHITESPACE_TAG; - return new OtrPolicyImpl(this.configurator.getPropertyInt( - "GLOBAL_POLICY", defaultScOtrPolicy)); + return new OtrPolicyImpl(this.configurator.getPropertyInt("POLICY", + OtrPolicy.OTRL_POLICY_MANUAL)); } /** @@ -843,31 +662,31 @@ private class ScSessionStatusScheduler { private final Timer timer = new Timer(); - private final Map tasks = - new ConcurrentHashMap(); + private final Map tasks = + new ConcurrentHashMap(); public void scheduleScSessionStatusChange( - final OtrContact otrContact, final ScSessionStatus status) + final Contact contact, final ScSessionStatus status) { - cancel(otrContact); + cancel(contact); TimerTask task = new TimerTask() { @Override public void run() { - setSessionStatus(otrContact, status); + setSessionStatus(contact, status); } }; timer.schedule(task, SESSION_TIMEOUT); - tasks.put(otrContact, task); + tasks.put(contact, task); } - public void cancel(final OtrContact otrContact) + public void cancel(final Contact contact) { - TimerTask task = tasks.get(otrContact); + TimerTask task = tasks.get(contact); if (task != null) task.cancel(); - tasks.remove(otrContact); + tasks.remove(contact); } public void serviceChanged(ServiceEvent ev) @@ -884,15 +703,13 @@ public void serviceChanged(ServiceEvent ev) ProtocolProviderService provider = (ProtocolProviderService) service; - Iterator i = tasks.keySet().iterator(); + Iterator i = tasks.keySet().iterator(); while (i.hasNext()) { - OtrContact otrContact = i.next(); - if (provider.equals( - otrContact.contact.getProtocolProvider())) - { - cancel(otrContact); + Contact contact = i.next(); + if (provider.equals(contact.getProtocolProvider())) { + cancel(contact); i.remove(); } } @@ -900,15 +717,14 @@ public void serviceChanged(ServiceEvent ev) } } - private void setSessionStatus(OtrContact contact, ScSessionStatus status) + private void setSessionStatus(Contact contact, ScSessionStatus status) { scSessionStatusMap.put(getSessionID(contact), status); - scheduler.cancel(contact); for (ScOtrEngineListener l : getListeners()) l.sessionStatusChanged(contact); } - public ScSessionStatus getSessionStatus(OtrContact contact) + public ScSessionStatus getSessionStatus(Contact contact) { SessionID sessionID = getSessionID(contact); SessionStatus sessionStatus = otrEngine.getSessionStatus(sessionID); @@ -953,9 +769,9 @@ public void launchHelp() .getI18NString("plugin.otr.authbuddydialog.HELP_URI")); } - public void refreshSession(OtrContact otrContact) + public void refreshSession(Contact contact) { - SessionID sessionID = getSessionID(otrContact); + SessionID sessionID = getSessionID(contact); try { otrEngine.refreshSession(sessionID); @@ -1004,25 +820,24 @@ public void serviceChanged(ServiceEvent ev) synchronized(contactsMap) { - Iterator i = contactsMap.values().iterator(); + Iterator i = contactsMap.values().iterator(); while (i.hasNext()) - { - OtrContact otrContact = i.next(); - if (provider.equals( - otrContact.contact.getProtocolProvider())) + { + Contact contact = i.next(); + if (provider.equals(contact.getProtocolProvider())) { - scSessionStatusMap.remove(getSessionID(otrContact)); + scSessionStatusMap.remove(getSessionID(contact)); i.remove(); } } } - Iterator i = progressDialogMap.keySet().iterator(); + Iterator i = progressDialogMap.keySet().iterator(); while (i.hasNext()) { - if (provider.equals(i.next().contact.getProtocolProvider())) + if (provider.equals(i.next().getProtocolProvider())) i.remove(); } scheduler.serviceChanged(ev); @@ -1031,14 +846,7 @@ public void serviceChanged(ServiceEvent ev) public void setContactPolicy(Contact contact, OtrPolicy policy) { - ProtocolProviderService pps = contact.getProtocolProvider(); - SessionID sessionID - = new SessionID( - pps.getAccountID().getAccountUniqueID(), - contact.getAddress(), - pps.getProtocolName()); - - String propertyID = sessionID + "contact_policy"; + String propertyID = getSessionID(contact) + "policy"; if (policy == null) this.configurator.removeProperty(propertyID); else @@ -1051,9 +859,9 @@ public void setContactPolicy(Contact contact, OtrPolicy policy) public void setGlobalPolicy(OtrPolicy policy) { if (policy == null) - this.configurator.removeProperty("GLOBAL_POLICY"); + this.configurator.removeProperty("POLICY"); else - this.configurator.setProperty("GLOBAL_POLICY", policy.getPolicy()); + this.configurator.setProperty("POLICY", policy.getPolicy()); for (ScOtrEngineListener l : getListeners()) l.globalPolicyChanged(); @@ -1061,32 +869,30 @@ public void setGlobalPolicy(OtrPolicy policy) public void showError(SessionID sessionID, String err) { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) + Contact contact = getContact(sessionID); + if (contact == null) return; - Contact contact = otrContact.contact; OtrActivator.uiService.getChat(contact).addMessage( contact.getDisplayName(), new Date(), Chat.ERROR_MESSAGE, err, OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); } - public void startSession(OtrContact otrContact) + public void startSession(Contact contact) { - SessionID sessionID = getSessionID(otrContact); + SessionID sessionID = getSessionID(contact); - ScSessionStatus scSessionStatus = getSessionStatus(otrContact); + ScSessionStatus scSessionStatus = getSessionStatus(contact); scSessionStatus = ScSessionStatus.LOADING; scSessionStatusMap.put(sessionID, scSessionStatus); for (ScOtrEngineListener l : getListeners()) { - l.sessionStatusChanged(otrContact); + l.sessionStatusChanged(contact); + scheduler.scheduleScSessionStatusChange( + contact, ScSessionStatus.TIMED_OUT); } - scheduler.scheduleScSessionStatusChange( - otrContact, ScSessionStatus.TIMED_OUT); - try { otrEngine.startSession(sessionID); @@ -1098,9 +904,9 @@ public void startSession(OtrContact otrContact) } } - public String transformReceiving(OtrContact otrContact, String msgText) + public String transformReceiving(Contact contact, String msgText) { - SessionID sessionID = getSessionID(otrContact); + SessionID sessionID = getSessionID(contact); try { return otrEngine.transformReceiving(sessionID, msgText); @@ -1113,9 +919,9 @@ public String transformReceiving(OtrContact otrContact, String msgText) } } - public String transformSending(OtrContact otrContact, String msgText) + public String transformSending(Contact contact, String msgText) { - SessionID sessionID = getSessionID(otrContact); + SessionID sessionID = getSessionID(contact); try { return otrEngine.transformSending(sessionID, msgText); @@ -1128,25 +934,25 @@ public String transformSending(OtrContact otrContact, String msgText) } } - private Session getSession(OtrContact contact) + private Session getSession(Contact contact) { SessionID sessionID = getSessionID(contact); return otrEngine.getSession(sessionID); } @Override - public void initSmp(OtrContact otrContact, String question, String secret) + public void initSmp(Contact contact, String question, String secret) { - Session session = getSession(otrContact); + Session session = getSession(contact); try { session.initSmp(question, secret); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); } progressDialog.init(); @@ -1155,27 +961,24 @@ public void initSmp(OtrContact otrContact, String question, String secret) catch (OtrException e) { logger.error("Error initializing SMP session with contact " - + otrContact.contact.getDisplayName(), e); + + contact.getDisplayName(), e); showError(session.getSessionID(), e.getMessage()); } } @Override - public void respondSmp( OtrContact otrContact, - InstanceTag receiverTag, - String question, - String secret) + public void respondSmp(Contact contact, String question, String secret) { - Session session = getSession(otrContact); + Session session = getSession(contact); try { - session.respondSmp(receiverTag, question, secret); + session.respondSmp(question, secret); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); } progressDialog.incrementProgress(); @@ -1185,24 +988,24 @@ public void respondSmp( OtrContact otrContact, { logger.error( "Error occured when sending SMP response to contact " - + otrContact.contact.getDisplayName(), e); + + contact.getDisplayName(), e); showError(session.getSessionID(), e.getMessage()); } } @Override - public void abortSmp(OtrContact otrContact) + public void abortSmp(Contact contact) { - Session session = getSession(otrContact); + Session session = getSession(contact); try { session.abortSmp(); - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + SmpProgressDialog progressDialog = progressDialogMap.get(contact); if (progressDialog == null) { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); } progressDialog.dispose(); @@ -1210,47 +1013,9 @@ public void abortSmp(OtrContact otrContact) catch (OtrException e) { logger.error("Error aborting SMP session with contact " - + otrContact.contact.getDisplayName(), e); + + contact.getDisplayName(), e); showError(session.getSessionID(), e.getMessage()); } - } - - public PublicKey getRemotePublicKey(OtrContact otrContact) - { - if (otrContact == null) - return null; - - Session session = getSession(otrContact); - - return session.getRemotePublicKey(); - } - - public List getSessionInstances(OtrContact otrContact) - { - if (otrContact == null) - return null; - - return getSession(otrContact).getInstances(); - } - - public boolean setOutgoingSession(OtrContact contact, InstanceTag tag) - { - if (contact == null) - return false; - - Session session = getSession(contact); - - scSessionStatusMap.remove(session.getSessionID()); - return session.setOutgoingInstance(tag); - } - - public Session getOutgoingSession(OtrContact contact) - { - if (contact == null) - return null; - - SessionID sessionID = getSessionID(contact); - return otrEngine.getOutgoingSession(sessionID); } } diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java index ffd211ae2..790fc160a 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java @@ -6,7 +6,6 @@ */ package net.java.sip.communicator.plugin.otr; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; /** @@ -19,9 +18,5 @@ public interface ScOtrEngineListener public void globalPolicyChanged(); - public void sessionStatusChanged(OtrContact contact); - - public void multipleInstancesDetected(OtrContact contact); - - public void outgoingSessionChanged(OtrContact contact); + public void sessionStatusChanged(Contact contact); } diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java index 5b36f5855..56ffff002 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java @@ -7,9 +7,7 @@ package net.java.sip.communicator.plugin.otr; import java.security.*; -import java.util.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; /** @@ -24,24 +22,24 @@ public interface ScOtrKeyManager public abstract void removeListener(ScOtrKeyManagerListener l); - public abstract void verify(OtrContact contact, String fingerprint); + public abstract void verify(Contact contact); - public abstract void unverify(OtrContact contact, String fingerprint); + public abstract void unverify(Contact contact); - public abstract boolean isVerified(Contact contact, String fingerprint); + public abstract boolean isVerified(Contact contact); - public abstract String getFingerprintFromPublicKey(PublicKey pubKey); - - public abstract List getAllRemoteFingerprints(Contact contact); + public abstract String getRemoteFingerprint(Contact contact); public abstract String getLocalFingerprint(AccountID account); public abstract byte[] getLocalFingerprintRaw(AccountID account); - public abstract void saveFingerprint(Contact contact, String fingerprint); + public abstract void savePublicKey(Contact contact, PublicKey pubKey); + + public abstract PublicKey loadPublicKey(Contact contact); public abstract KeyPair loadKeyPair(AccountID accountID); public abstract void generateKeyPair(AccountID accountID); -} +} diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java index eb6d07be3..37adc6325 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java @@ -11,7 +11,6 @@ import java.util.*; import net.java.otr4j.crypto.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; /** @@ -63,113 +62,47 @@ public void removeListener(ScOtrKeyManagerListener l) } } - public void verify(OtrContact otrContact, String fingerprint) + public void verify(Contact contact) { - if ((fingerprint == null) || otrContact == null) + if ((contact == null) || isVerified(contact)) return; - this.configurator.setProperty(otrContact.contact.getAddress() + fingerprint - + ".fingerprint.verified", true); + this.configurator.setProperty(contact.getAddress() + + ".publicKey.verified", true); for (ScOtrKeyManagerListener l : getListeners()) - l.contactVerificationStatusChanged(otrContact); + l.contactVerificationStatusChanged(contact); } - public void unverify(OtrContact otrContact, String fingerprint) + public void unverify(Contact contact) { - if ((fingerprint == null) || otrContact == null) + if ((contact == null) || !isVerified(contact)) return; - this.configurator.setProperty(otrContact.contact.getAddress() + fingerprint - + ".fingerprint.verified", false); + this.configurator.removeProperty(contact.getAddress() + + ".publicKey.verified"); for (ScOtrKeyManagerListener l : getListeners()) - l.contactVerificationStatusChanged(otrContact); + l.contactVerificationStatusChanged(contact); } - public boolean isVerified(Contact contact, String fingerprint) + public boolean isVerified(Contact contact) { - if (fingerprint == null || contact == null) + if (contact == null) return false; - return this.configurator.getPropertyBoolean( - contact.getAddress() + fingerprint - + ".fingerprint.verified", false); + return this.configurator.getPropertyBoolean(contact.getAddress() + + ".publicKey.verified", false); } - public List getAllRemoteFingerprints(Contact contact) + public String getRemoteFingerprint(Contact contact) { - if (contact == null) + PublicKey remotePublicKey = loadPublicKey(contact); + if (remotePublicKey == null) return null; - - /* - * The following lines are needed for backward compatibility with old - * versions of the otr plugin. Instead of lists of fingerprints the otr - * plugin used to store one public key for every contact in the form of - * "userID.publicKey=..." and one boolean property in the form of - * "userID.publicKey.verified=...". In order not to loose these old - * properties we have to convert them to match the new format. - */ - String userID = contact.getAddress(); - - byte[] b64PubKey = - this.configurator.getPropertyBytes(userID + ".publicKey"); - if (b64PubKey != null) - { - // We delete the old format property because we are going to convert - // it in the new format - this.configurator.removeProperty(userID + ".publicKey"); - - X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey); - - KeyFactory keyFactory; - try - { - keyFactory = KeyFactory.getInstance("DSA"); - PublicKey pubKey = keyFactory.generatePublic(publicKeySpec); - - boolean isVerified = - this.configurator.getPropertyBoolean(userID - + ".publicKey.verified", false); - - // We also make sure to delete this old format property if it - // exists. - this.configurator.removeProperty(userID + ".publicKey.verified"); - - String fingerprint = getFingerprintFromPublicKey(pubKey); - - // Now we can store the old properties in the new format. - if (isVerified) - verify(OtrContactManager.getOtrContact(contact, null), fingerprint); - else - unverify(OtrContactManager.getOtrContact(contact, null), fingerprint); - - // Finally we append the new fingerprint to out stored list of - // fingerprints. - this.configurator.appendProperty( - userID + ".fingerprints", fingerprint); - } - catch (NoSuchAlgorithmException e) - { - e.printStackTrace(); - } - catch (InvalidKeySpecException e) - { - e.printStackTrace(); - } - } - - // Now we can safely return our list of fingerprints for this contact - // without worrying that we missed an old format property. - return this.configurator.getAppendedProperties( - contact.getAddress() + ".fingerprints"); - } - - public String getFingerprintFromPublicKey(PublicKey pubKey) - { try { - return new OtrCryptoEngineImpl().getFingerprint(pubKey); + return new OtrCryptoEngineImpl().getFingerprint(remotePublicKey); } catch (OtrCryptoException e) { @@ -218,16 +151,52 @@ public byte[] getLocalFingerprintRaw(AccountID account) } } - public void saveFingerprint(Contact contact, String fingerprint) + public void savePublicKey(Contact contact, PublicKey pubKey) { if (contact == null) return; - this.configurator.appendProperty(contact.getAddress() + ".fingerprints", - fingerprint); + X509EncodedKeySpec x509EncodedKeySpec = + new X509EncodedKeySpec(pubKey.getEncoded()); + + this.configurator.setProperty(contact.getAddress() + ".publicKey", + x509EncodedKeySpec.getEncoded()); + + this.configurator.removeProperty(contact.getAddress() + + ".publicKey.verified"); + } + + public PublicKey loadPublicKey(Contact contact) + { + if (contact == null) + return null; - this.configurator.setProperty(contact.getAddress() + fingerprint - + ".fingerprint.verified", false); + String userID = contact.getAddress(); + + byte[] b64PubKey = + this.configurator.getPropertyBytes(userID + ".publicKey"); + if (b64PubKey == null) + return null; + + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey); + + // Generate KeyPair. + KeyFactory keyFactory; + try + { + keyFactory = KeyFactory.getInstance("DSA"); + return keyFactory.generatePublic(publicKeySpec); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + return null; + } + catch (InvalidKeySpecException e) + { + e.printStackTrace(); + return null; + } } public KeyPair loadKeyPair(AccountID account) diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerListener.java b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerListener.java index ba2213f1c..7ab2c6f02 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerListener.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerListener.java @@ -6,7 +6,7 @@ */ package net.java.sip.communicator.plugin.otr; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; +import net.java.sip.communicator.service.protocol.*; /** * @@ -14,5 +14,5 @@ */ public interface ScOtrKeyManagerListener { - public void contactVerificationStatusChanged(OtrContact contact); + public void contactVerificationStatusChanged(Contact contact); } diff --git a/src/net/java/sip/communicator/plugin/otr/SwingOtrActionHandler.java b/src/net/java/sip/communicator/plugin/otr/SwingOtrActionHandler.java index 0f5343461..309e38750 100644 --- a/src/net/java/sip/communicator/plugin/otr/SwingOtrActionHandler.java +++ b/src/net/java/sip/communicator/plugin/otr/SwingOtrActionHandler.java @@ -6,12 +6,12 @@ */ package net.java.sip.communicator.plugin.otr; +import net.java.sip.communicator.plugin.otr.authdialog.*; +import net.java.sip.communicator.service.protocol.*; + import java.awt.*; import java.util.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; -import net.java.sip.communicator.plugin.otr.authdialog.*; - /** * Default OtrActionHandler implementation that opens SWING buddy authenticate * dialog. @@ -24,13 +24,13 @@ public class SwingOtrActionHandler { public void onAuthenticateLinkClicked(UUID uuid) { - OtrContact otrContact = ScOtrEngineImpl.getOtrContact( + Contact contact = ScOtrEngineImpl.getContact( ScOtrEngineImpl.getScSessionForGuid(uuid).getSessionID()); - openAuthDialog(otrContact); + openAuthDialog(contact); } - public static void openAuthDialog(OtrContact contact) + public static void openAuthDialog(Contact contact) { // Launch auth buddy dialog. OtrBuddyAuthenticationDialog authenticateBuddyDialog diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java b/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java index 7c6119399..26f88662f 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java @@ -7,14 +7,13 @@ package net.java.sip.communicator.plugin.otr.authdialog; import java.awt.*; -import java.security.*; import javax.swing.*; import javax.swing.event.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; +import net.java.sip.communicator.service.protocol.*; /** * @author George Politis @@ -29,7 +28,7 @@ public class FingerprintAuthenticationPanel /** * The Contact that we are authenticating. */ - private final OtrContact otrContact; + private final Contact contact; private SIPCommTextField txtRemoteFingerprintComparison; @@ -61,9 +60,9 @@ public class FingerprintAuthenticationPanel * * @param contact The contact that this panel refers to. */ - FingerprintAuthenticationPanel(OtrContact contact) + FingerprintAuthenticationPanel(Contact contact) { - this.otrContact = contact; + this.contact = contact; initComponents(); loadContact(); @@ -108,13 +107,8 @@ private void initComponents() cbAction = new JComboBox(); cbAction.addItem(actionIHave); cbAction.addItem(actionIHaveNot); - - PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(otrContact); - String remoteFingerprint = - OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); cbAction.setSelectedItem(OtrActivator.scOtrKeyManager - .isVerified(otrContact.contact, remoteFingerprint) - ? actionIHave : actionIHaveNot); + .isVerified(contact) ? actionIHave : actionIHaveNot); pnlAction.add(cbAction, c); @@ -122,14 +116,10 @@ private void initComponents() c.weightx = 1.0; pnlAction.add(txtAction, c); - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - - txtRemoteFingerprintComparison = new SIPCommTextField( + txtRemoteFingerprintComparison = new SIPCommTextField( OtrActivator.resourceService .getI18NString("plugin.otr.authbuddydialog.FINGERPRINT_CHECK", - new String[] - {otrContact.contact.getDisplayName() + resourceName})); + new String[]{contact.getDisplayName()})); txtRemoteFingerprintComparison.getDocument().addDocumentListener(this); c.gridwidth = 2; @@ -146,25 +136,24 @@ public JComboBox getCbAction() /** * Sets up the {@link OtrBuddyAuthenticationDialog} components so that they - * reflect the {@link OtrBuddyAuthenticationDialog#otrContact} + * reflect the {@link OtrBuddyAuthenticationDialog#contact} */ private void loadContact() { // Local fingerprint. String account = - otrContact.contact.getProtocolProvider().getAccountID().getDisplayName(); + contact.getProtocolProvider().getAccountID().getDisplayName(); String localFingerprint = - OtrActivator.scOtrKeyManager.getLocalFingerprint(otrContact.contact + OtrActivator.scOtrKeyManager.getLocalFingerprint(contact .getProtocolProvider().getAccountID()); txtLocalFingerprint.setText(OtrActivator.resourceService.getI18NString( "plugin.otr.authbuddydialog.LOCAL_FINGERPRINT", new String[] { account, localFingerprint })); // Remote fingerprint. - String user = otrContact.contact.getDisplayName(); - PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(otrContact); + String user = contact.getDisplayName(); String remoteFingerprint = - OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); + OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact); txtRemoteFingerprint.setText(OtrActivator.resourceService .getI18NString("plugin.otr.authbuddydialog.REMOTE_FINGERPRINT", new String[] @@ -193,10 +182,6 @@ public void changedUpdate(DocumentEvent e) public void compareFingerprints() { - PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(otrContact); - String remoteFingerprint = - OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); - if(txtRemoteFingerprintComparison.getText() == null || txtRemoteFingerprintComparison.getText().length() == 0) { @@ -204,7 +189,8 @@ public void compareFingerprints() return; } if(txtRemoteFingerprintComparison.getText().toLowerCase().contains( - remoteFingerprint.toLowerCase())) + OtrActivator.scOtrKeyManager + .getRemoteFingerprint(contact).toLowerCase())) { txtRemoteFingerprintComparison.setBackground(Color.green); cbAction.setSelectedItem(actionIHave); diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsPanel.java b/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsPanel.java index 14f79dc8b..256814a81 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsPanel.java @@ -13,9 +13,9 @@ import javax.swing.border.*; import javax.swing.event.*; +import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.service.protocol.*; /** * @author @George Politis @@ -44,7 +44,7 @@ public KnownFingerprintsPanel() this.setPreferredSize(new Dimension(400, 200)); - openContact(getSelectedContact(), getSelectedFingerprint()); + openContact(getSelectedContact()); } /** @@ -74,7 +74,7 @@ public void valueChanged(ListSelectionEvent e) if (e.getValueIsAdjusting()) return; - openContact(getSelectedContact(), getSelectedFingerprint()); + openContact(getSelectedContact()); } }); @@ -88,16 +88,14 @@ public void valueChanged(ListSelectionEvent e) btnVerifyFingerprint = new JButton(); btnVerifyFingerprint.setText(OtrActivator.resourceService .getI18NString("plugin.otr.configform.VERIFY_FINGERPRINT")); - btnVerifyFingerprint.setEnabled(false); btnVerifyFingerprint.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { OtrActivator.scOtrKeyManager - .verify(OtrContactManager.getOtrContact( - getSelectedContact(), null), getSelectedFingerprint()); - openContact(getSelectedContact(), getSelectedFingerprint()); + .verify(getSelectedContact()); + openContact(getSelectedContact()); contactsTable.updateUI(); } }); @@ -107,16 +105,13 @@ public void actionPerformed(ActionEvent arg0) btnForgetFingerprint = new JButton(); btnForgetFingerprint.setText(OtrActivator.resourceService .getI18NString("plugin.otr.configform.FORGET_FINGERPRINT")); - btnForgetFingerprint.setEnabled(false); - btnForgetFingerprint.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { OtrActivator.scOtrKeyManager - .unverify(OtrContactManager.getOtrContact( - getSelectedContact(), null), getSelectedFingerprint()); - openContact(getSelectedContact(), getSelectedFingerprint()); + .unverify(getSelectedContact()); + openContact(getSelectedContact()); contactsTable.updateUI(); } }); @@ -134,27 +129,10 @@ private Contact getSelectedContact() KnownFingerprintsTableModel model = (KnownFingerprintsTableModel) contactsTable.getModel(); int index = contactsTable.getSelectedRow(); - if (index < 0 || index > model.getRowCount()) - return null; - - return model.getContactFromRow(index); - } - - /** - * Gets the selected fingerprint for this - * {@link KnownFingerprintsTableModel} - * - * return the selected fingerprint - */ - private String getSelectedFingerprint() - { - KnownFingerprintsTableModel model = - (KnownFingerprintsTableModel) contactsTable.getModel(); - int index = contactsTable.getSelectedRow(); - if (index < 0 || index > model.getRowCount()) + if (index < 0 || index > model.allContacts.size()) return null; - return model.getFingerprintFromRow(index); + return model.allContacts.get(index); } /** @@ -162,11 +140,11 @@ private String getSelectedFingerprint() * reflect the {@link Contact} param. * * @param contact the {@link Contact} to setup the components for. - * @param fingerprint the fingerprint to setup the components for. */ - private void openContact(Contact contact, String fingerprint) + private void openContact(Contact contact) { - if (contact == null || fingerprint == null) + if (contact == null + || OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact) == null) { btnForgetFingerprint.setEnabled(false); btnVerifyFingerprint.setEnabled(false); @@ -175,7 +153,7 @@ private void openContact(Contact contact, String fingerprint) { boolean verified = OtrActivator.scOtrKeyManager - .isVerified(contact, fingerprint); + .isVerified(contact); btnForgetFingerprint.setEnabled(verified); btnVerifyFingerprint.setEnabled(!verified); diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java b/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java index 991883dbb..1b32f26dd 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java @@ -8,12 +8,10 @@ import java.awt.*; import java.util.*; -import java.util.List; import javax.swing.table.*; import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.protocol.*; @@ -27,7 +25,6 @@ */ public class KnownFingerprintsTableModel extends AbstractTableModel - implements ScOtrKeyManagerListener { /** * Serial version UID. @@ -40,8 +37,7 @@ public class KnownFingerprintsTableModel public static final int FINGERPRINT_INDEX = 2; - public final LinkedHashMap> allContactsFingerprints = - new LinkedHashMap>(); + public final java.util.List allContacts = new Vector(); public KnownFingerprintsTableModel() { @@ -80,15 +76,10 @@ public KnownFingerprintsTableModel() Iterator contacts = metaContact.getContacts(); while (contacts.hasNext()) { - Contact contact = contacts.next(); - allContactsFingerprints.put( - contact, - OtrActivator.scOtrKeyManager.getAllRemoteFingerprints( - contact)); + allContacts.add(contacts.next()); } } } - OtrActivator.scOtrKeyManager.addListener(this); } /** @@ -121,8 +112,10 @@ public String getColumnName(int column) */ public Object getValueAt(int row, int column) { - Contact contact = getContactFromRow(row); - String fingerprint = getFingerprintFromRow(row); + if (row < 0) + return null; + + Contact contact = allContacts.get(row); switch (column) { case CONTACTNAME_INDEX: @@ -130,84 +123,25 @@ public Object getValueAt(int row, int column) case VERIFIED_INDEX: // TODO: Maybe use a CheckBoxColumn? return (OtrActivator.scOtrKeyManager - .isVerified(contact, fingerprint)) + .isVerified(contact)) ? OtrActivator.resourceService.getI18NString( "plugin.otr.configform.COLUMN_VALUE_VERIFIED_TRUE") : OtrActivator.resourceService.getI18NString( "plugin.otr.configform.COLUMN_VALUE_VERIFIED_FALSE"); case FINGERPRINT_INDEX: - return fingerprint; + return OtrActivator.scOtrKeyManager + .getRemoteFingerprint(contact); default: return null; } } - Contact getContactFromRow(int row) - { - if (row < 0 || row >= getRowCount()) - return null; - - int index = -1; - Contact contact = null; - for (Map.Entry> entry : - allContactsFingerprints.entrySet()) - { - boolean found = false; - contact = entry.getKey(); - List fingerprints = entry.getValue(); - for (String f : fingerprints) - { - index++; - if (index == row) - { - found = true; - break; - } - } - if (found) break; - } - - return contact; - } - - String getFingerprintFromRow(int row) - { - if (row < 0 || row >= getRowCount()) - return null; - - int index = -1; - String fingerprint = null; - for (Map.Entry> entry : - allContactsFingerprints.entrySet()) - { - boolean found = false; - List fingerprints = entry.getValue(); - for (String f : fingerprints) - { - index++; - fingerprint = f; - if (index == row) - { - found = true; - break; - } - } - if (found) break; - } - - return fingerprint; - } - /** * Implements AbstractTableModel#getRowCount(). */ public int getRowCount() { - int rowCount = 0; - for (Map.Entry> entry : - allContactsFingerprints.entrySet()) - rowCount += entry.getValue().size(); - return rowCount; + return allContacts.size(); } /** @@ -217,14 +151,4 @@ public int getColumnCount() { return 3; } - - @Override - public void contactVerificationStatusChanged(OtrContact otrContact) - { - Contact contact = otrContact.contact; - allContactsFingerprints.put( - contact, - OtrActivator.scOtrKeyManager.getAllRemoteFingerprints(contact)); - this.fireTableDataChanged(); - } } diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/OTRv3OutgoingSessionSwitcher.java b/src/net/java/sip/communicator/plugin/otr/authdialog/OTRv3OutgoingSessionSwitcher.java deleted file mode 100644 index 39b22e373..000000000 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/OTRv3OutgoingSessionSwitcher.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. - */ -package net.java.sip.communicator.plugin.otr.authdialog; - -import java.awt.*; -import java.awt.event.*; -import java.io.*; -import java.security.*; -import java.util.*; - -import javax.imageio.*; -import javax.swing.*; -import javax.swing.Timer; - -import net.java.otr4j.session.*; -import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; -import net.java.sip.communicator.service.contactlist.*; -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.gui.Container; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * A special {@link JMenuBar} that controls the switching of OTRv3 outgoing - * sessions in case the remote party is logged in multiple times. - * - * @author Marin Dzhigarov - * - */ -public class OTRv3OutgoingSessionSwitcher - extends SIPCommMenuBar - implements PluginComponent, - ActionListener, - ScOtrEngineListener, - ScOtrKeyManagerListener -{ - - private static final Logger logger - = Logger.getLogger(OTRv3OutgoingSessionSwitcher.class); - - private final PluginComponentFactory parentFactory; - - private static final long serialVersionUID = 0L; - - private final SelectorMenu menu = new SelectorMenu(); - - private ButtonGroup buttonGroup = new ButtonGroup(); - - private OtrContact contact; - - /** - * A map used for storing each Sessions corresponding JMenuItem - * . - */ - private final Map outgoingSessions - = new HashMap(); - - /** - * An animated {@link JMenu} - * @author Marin Dzhigarov - * - */ - private static class SelectorMenu - extends SIPCommMenu - { - /** - * Serial version UID. - */ - private static final long serialVersionUID = 0L; - - Image image = OtrActivator.resourceService.getImage( - "service.gui.icons.DOWN_ARROW_ICON").getImage(); - - private static float alpha = 0.95f; - - private final Timer alphaChanger = new Timer(20, new ActionListener() { - - private float incrementer = -.03f; - - private int fadeCycles = 0; - - @Override - public void actionPerformed(ActionEvent e) - { - float newAlpha = alpha + incrementer; - if (newAlpha < 0.2f) - { - newAlpha = 0.2f; - incrementer = -incrementer; - } else if (newAlpha > 0.85f) - { - newAlpha = 0.85f; - incrementer = -incrementer; - fadeCycles++; - } - alpha = newAlpha; - if (fadeCycles >= 3) - { - alphaChanger.stop(); - fadeCycles = 0; - alpha = 1f; - } - SelectorMenu.this.repaint(); - } - }); - - @Override - public void paintComponent(Graphics g) - { - Graphics2D g2d = (Graphics2D) g; - g2d.setComposite( - AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); - g.drawImage(image, getWidth() - image.getWidth(this) - 1, - (getHeight() - image.getHeight(this) - 1) / 2, this); - - super.paintComponent(g2d); - } - - /** - * Creates a fade in and out effect for this {@link JMenu} - */ - public void fadeAnimation() - { - alphaChanger.stop(); - alpha = 0.85f; - SelectorMenu.this.repaint(); - alphaChanger.start(); - } - }; - - /** - * The OTRv3OutgoingSessionSwitcher constructor - */ - public OTRv3OutgoingSessionSwitcher(Container container, - PluginComponentFactory parentFactory) - { - this.parentFactory = parentFactory; - - setPreferredSize(new Dimension(30, 28)); - setMaximumSize(new Dimension(30, 28)); - setMinimumSize(new Dimension(30, 28)); - - this.menu.setPreferredSize(new Dimension(30, 45)); - this.menu.setMaximumSize(new Dimension(30, 45)); - - this.add(menu); - - this.setBorder(null); - this.menu.setBorder(null); - this.menu.setOpaque(false); - this.setOpaque(false); - this.menu.setVisible(false); - - /* - * XXX This OtrV3OutgoingSessionSwitcher instance cannot be added as a - * listener to scOtrEngine and scOtrKeyManager without being removed - * later on because the latter live forever. Unfortunately, the - * dispose() method of this instance is never executed. OtrWeakListener - * will keep this instance as a listener of scOtrEngine and - * scOtrKeyManager for as long as this instance is necessary. And this - * instance will be strongly referenced by the JMenuItems which depict - * it. So when the JMenuItems are gone, this instance will become - * obsolete and OtrWeakListener will remove it as a listener of - * scOtrEngine and scOtrKeyManager. - */ - new OtrWeakListener( - this, - OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); - - try - { - finishedPadlockImage = new ImageIcon(ImageIO.read( - OtrActivator.resourceService.getImageURL( - "plugin.otr.FINISHED_ICON_BLACK_16x16"))); - verifiedLockedPadlockImage = new ImageIcon(ImageIO.read( - OtrActivator.resourceService.getImageURL( - "plugin.otr.ENCRYPTED_ICON_BLACK_16x16"))); - unverifiedLockedPadlockImage = new ImageIcon(ImageIO.read( - OtrActivator.resourceService.getImageURL( - "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_BLACK_16x16"))); - unlockedPadlockImage = new ImageIcon(ImageIO.read( - OtrActivator.resourceService.getImageURL( - "plugin.otr.PLAINTEXT_ICON_16x16"))); - } catch (IOException e) - { - logger.debug("Failed to load padlock image"); - } - - buildMenu(contact); - } - - @Override - public int getPositionIndex() - { - return -1; - } - - /** - * Sets the current contact. Meant to be used by plugin components that - * are interested of the current contact. The current contact is the contact - * for the currently selected chat transport. - * - * @param contact the current contact - */ - public void setCurrentContact(Contact contact) - { - if (this.contact != null && this.contact.contact == contact) - return; - - this.contact = - OtrContactManager.getOtrContact(contact, null); - buildMenu(this.contact); - } - - /** - * Sets the current meta contact. Meant to be used by plugin components that - * are interested of the current contact. The current contact could be the - * contact currently selected in the contact list or the contact for the - * currently selected chat, etc. It depends on the container, where this - * component is meant to be added. - * - * @param metaContact the current meta contact - */ - public void setCurrentContact(MetaContact metaContact) - { - setCurrentContact((metaContact == null) ? null : metaContact - .getDefaultContact()); - } - - /** - * Sets the current contact. Meant to be used by plugin components that - * are interested of the current contact. The current contact is the contact - * for the currently selected chat transport. - * - * @param contact the current contact - * @param resourceName the ContactResource name. Some components - * may be interested in a particular ContactResource of a contact. - */ - public void setCurrentContact(Contact contact, String resourceName) - { - if (this.contact != null && this.contact.contact == contact) - return; - - if (resourceName == null) - { - this.contact = - OtrContactManager.getOtrContact(contact, null); - buildMenu(this.contact); - } - else - { - for (ContactResource resource : contact.getResources()) - { - if (resource.getResourceName().equals(resourceName)) - { - OtrContact otrContact = - OtrContactManager.getOtrContact(contact, resource); - if (this.contact == otrContact) - return; - this.contact = otrContact; - buildMenu(this.contact); - } - } - } - } - - @Override - public void setCurrentContactGroup(MetaContactGroup metaGroup) {} - - @Override - public void setCurrentAccountID(AccountID accountID) {} - - @Override - public PluginComponentFactory getParentFactory() - { - return parentFactory; - } - - /** - * Implements ScOtrKeyManagerListener#contactVerificationStatusChanged( - * Contact). - */ - public void contactVerificationStatusChanged(OtrContact contact) - { - buildMenu(contact); - if (this.menu.isVisible()) - this.menu.fadeAnimation(); - } - - /** - * Implements ScOtrEngineListener#contactPolicyChanged(Contact). - */ - public void contactPolicyChanged(Contact contact) {} - - /** - * Implements ScOtrKeyManagerListener#globalPolicyChanged(). - */ - public void globalPolicyChanged() {} - - /** - * Implements ScOtrEngineListener#sessionStatusChanged(OtrContact). - */ - public void sessionStatusChanged(OtrContact contact) - { - buildMenu(contact); - if (this.menu.isVisible()) - this.menu.fadeAnimation(); - } - - /** - * Implements ScOtrEngineListener#multipleInstancesDetected(OtrContact). - */ - public void multipleInstancesDetected(OtrContact contact) - { - buildMenu(contact); - if (this.menu.isVisible()) - this.menu.fadeAnimation(); - } - - /** - * Implements ScOtrEngineListener#outgoingSessionChanged(OtrContact). - */ - public void outgoingSessionChanged(OtrContact contact) - { - buildMenu(contact); - } - - private ImageIcon verifiedLockedPadlockImage; - - private ImageIcon unverifiedLockedPadlockImage; - - private ImageIcon finishedPadlockImage; - - private ImageIcon unlockedPadlockImage; - - /** - * Builds the JMenu used for switching between outgoing OTRv3 Sessions in - * case the remote party is logged in multiple locations - * - * @param otrContact the contact which is logged in multiple locations - */ - private void buildMenu(OtrContact otrContact) - { - if (otrContact == null || !this.contact.equals(otrContact)) - { - return; - } - menu.removeAll(); - java.util.List multipleInstances = - OtrActivator.scOtrEngine.getSessionInstances( - otrContact); - - Session outgoingSession = - OtrActivator.scOtrEngine.getOutgoingSession(otrContact); - int index = 0; - for (Session session : multipleInstances) - { - index++; - if (!outgoingSessions.containsKey(session)) - { - JMenuItem menuItem = new JRadioButtonMenuItem(); - outgoingSessions.put(session, menuItem); - menuItem.addActionListener(this); - } - - JMenuItem menuItem = outgoingSessions.get(session); - menuItem.setText("Session " + index); - - ImageIcon imageIcon = null; - switch (session.getSessionStatus(session.getReceiverInstanceTag())) - { - case ENCRYPTED: - PublicKey pubKey = - session.getRemotePublicKey(session.getReceiverInstanceTag()); - String fingerprint = - OtrActivator.scOtrKeyManager. - getFingerprintFromPublicKey(pubKey); - imageIcon - = OtrActivator.scOtrKeyManager.isVerified( - otrContact.contact, fingerprint) - ? verifiedLockedPadlockImage - : unverifiedLockedPadlockImage; - break; - case FINISHED: - imageIcon = finishedPadlockImage; - break; - case PLAINTEXT: - imageIcon = unlockedPadlockImage; - break; - } - menuItem.setIcon(imageIcon); - - menu.add(menuItem); - SelectedObject selectedObject = - new SelectedObject(imageIcon, session); - - buttonGroup.add(menuItem); - menuItem.repaint(); - if (session == outgoingSession) - { - this.menu.setSelected(selectedObject); - setSelected(menu.getItem(index - 1)); - } - - } - updateEnableStatus(); - menu.repaint(); - } - - public void actionPerformed(ActionEvent e) - { - for (Map.Entry entry : outgoingSessions.entrySet()) - { - JMenuItem menuItem = (JRadioButtonMenuItem) e.getSource(); - if (menuItem.equals(entry.getValue())) - { - OtrActivator.scOtrEngine.setOutgoingSession( - contact, entry.getKey().getReceiverInstanceTag()); - break; - } - } - } - - /** - * Sets the menu visibility. The menu is visible as soon as it - * contains two or more items. If it is empty, it is invisible. - */ - private void updateEnableStatus() - { - this.menu.setVisible(this.menu.getItemCount() > 1); - } -} diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java index 3c4b49d61..b67ce810c 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java @@ -8,14 +8,12 @@ import java.awt.*; import java.awt.event.*; -import java.security.*; import javax.swing.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; -import net.java.sip.communicator.plugin.otr.authdialog.FingerprintAuthenticationPanel.ActionComboBoxItem; +import net.java.sip.communicator.plugin.otr.authdialog.FingerprintAuthenticationPanel.*; import net.java.sip.communicator.service.protocol.*; /** @@ -26,7 +24,7 @@ public class OtrBuddyAuthenticationDialog extends SIPCommDialog { - private final OtrContact contact; + private final Contact contact; /** * The {@link OtrBuddyAuthenticationDialog} ctor. @@ -34,7 +32,7 @@ public class OtrBuddyAuthenticationDialog * @param contact The {@link Contact} this * {@link OtrBuddyAuthenticationDialog} refers to. */ - public OtrBuddyAuthenticationDialog(OtrContact contact) + public OtrBuddyAuthenticationDialog(Contact contact) { super(false); this.contact = contact; @@ -180,20 +178,13 @@ else if (authenticationMethod.equals(am[2])) ActionComboBoxItem actionItem = (ActionComboBoxItem) fingerprintPanel. getCbAction().getSelectedItem(); - PublicKey pubKey = - OtrActivator.scOtrEngine.getRemotePublicKey(contact); - String fingerprint = - OtrActivator.scOtrKeyManager. - getFingerprintFromPublicKey(pubKey); switch (actionItem.action) { case I_HAVE: - OtrActivator.scOtrKeyManager.verify( - contact, fingerprint); + OtrActivator.scOtrKeyManager.verify(contact); break; case I_HAVE_NOT: - OtrActivator.scOtrKeyManager.unverify( - contact, fingerprint); + OtrActivator.scOtrKeyManager.unverify(contact); break; } dispose(); diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrConfigurationPanel.java b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrConfigurationPanel.java index 3b5b878f2..79ce6bcf6 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrConfigurationPanel.java @@ -4,7 +4,7 @@ * Distributable under LGPL license. * See terms of license at gnu.org. */ -package net.java.sip.communicator.plugin.otr.authdialog; +package net.java.sip.communicator.plugin.otr; import java.awt.*; import java.awt.event.*; @@ -14,9 +14,9 @@ import javax.swing.border.*; import net.java.otr4j.*; -import net.java.sip.communicator.plugin.desktoputil.*; -import net.java.sip.communicator.plugin.otr.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.otr.authdialog.*; /** * A special {@link Panel} that manages the OTR configuration. @@ -249,7 +249,12 @@ public void loadPolicy() cbAutoInitiate.setEnabled(otrEnabled); cbRequireOtr.setEnabled(otrEnabled); + String autoInitPropValue + = OtrActivator.configService.getString( + OtrActivator.AUTO_INIT_OTR_PROP); boolean isAutoInit = otrPolicy.getEnableAlways(); + if (autoInitPropValue != null) + isAutoInit = Boolean.parseBoolean(autoInitPropValue); cbAutoInitiate.setSelected(isAutoInit); @@ -317,7 +322,10 @@ public void actionPerformed(ActionEvent e) boolean isAutoInit = ((JCheckBox) e.getSource()).isSelected(); - otrPolicy.setSendWhitespaceTag(isAutoInit); + otrPolicy.setEnableAlways(isAutoInit); + OtrActivator.configService.setProperty( + OtrActivator.AUTO_INIT_OTR_PROP, + Boolean.toString(isAutoInit)); OtrActivator.scOtrEngine.setGlobalPolicy(otrPolicy); diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/SmpAuthenticateBuddyDialog.java b/src/net/java/sip/communicator/plugin/otr/authdialog/SmpAuthenticateBuddyDialog.java index 6f72ce8d0..7d91c7ff3 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/SmpAuthenticateBuddyDialog.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/SmpAuthenticateBuddyDialog.java @@ -11,10 +11,9 @@ import javax.swing.*; -import net.java.otr4j.session.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.otr.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; +import net.java.sip.communicator.service.protocol.*; /** * The dialog that pops up when the remote party send us SMP @@ -27,17 +26,13 @@ public class SmpAuthenticateBuddyDialog extends SIPCommDialog { - private final OtrContact otrContact; + private final Contact contact; private final String question; - private final InstanceTag receiverTag; - - public SmpAuthenticateBuddyDialog( - OtrContact contact, InstanceTag receiverTag, String question) + public SmpAuthenticateBuddyDialog(Contact contact, String question) { - this.otrContact = contact; - this.receiverTag = receiverTag; + this.contact = contact; this.question = question; initComponents(); } @@ -65,17 +60,12 @@ private void initComponents() , Font.BOLD , 14); authenticationFrom.setFont(newFont); - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; String authFromText = String.format( OtrActivator.resourceService .getI18NString( "plugin.otr.authbuddydialog.AUTHENTICATION_FROM", - new String[] - {otrContact.contact.getDisplayName() + - resourceName})); + new String[] {contact.getDisplayName()})); authenticationFrom.setText(authFromText); mainPanel.add(authenticationFrom); @@ -180,7 +170,7 @@ public void actionPerformed(ActionEvent arg0) { public void actionPerformed(ActionEvent e) { - OtrActivator.scOtrEngine.abortSmp(otrContact); + OtrActivator.scOtrEngine.abortSmp(contact); SmpAuthenticateBuddyDialog.this.dispose(); } }); @@ -198,7 +188,7 @@ public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e) { OtrActivator.scOtrEngine.respondSmp( - otrContact, receiverTag, question, answerTextBox.getText()); + contact, question, answerTextBox.getText()); SmpAuthenticateBuddyDialog.this.dispose(); } }); diff --git a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf index 07807d412..73f917a57 100644 --- a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf +++ b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf @@ -32,5 +32,4 @@ Import-Package: org.osgi.framework, org.bouncycastle.crypto.engines, org.bouncycastle.crypto.modes, org.bouncycastle.util, - org.bouncycastle.util.encoders, - net.java.sip.communicator.service.msghistory + org.bouncycastle.util.encoders