diff options
author | Marin <m.dzhigarov@gmail.com> | 2013-12-20 13:29:16 +0200 |
---|---|---|
committer | Marin <m.dzhigarov@gmail.com> | 2014-01-10 12:35:17 +0200 |
commit | c164b0aa0a2315d9b0c0f32a0fdb4826ebb7ff9d (patch) | |
tree | 6d32c02006a10a8f180b9fd7dbe7b35357a22d3b | |
parent | 8ac902456870615486df0fbc4bb073695d93a35b (diff) | |
download | jitsi-c164b0aa0a2315d9b0c0f32a0fdb4826ebb7ff9d.zip jitsi-c164b0aa0a2315d9b0c0f32a0fdb4826ebb7ff9d.tar.gz jitsi-c164b0aa0a2315d9b0c0f32a0fdb4826ebb7ff9d.tar.bz2 |
Updates the otr plugin to use the latest version of the OTR protocol.
21 files changed, 955 insertions, 80 deletions
diff --git a/lib/installer-exclude/otr4j.jar b/lib/installer-exclude/otr4j.jar Binary files differindex fc22915..f755a6a 100644 --- a/lib/installer-exclude/otr4j.jar +++ b/lib/installer-exclude/otr4j.jar diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 9a4252d..00bb3b9 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -1611,6 +1611,10 @@ plugin.otr.activator.fallbackmessage=<span style="font-weight: bold;">{0} is try <a href="http://en.wikipedia.org/wiki/Off-the-Record_Messaging">Off-The-Record conversation</a> with you. However, your software does not support \ Off-The-Record messaging. For more information see <a href="http://en.wikipedia.org/wiki/Off-the-Record_Messaging">\ http://en.wikipedia.org/wiki/Off-the-Record_Messaging</a></span> +plugin.otr.activator.multipleinstancesdetected=Your buddy {0} is logged in multiple times and OTR has established multiple sessions. You can select \ +an outgoing session from the menu above. +plugin.otr.activator.msgfromanotherinstance={0} sent you a message that was intended for another session. If you are logged in multiple times \ +another session may have received this message. # global proxy plugin plugin.globalproxy.GLOBAL_PROXY_CONFIG=Global Proxy diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatPanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatPanel.java index 4853797..9a80325 100644 --- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatPanel.java @@ -323,6 +323,7 @@ public class ChatPanel topSplitPane.setRightComponent(rightPanel); topPanel.add(topSplitPane); + } else { @@ -354,6 +355,7 @@ public class ChatPanel this.repaint(); } + writeMessagePanel.initPluginComponents(); writeMessagePanel.setTransportSelectorBoxVisible(true); //Enables to change the protocol provider by simply pressing the diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java index 8470596..14afdc8 100755 --- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java @@ -6,6 +6,7 @@ package net.java.sip.communicator.impl.gui.main.chat; import java.awt.*; +import java.awt.Container; import java.awt.event.*; import java.io.*; import java.util.*; @@ -18,10 +19,13 @@ import javax.swing.text.html.*; import javax.swing.undo.*; import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.event.*; import net.java.sip.communicator.impl.gui.main.chat.conference.*; import net.java.sip.communicator.impl.gui.main.chat.menus.*; import net.java.sip.communicator.impl.gui.utils.*; 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.event.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; @@ -29,6 +33,7 @@ import net.java.sip.communicator.util.*; import net.java.sip.communicator.util.skin.*; import org.jitsi.service.configuration.*; +import org.osgi.framework.*; /** * The <tt>ChatWritePanel</tt> is the panel, where user writes her messages. @@ -46,6 +51,7 @@ public class ChatWritePanel MouseListener, UndoableEditListener, DocumentListener, + PluginComponentListener, Skinnable { /** @@ -1518,4 +1524,102 @@ public class ChatWritePanel isOutdatedResource = true; } } + + /** + * Initializes plug-in components for this container. + */ + void initPluginComponents() + { + // Search for plugin components registered through the OSGI bundle + // context. + ServiceReference[] serRefs = null; + + String osgiFilter = "(" + + net.java.sip.communicator.service.gui.Container.CONTAINER_ID + + "="+net.java.sip.communicator.service.gui.Container. + CONTAINER_CHAT_WRITE_PANEL.getID()+")"; + + try + { + serRefs = GuiActivator.bundleContext.getServiceReferences( + PluginComponentFactory.class.getName(), + osgiFilter); + } + catch (InvalidSyntaxException exc) + { + logger.error("Could not obtain plugin reference.", exc); + } + if (serRefs != null) + { + for (int i = 0; i < serRefs.length; i ++) + { + PluginComponentFactory factory = + (PluginComponentFactory) GuiActivator + .bundleContext.getService(serRefs[i]); + + PluginComponent component = + factory.getPluginComponentInstance(this); + ChatSession chatSession = chatPanel.getChatSession(); + if (chatSession instanceof MetaContactChatSession) + { + MetaContact metaContact = + (MetaContact) chatSession.getDescriptor(); + component.setCurrentContact(metaContact); + } + if (component.getComponent() == null) + continue; + + centerPanel.add((Component)component.getComponent()); + } + } + GuiActivator.getUIService().addPluginComponentListener(this); + this.centerPanel.repaint(); + } + + /** + * Indicates that a new plugin component has been added. Adds it to this + * container if it belongs to it. + * + * @param event the <tt>PluginComponentEvent</tt> that notified us + */ + public void pluginComponentAdded(PluginComponentEvent event) + { + PluginComponentFactory factory = event.getPluginComponentFactory(); + if (!factory.getContainer().equals( + net.java.sip.communicator.service. + gui.Container.CONTAINER_CHAT_WRITE_PANEL)) + return; + PluginComponent c = factory.getPluginComponentInstance(this); + ChatSession chatSession = chatPanel.getChatSession(); + if (chatSession instanceof MetaContactChatSession) + { + MetaContact metaContact = (MetaContact) chatSession.getDescriptor(); + c.setCurrentContact(metaContact); + } + centerPanel.add((Component) c.getComponent()); + + this.centerPanel.repaint(); + } + + /** + * Removes the according plug-in component from this container. + * + * @param event the <tt>PluginComponentEvent</tt> that notified us + */ + public void pluginComponentRemoved(PluginComponentEvent event) + { + PluginComponentFactory factory = event.getPluginComponentFactory(); + + if (!factory.getContainer().equals( + net.java.sip.communicator.service. + gui.Container.CONTAINER_CHAT_WRITE_PANEL)) + return; + + Component c = + (Component)factory.getPluginComponentInstance(this) + .getComponent(); + + this.centerPanel.remove(c); + this.centerPanel.repaint(); + } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrActivator.java b/src/net/java/sip/communicator/plugin/otr/OtrActivator.java index ae56e39..5e35942 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrActivator.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrActivator.java @@ -8,13 +8,14 @@ package net.java.sip.communicator.plugin.otr; 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.protocol.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; - import net.java.sip.communicator.util.Logger; + import org.jitsi.service.configuration.*; import org.jitsi.service.resources.*; import org.jitsi.util.*; @@ -370,6 +371,20 @@ public class OtrActivator 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. @@ -384,7 +399,8 @@ public class OtrActivator // Register the configuration form. bundleContext.registerService(ConfigurationForm.class.getName(), new LazyConfigurationForm( - "net.java.sip.communicator.plugin.otr.OtrConfigurationPanel", + "net.java.sip.communicator.plugin.otr.authdialog." + + "OtrConfigurationPanel", getClass().getClassLoader(), "plugin.otr.configform.ICON", "service.gui.CHAT", 1), diff --git a/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java b/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java index dbd9c87..a4c47e4 100755 --- a/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrConfigurator.java @@ -6,6 +6,8 @@ */
package net.java.sip.communicator.plugin.otr;
+import java.util.*;
+
import org.bouncycastle.util.encoders.*;
import org.jitsi.service.configuration.*;
@@ -142,4 +144,31 @@ public class OtrConfigurator {
return OtrActivator.configService.getInt(getID(id), defaultValue);
}
+
+ /**
+ * Appends <tt>value</tt> 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<String> getAppendedProperties(String id)
+ {
+ String listProperties =
+ (String) OtrActivator.configService.getProperty(getID(id));
+
+ if (listProperties == null) return new ArrayList<String>();
+
+ return Arrays.asList(listProperties.split(","));
+ }
}
diff --git a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java index 040f932..613b0f8 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrContactMenu.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.plugin.otr; import java.awt.event.*; +import java.security.*; import javax.swing.*; @@ -470,8 +471,13 @@ class OtrContactMenu switch (sessionStatus) { case ENCRYPTED: + PublicKey pubKey = + OtrActivator.scOtrEngine.getRemotePublicKey(contact); + String fingerprint = + OtrActivator.scOtrKeyManager. + getFingerprintFromPublicKey(pubKey); imageID - = OtrActivator.scOtrKeyManager.isVerified(contact) + = OtrActivator.scOtrKeyManager.isVerified(contact, fingerprint) ? "plugin.otr.ENCRYPTED_ICON_16x16" : "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16"; break; @@ -490,4 +496,14 @@ class OtrContactMenu separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID)); } + + @Override + public void multipleInstancesDetected(Contact contact) {} + + @Override + public void outgoingSessionChanged(Contact contact) + { + if (contact.equals(OtrContactMenu.this.contact)) + setSessionStatus(OtrActivator.scOtrEngine.getSessionStatus(contact)); + } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java index 2984fb7..40810ce 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrMetaContactButton.java @@ -9,6 +9,7 @@ package net.java.sip.communicator.plugin.otr; import java.awt.*; import java.awt.event.*; import java.io.*; +import java.security.*; import javax.imageio.*; @@ -272,12 +273,17 @@ public class OtrMetaContactButton switch (status) { case ENCRYPTED: + PublicKey pubKey = + OtrActivator.scOtrEngine.getRemotePublicKey(contact); + String fingerprint = + OtrActivator.scOtrKeyManager. + getFingerprintFromPublicKey(pubKey); image - = OtrActivator.scOtrKeyManager.isVerified(contact) + = OtrActivator.scOtrKeyManager.isVerified(contact, fingerprint) ? verifiedLockedPadlockImage : unverifiedLockedPadlockImage; tipKey = - OtrActivator.scOtrKeyManager.isVerified(contact) + OtrActivator.scOtrKeyManager.isVerified(contact, fingerprint) ? "plugin.otr.menu.VERIFIED" : "plugin.otr.menu.UNVERIFIED"; break; @@ -308,4 +314,19 @@ public class OtrMetaContactButton .getI18NString(tipKey)); button.repaint(); } + + @Override + public void multipleInstancesDetected(Contact contact) + {} + + @Override + public void outgoingSessionChanged(Contact contact) + { + // OtrMetaContactButton.this.contact can be null. + if (contact.equals(OtrMetaContactButton.this.contact)) + { + setStatus( + OtrActivator.scOtrEngine.getSessionStatus(contact)); + } + } } diff --git a/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java index 423cf23..b94a9bf 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrWeakListener.java @@ -152,4 +152,28 @@ public class OtrWeakListener if (l != null) l.sessionStatusChanged(contact); } + + /** + * Forwards the event/notification to the associated + * <tt>T</tt> if it is still needed by the application. + */ + public void multipleInstancesDetected(Contact contact) + { + ScOtrEngineListener l = getListener(); + + if (l != null) + l.multipleInstancesDetected(contact); + } + + /** + * Forwards the event/notification to the associated + * <tt>T</tt> if it is still needed by the application. + */ + public void outgoingSessionChanged(Contact 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 853c5fc..cade910 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java @@ -6,7 +6,11 @@ */ 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.service.protocol.*; /** @@ -100,6 +104,42 @@ public interface ScOtrEngine public abstract void refreshSession(Contact 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. + * <a href="https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html"> + * https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html</a> + * <p> + * Returns a list containing all instances of a session. The 'master' + * session is always first in the list. + * + * @param contact the {@link Contact} for whom we want to get the instances + * + * @return A list of all instances of the session for the specified contact. + */ + public abstract List<Session> getSessionInstances(Contact 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. + * <a href="https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html"> + * https://otr.cypherpunks.ca/Protocol-v3-4.0.0.html</a> + * <p> + * 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 <tt>InstanceTag</tt>. + * + * @param contact the {@link Contact} 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(Contact contact, InstanceTag tag); + + /** * Gets the {@link ScSessionStatus} for the given {@link Contact}. * * @param contact the {@link Contact} whose {@link ScSessionStatus} we are @@ -135,6 +175,8 @@ public interface ScOtrEngine */ public abstract void removeListener(ScOtrEngineListener listener); + public abstract PublicKey getRemotePublicKey(Contact contact); + // 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 b3765fa..137122c 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java @@ -12,6 +12,7 @@ import java.util.*; 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.authdialog.*;
import net.java.sip.communicator.service.browserlauncher.*;
@@ -265,19 +266,20 @@ public class ScOtrEngineImpl progressDialog = new SmpProgressDialog(contact);
progressDialogMap.put(contact, progressDialog);
}
-
+
progressDialog.init();
progressDialog.setVisible(true);
}
@Override
- public void verify(SessionID sessionID, boolean approved)
+ public void verify(
+ SessionID sessionID, String fingerprint, boolean approved)
{
Contact contact = getContact(sessionID);
if (contact == null)
return;
- OtrActivator.scOtrKeyManager.verify(contact);
+ OtrActivator.scOtrKeyManager.verify(contact, fingerprint);
SmpProgressDialog progressDialog = progressDialogMap.get(contact);
if (progressDialog == null)
@@ -285,20 +287,20 @@ public class ScOtrEngineImpl progressDialog = new SmpProgressDialog(contact);
progressDialogMap.put(contact, progressDialog);
}
-
+
progressDialog.setProgressSuccess();
progressDialog.setVisible(true);
}
@Override
- public void unverify(SessionID sessionID)
+ public void unverify(SessionID sessionID, String fingerprint)
{
Contact contact = getContact(sessionID);
if (contact == null)
return;
- OtrActivator.scOtrKeyManager.unverify(contact);
-
+ OtrActivator.scOtrKeyManager.unverify(contact, fingerprint);
+
SmpProgressDialog progressDialog = progressDialogMap.get(contact);
if (progressDialog == null)
{
@@ -332,6 +334,42 @@ public class ScOtrEngineImpl "plugin.otr.activator.fallbackmessage",
new String[] {accountID.getDisplayName()});
}
+
+ @Override
+ public void multipleInstancesDetected(SessionID sessionID)
+ {
+ Contact contact = getContact(sessionID);
+ if (contact == null)
+ return;
+
+ String message =
+ OtrActivator.resourceService.getI18NString(
+ "plugin.otr.activator.multipleinstancesdetected",
+ new String[] {contact.getDisplayName()});
+ OtrActivator.uiService.getChat(contact).addMessage(
+ contact.getDisplayName(),
+ new Date(), Chat.SYSTEM_MESSAGE,
+ message,
+ OperationSetBasicInstantMessaging.HTML_MIME_TYPE);
+ }
+
+ @Override
+ public void messageFromAnotherInstanceReceived(SessionID sessionID)
+ {
+ Contact contact = getContact(sessionID);
+ if (contact == null)
+ return;
+
+ String message =
+ OtrActivator.resourceService.getI18NString(
+ "plugin.otr.activator.msgfromanotherinstance",
+ new String[] {contact.getDisplayName()});
+ OtrActivator.uiService.getChat(contact).addMessage(
+ contact.getDisplayName(),
+ new Date(), Chat.SYSTEM_MESSAGE,
+ message,
+ OperationSetBasicInstantMessaging.HTML_MIME_TYPE);
+ }
}
/**
@@ -453,15 +491,38 @@ public class ScOtrEngineImpl PublicKey remotePubKey =
otrEngine.getRemotePublicKey(sessionID);
- PublicKey storedPubKey =
- OtrActivator.scOtrKeyManager.loadPublicKey(contact);
+ 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);
+ }
- if (!remotePubKey.equals(storedPubKey))
- OtrActivator.scOtrKeyManager.savePublicKey(contact,
- remotePubKey);
+ List<String> allFingerprintsOfContact =
+ OtrActivator.scOtrKeyManager.
+ getAllRemoteFingerprints(contact);
+ if (allFingerprintsOfContact != null)
+ {
+ if (!allFingerprintsOfContact.contains(
+ remoteFingerprint))
+ {
+ OtrActivator.scOtrKeyManager.saveFingerprint(
+ contact, remoteFingerprint);
+ }
+ }
- if (!OtrActivator.scOtrKeyManager.isVerified(contact))
+ if (!OtrActivator.scOtrKeyManager.isVerified(
+ contact, remoteFingerprint))
{
+ OtrActivator.scOtrKeyManager.unverify(
+ contact, remoteFingerprint);
UUID sessionGuid = null;
for(ScSessionID scSessionID : contactsMap.keySet())
{
@@ -531,7 +592,8 @@ public class ScOtrEngineImpl message
= OtrActivator.resourceService.getI18NString(
- OtrActivator.scOtrKeyManager.isVerified(contact)
+ OtrActivator.scOtrKeyManager.isVerified(
+ contact, remoteFingerprint)
? "plugin.otr.activator.sessionstared"
: "plugin.otr.activator"
+ ".unverifiedsessionstared",
@@ -565,6 +627,26 @@ public class ScOtrEngineImpl for (ScOtrEngineListener l : getListeners())
l.sessionStatusChanged(contact);
}
+
+ public void multipleInstancesDetected(SessionID sessionID)
+ {
+ Contact contact = getContact(sessionID);
+ if (contact == null)
+ return;
+
+ for (ScOtrEngineListener l : getListeners())
+ l.multipleInstancesDetected(contact);
+ }
+
+ public void outgoingSessionChanged(SessionID sessionID)
+ {
+ Contact contact = getContact(sessionID);
+ if (contact == null)
+ return;
+
+ for (ScOtrEngineListener l : getListeners())
+ l.outgoingSessionChanged(contact);
+ }
});
}
@@ -1036,6 +1118,34 @@ public class ScOtrEngineImpl + contact.getDisplayName(), e);
showError(session.getSessionID(), e.getMessage());
}
-
+ }
+
+ public PublicKey getRemotePublicKey(Contact contact)
+ {
+ if (contact == null)
+ return null;
+
+ Session session = getSession(contact);
+
+ return session.getRemotePublicKey();
+ }
+
+ public List<Session> getSessionInstances(Contact contact)
+ {
+ if (contact == null)
+ return null;
+
+ return getSession(contact).getInstances();
+ }
+
+ public boolean setOutgoingSession(Contact contact, InstanceTag tag)
+ {
+ if (contact == null)
+ return false;
+
+ Session session = getSession(contact);
+
+ scSessionStatusMap.remove(session.getSessionID());
+ return session.setOutgoingInstance(tag);
}
}
diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java index 790fc16..8ab95b8 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineListener.java @@ -19,4 +19,8 @@ public interface ScOtrEngineListener public void globalPolicyChanged(); public void sessionStatusChanged(Contact contact); + + public void multipleInstancesDetected(Contact contact); + + public void outgoingSessionChanged(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 bc83cd7..47ca22d 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.plugin.otr;
import java.security.*;
+import java.util.*;
import net.java.sip.communicator.service.protocol.*;
@@ -22,19 +23,21 @@ public interface ScOtrKeyManager public abstract void removeListener(ScOtrKeyManagerListener l);
- public abstract void verify(Contact contact);
+ public abstract void verify(Contact contact, String fingerprint);
- public abstract void unverify(Contact contact);
+ public abstract void unverify(Contact contact, String fingerprint);
- public abstract boolean isVerified(Contact contact);
+ public abstract boolean isVerified(Contact contact, String fingerprint);
- public abstract String getRemoteFingerprint(Contact contact);
+ public abstract String getFingerprintFromPublicKey(PublicKey pubKey);
+
+ public abstract List<String> getAllRemoteFingerprints(Contact contact);
public abstract String getLocalFingerprint(AccountID account);
public abstract byte[] getLocalFingerprintRaw(AccountID account);
- public abstract void savePublicKey(Contact contact, PublicKey pubKey);
+ public abstract void saveFingerprint(Contact contact, String fingerprint);
public abstract PublicKey loadPublicKey(Contact contact);
diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java index 37adc63..8933530 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java @@ -62,47 +62,113 @@ public class ScOtrKeyManagerImpl }
}
- public void verify(Contact contact)
+ public void verify(Contact contact, String fingerprint)
{
- if ((contact == null) || isVerified(contact))
+ if ((fingerprint == null) || contact == null)
return;
- this.configurator.setProperty(contact.getAddress()
- + ".publicKey.verified", true);
+ this.configurator.setProperty(contact.getAddress() + fingerprint
+ + ".fingerprint.verified", true);
for (ScOtrKeyManagerListener l : getListeners())
l.contactVerificationStatusChanged(contact);
}
- public void unverify(Contact contact)
+ public void unverify(Contact contact, String fingerprint)
{
- if ((contact == null) || !isVerified(contact))
+ if ((fingerprint == null) || contact == null)
return;
- this.configurator.removeProperty(contact.getAddress()
- + ".publicKey.verified");
+ this.configurator.setProperty(contact.getAddress() + fingerprint
+ + ".fingerprint.verified", false);
for (ScOtrKeyManagerListener l : getListeners())
l.contactVerificationStatusChanged(contact);
}
- public boolean isVerified(Contact contact)
+ public boolean isVerified(Contact contact, String fingerprint)
{
- if (contact == null)
+ if (fingerprint == null || contact == null)
return false;
- return this.configurator.getPropertyBoolean(contact.getAddress()
- + ".publicKey.verified", false);
+ return this.configurator.getPropertyBoolean(
+ contact.getAddress() + fingerprint
+ + ".fingerprint.verified", false);
}
- public String getRemoteFingerprint(Contact contact)
+ public List<String> getAllRemoteFingerprints(Contact contact)
{
- PublicKey remotePublicKey = loadPublicKey(contact);
- if (remotePublicKey == null)
+ if (contact == 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(contact, fingerprint);
+ else
+ unverify(contact, 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(remotePublicKey);
+ return new OtrCryptoEngineImpl().getFingerprint(pubKey);
}
catch (OtrCryptoException e)
{
@@ -151,19 +217,16 @@ public class ScOtrKeyManagerImpl }
}
- public void savePublicKey(Contact contact, PublicKey pubKey)
+ public void saveFingerprint(Contact contact, String fingerprint)
{
if (contact == null)
return;
- X509EncodedKeySpec x509EncodedKeySpec =
- new X509EncodedKeySpec(pubKey.getEncoded());
-
- this.configurator.setProperty(contact.getAddress() + ".publicKey",
- x509EncodedKeySpec.getEncoded());
+ this.configurator.appendProperty(contact.getAddress() + ".fingerprints",
+ fingerprint);
- this.configurator.removeProperty(contact.getAddress()
- + ".publicKey.verified");
+ this.configurator.setProperty(contact.getAddress() + fingerprint
+ + ".fingerprint.verified", false);
}
public PublicKey loadPublicKey(Contact contact)
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 26f8866..31d6848 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/FingerprintAuthenticationPanel.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.plugin.otr.authdialog; import java.awt.*; +import java.security.*; import javax.swing.*; import javax.swing.event.*; @@ -107,8 +108,13 @@ public class FingerprintAuthenticationPanel cbAction = new JComboBox(); cbAction.addItem(actionIHave); cbAction.addItem(actionIHaveNot); + + PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(contact); + String remoteFingerprint = + OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); cbAction.setSelectedItem(OtrActivator.scOtrKeyManager - .isVerified(contact) ? actionIHave : actionIHaveNot); + .isVerified(contact, remoteFingerprint) + ? actionIHave : actionIHaveNot); pnlAction.add(cbAction, c); @@ -152,8 +158,9 @@ public class FingerprintAuthenticationPanel // Remote fingerprint. String user = contact.getDisplayName(); + PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(contact); String remoteFingerprint = - OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact); + OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); txtRemoteFingerprint.setText(OtrActivator.resourceService .getI18NString("plugin.otr.authbuddydialog.REMOTE_FINGERPRINT", new String[] @@ -182,6 +189,10 @@ public class FingerprintAuthenticationPanel public void compareFingerprints() { + PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(contact); + String remoteFingerprint = + OtrActivator.scOtrKeyManager.getFingerprintFromPublicKey(pubKey); + if(txtRemoteFingerprintComparison.getText() == null || txtRemoteFingerprintComparison.getText().length() == 0) { @@ -189,8 +200,7 @@ public class FingerprintAuthenticationPanel return; } if(txtRemoteFingerprintComparison.getText().toLowerCase().contains( - OtrActivator.scOtrKeyManager - .getRemoteFingerprint(contact).toLowerCase())) + remoteFingerprint.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 256814a..099bb67 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.*; 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 class KnownFingerprintsPanel this.setPreferredSize(new Dimension(400, 200)); - openContact(getSelectedContact()); + openContact(getSelectedContact(), getSelectedFingerprint()); } /** @@ -74,7 +74,7 @@ public class KnownFingerprintsPanel if (e.getValueIsAdjusting()) return; - openContact(getSelectedContact()); + openContact(getSelectedContact(), getSelectedFingerprint()); } }); @@ -88,14 +88,15 @@ public class KnownFingerprintsPanel 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(getSelectedContact()); - openContact(getSelectedContact()); + .verify(getSelectedContact(), getSelectedFingerprint()); + openContact(getSelectedContact(), getSelectedFingerprint()); contactsTable.updateUI(); } }); @@ -105,13 +106,15 @@ public class KnownFingerprintsPanel 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(getSelectedContact()); - openContact(getSelectedContact()); + .unverify(getSelectedContact(), getSelectedFingerprint()); + openContact(getSelectedContact(), getSelectedFingerprint()); contactsTable.updateUI(); } }); @@ -129,10 +132,27 @@ public class KnownFingerprintsPanel KnownFingerprintsTableModel model = (KnownFingerprintsTableModel) contactsTable.getModel(); int index = contactsTable.getSelectedRow(); - if (index < 0 || index > model.allContacts.size()) + 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()) return null; - return model.allContacts.get(index); + return model.getFingerprintFromRow(index); } /** @@ -140,11 +160,11 @@ public class KnownFingerprintsPanel * 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) + private void openContact(Contact contact, String fingerprint) { - if (contact == null - || OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact) == null) + if (contact == null || fingerprint == null) { btnForgetFingerprint.setEnabled(false); btnVerifyFingerprint.setEnabled(false); @@ -153,7 +173,7 @@ public class KnownFingerprintsPanel { boolean verified = OtrActivator.scOtrKeyManager - .isVerified(contact); + .isVerified(contact, fingerprint); 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 1b32f26..41dbbfb 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/KnownFingerprintsTableModel.java @@ -8,6 +8,7 @@ package net.java.sip.communicator.plugin.otr.authdialog; import java.awt.*; import java.util.*; +import java.util.List; import javax.swing.table.*; @@ -25,6 +26,7 @@ import org.osgi.framework.*; */ public class KnownFingerprintsTableModel extends AbstractTableModel + implements ScOtrKeyManagerListener { /** * Serial version UID. @@ -37,7 +39,8 @@ public class KnownFingerprintsTableModel public static final int FINGERPRINT_INDEX = 2; - public final java.util.List<Contact> allContacts = new Vector<Contact>(); + public final LinkedHashMap<Contact, List<String>> allContactsFingerprints = + new LinkedHashMap<Contact, List<String>>(); public KnownFingerprintsTableModel() { @@ -76,10 +79,15 @@ public class KnownFingerprintsTableModel Iterator<Contact> contacts = metaContact.getContacts(); while (contacts.hasNext()) { - allContacts.add(contacts.next()); + Contact contact = contacts.next(); + allContactsFingerprints.put( + contact, + OtrActivator.scOtrKeyManager.getAllRemoteFingerprints( + contact)); } } } + OtrActivator.scOtrKeyManager.addListener(this); } /** @@ -112,10 +120,8 @@ public class KnownFingerprintsTableModel */ public Object getValueAt(int row, int column) { - if (row < 0) - return null; - - Contact contact = allContacts.get(row); + Contact contact = getContactFromRow(row); + String fingerprint = getFingerprintFromRow(row); switch (column) { case CONTACTNAME_INDEX: @@ -123,25 +129,84 @@ public class KnownFingerprintsTableModel case VERIFIED_INDEX: // TODO: Maybe use a CheckBoxColumn? return (OtrActivator.scOtrKeyManager - .isVerified(contact)) + .isVerified(contact, fingerprint)) ? OtrActivator.resourceService.getI18NString( "plugin.otr.configform.COLUMN_VALUE_VERIFIED_TRUE") : OtrActivator.resourceService.getI18NString( "plugin.otr.configform.COLUMN_VALUE_VERIFIED_FALSE"); case FINGERPRINT_INDEX: - return OtrActivator.scOtrKeyManager - .getRemoteFingerprint(contact); + return fingerprint; default: return null; } } + Contact getContactFromRow(int row) + { + if (row < 0 || row >= getRowCount()) + return null; + + int index = -1; + Contact contact = null; + for (Map.Entry<Contact, List<String>> entry : + allContactsFingerprints.entrySet()) + { + boolean found = false; + contact = entry.getKey(); + List<String> 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<Contact, List<String>> entry : + allContactsFingerprints.entrySet()) + { + boolean found = false; + List<String> 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() { - return allContacts.size(); + int rowCount = 0; + for (Map.Entry<Contact, List<String>> entry : + allContactsFingerprints.entrySet()) + rowCount += entry.getValue().size(); + return rowCount; } /** @@ -151,4 +216,13 @@ public class KnownFingerprintsTableModel { return 3; } + + @Override + public void contactVerificationStatusChanged(Contact contact) + { + allContactsFingerprints.put( + contact, + OtrActivator.scOtrKeyManager.getAllRemoteFingerprints(contact)); + this.fireTableDataChanged(); + } } 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 b67ce81..f82c01d 100644 --- a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrBuddyAuthenticationDialog.java @@ -8,12 +8,13 @@ package net.java.sip.communicator.plugin.otr.authdialog; 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.authdialog.FingerprintAuthenticationPanel.*; +import net.java.sip.communicator.plugin.otr.authdialog.FingerprintAuthenticationPanel.ActionComboBoxItem; import net.java.sip.communicator.service.protocol.*; /** @@ -178,13 +179,20 @@ public class OtrBuddyAuthenticationDialog 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); + OtrActivator.scOtrKeyManager.verify( + contact, fingerprint); break; case I_HAVE_NOT: - OtrActivator.scOtrKeyManager.unverify(contact); + OtrActivator.scOtrKeyManager.unverify( + contact, fingerprint); break; } dispose(); diff --git a/src/net/java/sip/communicator/plugin/otr/OtrConfigurationPanel.java b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrConfigurationPanel.java index 79ce6bc..9435f23 100644 --- a/src/net/java/sip/communicator/plugin/otr/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; +package net.java.sip.communicator.plugin.otr.authdialog; import java.awt.*; import java.awt.event.*; @@ -16,6 +16,7 @@ import javax.swing.border.*; import net.java.otr4j.*; 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.plugin.otr.authdialog.*; /** diff --git a/src/net/java/sip/communicator/plugin/otr/authdialog/OtrV3OutgoingSessionSwitcher.java b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrV3OutgoingSessionSwitcher.java new file mode 100644 index 0000000..4ced8c1 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/otr/authdialog/OtrV3OutgoingSessionSwitcher.java @@ -0,0 +1,319 @@ +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.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.*; + +public class OtrV3OutgoingSessionSwitcher + extends SIPCommMenuBar + implements PluginComponent, + ActionListener, + ScOtrEngineListener, + ScOtrKeyManagerListener +{ + + private static final Logger logger + = Logger.getLogger(OtrV3OutgoingSessionSwitcher.class); + + private static final long serialVersionUID = 0L; + + private final SelectorMenu menu = new SelectorMenu(); + + private ButtonGroup buttonGroup = new ButtonGroup(); + + Contact contact; + + private final Map<Session, JMenuItem> outgoingSessions + = new HashMap<Session, JMenuItem>(); + 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(); + 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); + } + + public void fadeAnimation() + { + alphaChanger.stop(); + alpha = 0.85f; + repaint(); + alphaChanger.start(); + } + }; + + private final PluginComponentFactory parentFactory; + + 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); + + buildMenu(contact); + + /* + * 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<OtrV3OutgoingSessionSwitcher>( + this, + OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); + + try + { + finishedPadlockImage = new ImageIcon(ImageIO.read( + OtrActivator.resourceService.getImageURL( + "plugin.otr.FINISHED_ICON_16x16"))); + verifiedLockedPadlockImage = new ImageIcon(ImageIO.read( + OtrActivator.resourceService.getImageURL( + "plugin.otr.ENCRYPTED_ICON_16x16"))); + unverifiedLockedPadlockImage = new ImageIcon(ImageIO.read( + OtrActivator.resourceService.getImageURL( + "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16"))); + unlockedPadlockImage = new ImageIcon(ImageIO.read( + OtrActivator.resourceService.getImageURL( + "plugin.otr.PLAINTEXT_ICON_16x16"))); + } catch (IOException e) + { + logger.debug("Failed to load padlock image"); + } + } + + @Override + public int getPositionIndex() + { + return -1; + } + + @Override + public void setCurrentContact(Contact contact) + { + if (this.contact == contact) + return; + + this.contact = contact; + System.out.println("ot tuk1"); + buildMenu(contact); + } + + @Override + public void setCurrentContact(MetaContact metaContact) + { + setCurrentContact((metaContact == null) ? null : metaContact + .getDefaultContact()); + } + + @Override + public void setCurrentContactGroup(MetaContactGroup metaGroup) {} + + @Override + public void setCurrentAccountID(AccountID accountID) {} + + @Override + public PluginComponentFactory getParentFactory() + { + return parentFactory; + } + + @Override + public void contactVerificationStatusChanged(Contact contact) + { + if (contact == null || this.contact != contact) return; + } + + @Override + public void contactPolicyChanged(Contact contact) {} + + @Override + public void globalPolicyChanged() {} + + @Override + public void sessionStatusChanged(Contact contact) + { + System.out.println("ot tuk2"); + buildMenu(contact); + } + + @Override + public void multipleInstancesDetected(Contact contact) + { + System.out.println("ot tuk3"); + buildMenu(contact); + } + + private ImageIcon verifiedLockedPadlockImage; + private ImageIcon unverifiedLockedPadlockImage; + private ImageIcon finishedPadlockImage; + private ImageIcon unlockedPadlockImage; + void buildMenu(Contact contact) + { + if (contact == null || this.contact != contact) { + return; + } + menu.removeAll(); + + java.util.List<Session> multipleInstances = + OtrActivator.scOtrEngine.getSessionInstances(contact); + + int index = 0; + for (Session session : multipleInstances) + { + index++; + if (!outgoingSessions.containsKey(session)) + outgoingSessions.put(session, new JRadioButtonMenuItem()); + + 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(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); + this.menu.setSelected(selectedObject); + + buttonGroup.add(menuItem); + menuItem.addActionListener(this); + setSelected(menu.getItem(0)); + } + System.out.println("da"); + updateEnableStatus(); + } + + @Override + public void actionPerformed(ActionEvent e) + { + for (Map.Entry<Session, JMenuItem> entry : outgoingSessions.entrySet()) + { + JMenuItem menuItem = (JRadioButtonMenuItem) e.getSource(); + if (menuItem.equals(entry.getValue())) + { + OtrActivator.scOtrEngine.setOutgoingSession( + contact, entry.getKey().getReceiverInstanceTag()); + break; + } + } + } + + @Override + public void outgoingSessionChanged(Contact contact) + { + buildMenu(contact); + } + + /** + * Sets the menu to enabled or disabled. The menu is enabled, as soon as it + * contains two or more items. If it is empty, it is disabled. + */ + private void updateEnableStatus() + { + this.menu.setVisible(this.menu.getItemCount() > 1); + this.menu.fadeAnimation(); + } +} diff --git a/src/net/java/sip/communicator/service/gui/Container.java b/src/net/java/sip/communicator/service/gui/Container.java index a493d86..36810a4 100644 --- a/src/net/java/sip/communicator/service/gui/Container.java +++ b/src/net/java/sip/communicator/service/gui/Container.java @@ -84,6 +84,11 @@ public class Container = new Container("CONTAINER_GROUP_RIGHT_BUTTON_MENU"); /** + * Chat write panel container. + */ + public static final Container CONTAINER_CHAT_WRITE_PANEL + = new Container("CONTAINER_CHAT_WRITE_PANEL"); + /** * Chat window "menu bar" container. */ public static final Container CONTAINER_CHAT_MENU_BAR |