Added improvements to OTR user interface, among which new icons and

tooltips. Patch provided by Marin Dzhigarov on Nov 25, 2013.
cusax-fix 4926
yanas 12 years ago
parent 25f3208044
commit 9383a5e000

@ -543,13 +543,15 @@ plugin.securityconfig.ICON=resources/images/plugin/securityconfig/security.png
# otr plugin icons # otr plugin icons
plugin.otr.ENCRYPTED_ICON_16x16=resources/images/plugin/otr/encrypted16x16.png plugin.otr.ENCRYPTED_ICON_16x16=resources/images/plugin/otr/encrypted16x16.png
plugin.otr.ENCRYPTED_ICON_22x22=resources/images/plugin/otr/encrypted22x22.png plugin.otr.ENCRYPTED_ICON_22x22=resources/images/plugin/otr/encrypted_verified22x22.png
plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16=resources/images/plugin/otr/encrypted_unverified16x16.png plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16=resources/images/plugin/otr/encrypted_unverified16x16.png
plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22=resources/images/plugin/otr/encrypted_unverified22x22.png plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22=resources/images/plugin/otr/encrypted_unverified22x22.png
plugin.otr.PLAINTEXT_ICON_16x16=resources/images/plugin/otr/plaintext16x16.png plugin.otr.PLAINTEXT_ICON_16x16=resources/images/plugin/otr/plaintext16x16.png
plugin.otr.PLAINTEXT_ICON_22x22=resources/images/plugin/otr/plaintext22x22.png plugin.otr.PLAINTEXT_ICON_22x22=resources/images/plugin/otr/plaintext22x22.png
plugin.otr.FINISHED_ICON_16x16=resources/images/plugin/otr/finished16x16.png plugin.otr.FINISHED_ICON_16x16=resources/images/plugin/otr/finished16x16.png
plugin.otr.FINISHED_ICON_22x22=resources/images/plugin/otr/finished22x22.png plugin.otr.FINISHED_ICON_22x22=resources/images/plugin/otr/finished22x22.png
plugin.otr.LOADING_ICON_22x22=resources/images/plugin/otr/padlockLoading.gif
plugin.otr.BROKEN_ICON_22x22=resources/images/plugin/otr/padlockBrokenOpen.png
plugin.otr.HELP_ICON_15x15=resources/images/plugin/otr/help15x15.png plugin.otr.HELP_ICON_15x15=resources/images/plugin/otr/help15x15.png
plugin.otr.MENU_ITEM_ICON_16x16=resources/images/plugin/otr/otr_menu_icon.png plugin.otr.MENU_ITEM_ICON_16x16=resources/images/plugin/otr/otr_menu_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1531,6 +1531,11 @@ plugin.securityconfig.masterpassword.MP_INPUT=Please enter your master password:
plugin.otr.menu.TITLE=Secure chat plugin.otr.menu.TITLE=Secure chat
plugin.otr.menu.START_OTR=Start private conversation plugin.otr.menu.START_OTR=Start private conversation
plugin.otr.menu.END_OTR=End private conversation plugin.otr.menu.END_OTR=End private conversation
plugin.otr.menu.FINISHED=Your buddy has ended your private conversation. You should do the same
plugin.otr.menu.VERIFIED=Your private conversation is verified
plugin.otr.menu.UNVERIFIED=Your private conversation is not verified. Please authenticate your buddy
plugin.otr.menu.LOADING_OTR=Starting private conversation...
plugin.otr.menu.TIMED_OUT=Starting private conversation timed out.
plugin.otr.menu.REFRESH_OTR=Refresh private conversation plugin.otr.menu.REFRESH_OTR=Refresh private conversation
plugin.otr.menu.AUTHENTICATE_BUDDY=Authenticate buddy plugin.otr.menu.AUTHENTICATE_BUDDY=Authenticate buddy
plugin.otr.menu.WHATS_THIS=What's this plugin.otr.menu.WHATS_THIS=What's this

@ -7,12 +7,10 @@
package net.java.sip.communicator.plugin.otr; package net.java.sip.communicator.plugin.otr;
import java.awt.event.*; import java.awt.event.*;
import java.lang.ref.*;
import javax.swing.*; import javax.swing.*;
import net.java.otr4j.*; import net.java.otr4j.*;
import net.java.otr4j.session.*;
import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.*;
@ -62,7 +60,7 @@ class OtrContactMenu
*/ */
private OtrPolicy otrPolicy; private OtrPolicy otrPolicy;
private SessionStatus sessionStatus; private ScSessionStatus sessionStatus;
private final JMenu parentMenu; private final JMenu parentMenu;
@ -94,14 +92,14 @@ public OtrContactMenu( Contact contact,
* XXX This OtrContactMenu instance cannot be added as a listener to * XXX This OtrContactMenu instance cannot be added as a listener to
* scOtrEngine and scOtrKeyManager without being removed later on * scOtrEngine and scOtrKeyManager without being removed later on
* because the latter live forever. Unfortunately, the dispose() method * because the latter live forever. Unfortunately, the dispose() method
* of this instance is never executed. WeakListener will keep this * of this instance is never executed. OtrWeakListener will keep this
* instance as a listener of scOtrEngine and scOtrKeyManager for as long * instance as a listener of scOtrEngine and scOtrKeyManager for as long
* as this instance is necessary. And this instance will be strongly * as this instance is necessary. And this instance will be strongly
* referenced by the JMenuItems which depict it. So when the JMenuItems * referenced by the JMenuItems which depict it. So when the JMenuItems
* are gone, this instance will become obsolete and WeakListener will * are gone, this instance will become obsolete and OtrWeakListener will
* remove it as a listener of scOtrEngine and scOtrKeyManager. * remove it as a listener of scOtrEngine and scOtrKeyManager.
*/ */
new WeakListener( new OtrWeakListener<OtrContactMenu>(
this, this,
OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager);
@ -258,6 +256,19 @@ private void buildMenu()
switch (this.sessionStatus) switch (this.sessionStatus)
{ {
case LOADING:
if (separateMenu != null)
{
separateMenu.add(endOtr);
separateMenu.add(refreshOtr);
}
else
{
parentMenu.add(endOtr);
parentMenu.add(refreshOtr);
}
break;
case ENCRYPTED: case ENCRYPTED:
JMenuItem authBuddy = new JMenuItem(); JMenuItem authBuddy = new JMenuItem();
authBuddy.setText(OtrActivator.resourceService authBuddy.setText(OtrActivator.resourceService
@ -293,6 +304,7 @@ private void buildMenu()
} }
break; break;
case TIMED_OUT:
case PLAINTEXT: case PLAINTEXT:
if (separateMenu != null) if (separateMenu != null)
separateMenu.add(startOtr); separateMenu.add(startOtr);
@ -406,9 +418,9 @@ public void sessionStatusChanged(Contact contact)
* icon and, if necessary, rebuilds the menuitems to match the passed in * icon and, if necessary, rebuilds the menuitems to match the passed in
* sessionStatus. * sessionStatus.
* *
* @param sessionStatus the {@link SessionStatus}. * @param sessionStatus the {@link ScSessionStatus}.
*/ */
private void setSessionStatus(SessionStatus sessionStatus) private void setSessionStatus(ScSessionStatus sessionStatus)
{ {
if (sessionStatus != this.sessionStatus) if (sessionStatus != this.sessionStatus)
{ {
@ -478,147 +490,4 @@ private void updateIcon()
separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID)); separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID));
} }
/**
* Implements a <tt>ScOtrEngineListener</tt> and
* <tt>ScOtrKeyManagerListener</tt> listener for the purposes of
* <tt>OtrContactMenu</tt> which listens to <tt>ScOtrEngine</tt> and
* <tt>ScOtrKeyManager</tt> while weakly referencing the
* <tt>OtrContactMenu</tt>. Fixes a memory leak of <tt>OtrContactMenu</tt>
* instances because these cannot determine when they are to be explicitly
* disposed.
*
* @author Lyubomir Marinov
*/
private static class WeakListener
implements ScOtrEngineListener,
ScOtrKeyManagerListener
{
/**
* The <tt>ScOtrEngine</tt> the <tt>OtrContactMenu</tt> associated with
* this instance is to listen to.
*/
private final ScOtrEngine engine;
/**
* The <tt>ScOtrKeyManager</tt> the <tt>OtrContactMenu</tt> associated
* with this instance is to listen to.
*/
private final ScOtrKeyManager keyManager;
/**
* The <tt>OtrContactMenu</tt> which is associated with this instance
* and which is to listen to {@link #engine} and {@link #keyManager}.
*/
private final WeakReference<OtrContactMenu> listener;
/**
* Initializes a new <tt>WeakListener</tt> instance which is to allow
* a specific <tt>OtrContactMenu</tt> to listener to a specific
* <tt>ScOtrEngine</tt> and a specific <tt>ScOtrKeyManager</tt> without
* being retained by them forever (because they live forever).
*
* @param listener the <tt>OtrContactMenu</tt> which is to listen to the
* specified <tt>engine</tt> and <tt>keyManager</tt>
* @param engine the <tt>ScOtrEngine</tt> which is to be listened to by
* the specified <tt>OtrContactMenu</tt>
* @param keyManager the <tt>ScOtrKeyManager</tt> which is to be
* listened to by the specified <tt>OtrContactMenu</tt>
*/
public WeakListener(
OtrContactMenu listener,
ScOtrEngine engine, ScOtrKeyManager keyManager)
{
if (listener == null)
throw new NullPointerException("listener");
this.listener = new WeakReference<OtrContactMenu>(listener);
this.engine = engine;
this.keyManager = keyManager;
this.engine.addListener(this);
this.keyManager.addListener(this);
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>OtrContactMenu</tt> if it is still needed by the application.
*/
public void contactPolicyChanged(Contact contact)
{
ScOtrEngineListener l = getListener();
if (l != null)
l.contactPolicyChanged(contact);
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>OtrContactMenu</tt> if it is still needed by the application.
*/
public void contactVerificationStatusChanged(Contact contact)
{
ScOtrKeyManagerListener l = getListener();
if (l != null)
l.contactVerificationStatusChanged(contact);
}
/**
* Gets the <tt>OtrContactMenu</tt> which is listening to
* {@link #engine} and {@link #keyManager}. If the
* <tt>OtrContactMenu</tt> is no longer needed by the application, this
* instance seizes listening to <tt>engine</tt> and <tt>keyManager</tt>
* and allows the memory used by this instance to be reclaimed by the
* Java virtual machine.
*
* @return the <tt>OtrContactMenu</tt> which is listening to
* <tt>engine</tt> and <tt>keyManager</tt> if it is still needed by the
* application; otherwise, <tt>null</tt>
*/
private OtrContactMenu getListener()
{
OtrContactMenu l = this.listener.get();
if (l == null)
{
engine.removeListener(this);
keyManager.removeListener(this);
}
return l;
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>OtrContactMenu</tt> if it is still needed by the application.
*/
public void globalPolicyChanged()
{
ScOtrEngineListener l = getListener();
if (l != null)
l.globalPolicyChanged();
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>OtrContactMenu</tt> if it is still needed by the application.
*/
public void sessionStatusChanged(Contact contact)
{
ScOtrEngineListener l = getListener();
if (l != null)
l.sessionStatusChanged(contact);
}
}
} }

@ -8,17 +8,15 @@
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.io.*;
import javax.imageio.*;
import net.java.otr4j.*; import net.java.otr4j.*;
import net.java.otr4j.session.*; import net.java.otr4j.session.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container; import net.java.sip.communicator.service.gui.Container;
import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.*;
/** /**
* A {@link AbstractPluginComponent} that registers the Off-the-Record button in * A {@link AbstractPluginComponent} that registers the Off-the-Record button in
@ -28,69 +26,78 @@
*/ */
public class OtrMetaContactButton public class OtrMetaContactButton
extends AbstractPluginComponent extends AbstractPluginComponent
implements ScOtrEngineListener,
ScOtrKeyManagerListener
{ {
/**
* The logger
*/
private final Logger logger = Logger.getLogger(OtrMetaContactButton.class);
private SIPCommButton button; private SIPCommButton button;
private Contact contact; private Contact contact;
private final ScOtrEngineListener scOtrEngineListener = /**
new ScOtrEngineListener() * 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 (contact.equals(OtrMetaContactButton.this.contact))
{ {
public void sessionStatusChanged(Contact contact)
{
// OtrMetaContactButton.this.contact can be null.
if (contact.equals(OtrMetaContactButton.this.contact))
{
setStatus(
OtrActivator.scOtrEngine.getSessionStatus(contact));
}
}
public void contactPolicyChanged(Contact contact) setStatus(
{ OtrActivator.scOtrEngine.getSessionStatus(contact));
// OtrMetaContactButton.this.contact can be null. }
if (contact.equals(OtrMetaContactButton.this.contact)) }
{
setPolicy(
OtrActivator.scOtrEngine.getContactPolicy(contact));
}
}
public void globalPolicyChanged() public void contactPolicyChanged(Contact contact)
{ {
if (OtrMetaContactButton.this.contact != null) // OtrMetaContactButton.this.contact can be null.
setPolicy( if (contact.equals(OtrMetaContactButton.this.contact))
OtrActivator.scOtrEngine.getContactPolicy(contact));
}
};
private final ScOtrKeyManagerListener scOtrKeyManagerListener =
new ScOtrKeyManagerListener()
{ {
public void contactVerificationStatusChanged(Contact contact) setPolicy(
{ OtrActivator.scOtrEngine.getContactPolicy(contact));
// OtrMetaContactButton.this.contact can be null. }
if (contact.equals(OtrMetaContactButton.this.contact)) }
{
setStatus( public void globalPolicyChanged()
OtrActivator.scOtrEngine.getSessionStatus(contact)); {
} if (OtrMetaContactButton.this.contact != null)
} setPolicy(
}; OtrActivator.scOtrEngine.getContactPolicy(contact));
}
public void contactVerificationStatusChanged(Contact contact)
{
// OtrMetaContactButton.this.contact can be null.
if (contact.equals(OtrMetaContactButton.this.contact))
{
setStatus(
OtrActivator.scOtrEngine.getSessionStatus(contact));
}
}
public OtrMetaContactButton(Container container, public OtrMetaContactButton(Container container,
PluginComponentFactory parentFactory) PluginComponentFactory parentFactory)
{ {
super(container, parentFactory); super(container, parentFactory);
OtrActivator.scOtrEngine.addListener(scOtrEngineListener); /*
OtrActivator.scOtrKeyManager.addListener(scOtrKeyManagerListener); * XXX This OtrMetaContactButton 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
void dispose() * of this instance is never executed. OtrWeakListener will keep this
{ * instance as a listener of scOtrEngine and scOtrKeyManager for as long
OtrActivator.scOtrEngine.removeListener(scOtrEngineListener); * as this instance is necessary. And this instance will be strongly
OtrActivator.scOtrKeyManager.removeListener(scOtrKeyManagerListener); * 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<OtrMetaContactButton>(
this,
OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager);
} }
/** /**
@ -122,13 +129,15 @@ public void actionPerformed(ActionEvent e)
{ {
case ENCRYPTED: case ENCRYPTED:
case FINISHED: case FINISHED:
// Default action for finished and encrypted sessions is case LOADING:
// end session. // Default action for finished, encrypted and loading
// sessions is end session.
OtrActivator.scOtrEngine.endSession(contact); OtrActivator.scOtrEngine.endSession(contact);
break; break;
case TIMED_OUT:
case PLAINTEXT: case PLAINTEXT:
// Default action for finished and plaintext sessions is // Default action for timed_out and plaintext sessions
// start session. // is start session.
OtrActivator.scOtrEngine.startSession(contact); OtrActivator.scOtrEngine.startSession(contact);
break; break;
} }
@ -173,7 +182,7 @@ public void setCurrentContact(Contact contact)
} }
else else
{ {
this.setStatus(SessionStatus.PLAINTEXT); this.setStatus(ScSessionStatus.PLAINTEXT);
this.setPolicy(null); this.setPolicy(null);
} }
} }
@ -205,7 +214,7 @@ private void setPolicy(OtrPolicy contactPolicy)
* *
* @param status the {@link SessionStatus}. * @param status the {@link SessionStatus}.
*/ */
private void setStatus(SessionStatus status) private void setStatus(ScSessionStatus status)
{ {
String urlKey; String urlKey;
String tipKey; String tipKey;
@ -216,31 +225,37 @@ private void setStatus(SessionStatus status)
= OtrActivator.scOtrKeyManager.isVerified(contact) = OtrActivator.scOtrKeyManager.isVerified(contact)
? "plugin.otr.ENCRYPTED_ICON_22x22" ? "plugin.otr.ENCRYPTED_ICON_22x22"
: "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22"; : "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22";
tipKey = "plugin.otr.menu.END_OTR"; tipKey =
OtrActivator.scOtrKeyManager.isVerified(contact)
? "plugin.otr.menu.VERIFIED"
: "plugin.otr.menu.UNVERIFIED";
break; break;
case FINISHED: case FINISHED:
urlKey = "plugin.otr.FINISHED_ICON_22x22"; urlKey = "plugin.otr.FINISHED_ICON_22x22";
tipKey = "plugin.otr.menu.END_OTR"; tipKey = "plugin.otr.menu.FINISHED";
break; break;
case PLAINTEXT: case PLAINTEXT:
urlKey = "plugin.otr.PLAINTEXT_ICON_22x22"; urlKey = "plugin.otr.PLAINTEXT_ICON_22x22";
tipKey = "plugin.otr.menu.START_OTR"; tipKey = "plugin.otr.menu.START_OTR";
break; break;
case LOADING:
urlKey = "plugin.otr.LOADING_ICON_22x22";
tipKey = "plugin.otr.menu.LOADING_OTR";
break;
case TIMED_OUT:
urlKey = "plugin.otr.BROKEN_ICON_22x22";
tipKey = "plugin.otr.menu.TIMED_OUT";
break;
default: default:
return; return;
} }
try SIPCommButton button = getButton();
{ button.setIconImage(
SIPCommButton button = getButton(); Toolkit.getDefaultToolkit().getImage(
button.setImage( OtrActivator.resourceService.getImageURL(urlKey)));
ImageIO.read(OtrActivator.resourceService.getImageURL(urlKey))); button.setToolTipText(OtrActivator.resourceService
button.setToolTipText(OtrActivator.resourceService .getI18NString(tipKey));
.getI18NString(tipKey)); button.repaint();
}
catch (IOException e)
{
e.printStackTrace();
}
} }
} }

@ -7,7 +7,6 @@
package net.java.sip.communicator.plugin.otr; package net.java.sip.communicator.plugin.otr;
import net.java.otr4j.*; import net.java.otr4j.*;
import net.java.otr4j.session.*;
import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.event.*;
@ -27,12 +26,13 @@ public MessageDeliveredEvent messageDelivered(MessageDeliveredEvent evt)
Contact contact = evt.getDestinationContact(); Contact contact = evt.getDestinationContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
SessionStatus sessionStatus = ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact); OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't // If OTR is disabled and we are not over an encrypted session, don't
// process anything. // process anything.
if (!policy.getEnableManual() if (!policy.getEnableManual()
&& sessionStatus == SessionStatus.PLAINTEXT) && sessionStatus != ScSessionStatus.ENCRYPTED
&& sessionStatus != ScSessionStatus.FINISHED)
return evt; return evt;
if (OtrActivator.scOtrEngine.isMessageUIDInjected(evt if (OtrActivator.scOtrEngine.isMessageUIDInjected(evt
@ -63,12 +63,13 @@ public MessageDeliveredEvent messageDeliveryPending(
Contact contact = evt.getDestinationContact(); Contact contact = evt.getDestinationContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
SessionStatus sessionStatus = ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact); OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't // If OTR is disabled and we are not over an encrypted session, don't
// process anything. // process anything.
if (!policy.getEnableManual() if (!policy.getEnableManual()
&& sessionStatus == SessionStatus.PLAINTEXT) && sessionStatus != ScSessionStatus.ENCRYPTED
&& sessionStatus != ScSessionStatus.FINISHED)
return evt; return evt;
// If this is a message otr4j injected earlier, return the event as is. // If this is a message otr4j injected earlier, return the event as is.
@ -111,12 +112,13 @@ public MessageReceivedEvent messageReceived(MessageReceivedEvent evt)
Contact contact = evt.getSourceContact(); Contact contact = evt.getSourceContact();
OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact);
SessionStatus sessionStatus = ScSessionStatus sessionStatus =
OtrActivator.scOtrEngine.getSessionStatus(contact); OtrActivator.scOtrEngine.getSessionStatus(contact);
// If OTR is disabled and we are not over an encrypted session, don't // If OTR is disabled and we are not over an encrypted session, don't
// process anything. // process anything.
if (!policy.getEnableManual() if (!policy.getEnableManual()
&& sessionStatus == SessionStatus.PLAINTEXT) && sessionStatus != ScSessionStatus.ENCRYPTED
&& sessionStatus != ScSessionStatus.FINISHED)
return evt; return evt;
// Process the incoming message. // Process the incoming message.

@ -0,0 +1,155 @@
/*
* 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.lang.ref.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Implements a <tt>ScOtrEngineListener</tt> and
* <tt>ScOtrKeyManagerListener</tt> listener for the purposes of
* <tt>OtrContactMenu</tt> and <tt>OtrMetaContactButton</tt> which listen to
* <tt>ScOtrEngine</tt> and <tt>ScOtrKeyManager</tt> while weakly referencing
* them. Fixes a memory leak of <tt>OtrContactMenu</tt> and
* <tt>OtrMetaContactButton</tt> instances because these cannot determine when
* they are to be explicitly disposed.
*
* @author Lyubomir Marinov
*/
public class OtrWeakListener
<T extends ScOtrEngineListener &
ScOtrKeyManagerListener>
implements ScOtrEngineListener,
ScOtrKeyManagerListener
{
/**
* The <tt>ScOtrEngine</tt> the <tt>T</tt> associated with
* this instance is to listen to.
*/
private final ScOtrEngine engine;
/**
* The <tt>ScOtrKeyManager</tt> the <tt>T</tt> associated
* with this instance is to listen to.
*/
private final ScOtrKeyManager keyManager;
/**
* The <tt>T</tt> which is associated with this instance
* and which is to listen to {@link #engine} and {@link #keyManager}.
*/
private final WeakReference<T> listener;
/**
* Initializes a new <tt>OtrWeakListener</tt> instance which is to allow
* a specific <tt>T</tt> to listener to a specific
* <tt>ScOtrEngine</tt> and a specific <tt>ScOtrKeyManager</tt> without
* being retained by them forever (because they live forever).
*
* @param listener the <tt>T</tt> which is to listen to the
* specified <tt>engine</tt> and <tt>keyManager</tt>
* @param engine the <tt>ScOtrEngine</tt> which is to be listened to by
* the specified <tt>T</tt>
* @param keyManager the <tt>ScOtrKeyManager</tt> which is to be
* listened to by the specified <tt>T</tt>
*/
public OtrWeakListener(
T listener,
ScOtrEngine engine, ScOtrKeyManager keyManager)
{
if (listener == null)
throw new NullPointerException("listener");
this.listener = new WeakReference<T>(listener);
this.engine = engine;
this.keyManager = keyManager;
this.engine.addListener(this);
this.keyManager.addListener(this);
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>T</tt> if it is still needed by the application.
*/
public void contactPolicyChanged(Contact contact)
{
ScOtrEngineListener l = getListener();
if (l != null)
l.contactPolicyChanged(contact);
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>T</tt> if it is still needed by the application.
*/
public void contactVerificationStatusChanged(Contact contact)
{
ScOtrKeyManagerListener l = getListener();
if (l != null)
l.contactVerificationStatusChanged(contact);
}
/**
* Gets the <tt>T</tt> which is listening to {@link #engine}
* and {@link #keyManager}. If the <tt>T</tt> is no longer needed by
* the application, this instance seizes listening to <tt>engine</tt> and
* <tt>keyManager</tt> and allows the memory used by this instance to be
* reclaimed by the Java virtual machine.
*
* @return the <tt>T</tt> which is listening to
* <tt>engine</tt> and <tt>keyManager</tt> if it is still needed by the
* application; otherwise, <tt>null</tt>
*/
private T getListener()
{
T l = this.listener.get();
if (l == null)
{
engine.removeListener(this);
keyManager.removeListener(this);
}
return l;
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>T</tt> if it is still needed by the application.
*/
public void globalPolicyChanged()
{
ScOtrEngineListener l = getListener();
if (l != null)
l.globalPolicyChanged();
}
/**
* {@inheritDoc}
*
* Forwards the event/notification to the associated
* <tt>T</tt> if it is still needed by the application.
*/
public void sessionStatusChanged(Contact contact)
{
ScOtrEngineListener l = getListener();
if (l != null)
l.sessionStatusChanged(contact);
}
}

@ -7,7 +7,6 @@
package net.java.sip.communicator.plugin.otr; package net.java.sip.communicator.plugin.otr;
import net.java.otr4j.*; import net.java.otr4j.*;
import net.java.otr4j.session.*;
import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.*;
/** /**
@ -101,13 +100,13 @@ public abstract void respondSmp(
public abstract void refreshSession(Contact contact); public abstract void refreshSession(Contact contact);
/** /**
* Gets the {@link SessionStatus} for the given {@link Contact}. * Gets the {@link ScSessionStatus} for the given {@link Contact}.
* *
* @param contact the {@link Contact} whose {@link SessionStatus} we are * @param contact the {@link Contact} whose {@link ScSessionStatus} we are
* interested in. * interested in.
* @return the {@link SessionStatus}. * @return the {@link ScSessionStatus}.
*/ */
public abstract SessionStatus getSessionStatus(Contact contact); public abstract ScSessionStatus getSessionStatus(Contact contact);
// New Methods (Misc) // New Methods (Misc)

@ -314,6 +314,27 @@ public String getFallbackMessage(SessionID sessionID)
} }
} }
/**
* The max timeout period elapsed prior to establishing a TIMED_OUT session.
*/
private static final int SESSION_TIMEOUT =
OtrActivator.configService.getInt(
"net.java.sip.communicator.plugin.otr.SESSION_STATUS_TIMEOUT",
30000);
/**
* Manages the scheduling of TimerTasks that are used to set Contact's
* ScSessionStatus (to TIMED_OUT) after a period of time.
*/
private ScSessionStatusScheduler scheduler = new ScSessionStatusScheduler();
/**
* This mapping is used for taking care of keeping SessionStatus and
* ScSessionStatus in sync for every Session object.
*/
private Map<SessionID, ScSessionStatus> scSessionStatusMap =
new ConcurrentHashMap<SessionID, ScSessionStatus>();
private static final Map<ScSessionID, Contact> contactsMap = private static final Map<ScSessionID, Contact> contactsMap =
new Hashtable<ScSessionID, Contact>(); new Hashtable<ScSessionID, Contact>();
@ -388,6 +409,7 @@ public ScOtrEngineImpl()
// Clears the map after previous instance // Clears the map after previous instance
// This is required because of OSGi restarts in the same VM on Android // This is required because of OSGi restarts in the same VM on Android
contactsMap.clear(); contactsMap.clear();
scSessionStatusMap.clear();
this.otrEngine.addOtrEngineListener(new OtrEngineListener() this.otrEngine.addOtrEngineListener(new OtrEngineListener()
{ {
@ -397,10 +419,17 @@ public void sessionStatusChanged(SessionID sessionID)
if (contact == null) if (contact == null)
return; return;
// Cancels any scheduled tasks that will change the
// ScSessionStatus for this Contact
scheduler.cancel(contact);
ScSessionStatus scSessionStatus = getSessionStatus(contact);
String message = ""; String message = "";
switch (otrEngine.getSessionStatus(sessionID)) switch (otrEngine.getSessionStatus(sessionID))
{ {
case ENCRYPTED: case ENCRYPTED:
scSessionStatus = ScSessionStatus.ENCRYPTED;
scSessionStatusMap.put(sessionID, scSessionStatus);
PublicKey remotePubKey = PublicKey remotePubKey =
otrEngine.getRemotePublicKey(sessionID); otrEngine.getRemotePublicKey(sessionID);
@ -490,6 +519,8 @@ public void sessionStatusChanged(SessionID sessionID)
break; break;
case FINISHED: case FINISHED:
scSessionStatus = ScSessionStatus.FINISHED;
scSessionStatusMap.put(sessionID, scSessionStatus);
message = message =
OtrActivator.resourceService.getI18NString( OtrActivator.resourceService.getI18NString(
"plugin.otr.activator.sessionfinished", "plugin.otr.activator.sessionfinished",
@ -497,6 +528,8 @@ public void sessionStatusChanged(SessionID sessionID)
{ contact.getDisplayName() }); { contact.getDisplayName() });
break; break;
case PLAINTEXT: case PLAINTEXT:
scSessionStatus = ScSessionStatus.PLAINTEXT;
scSessionStatusMap.put(sessionID, scSessionStatus);
message = message =
OtrActivator.resourceService.getI18NString( OtrActivator.resourceService.getI18NString(
"plugin.otr.activator.sessionlost", new String[] "plugin.otr.activator.sessionlost", new String[]
@ -575,6 +608,8 @@ public void endSession(Contact contact)
SessionID sessionID = getSessionID(contact); SessionID sessionID = getSessionID(contact);
try try
{ {
setSessionStatus(contact, ScSessionStatus.PLAINTEXT);
otrEngine.endSession(sessionID); otrEngine.endSession(sessionID);
} }
catch (OtrException e) catch (OtrException e)
@ -617,9 +652,73 @@ private ScOtrEngineListener[] getListeners()
} }
} }
public SessionStatus getSessionStatus(Contact contact) /**
* Manages the scheduling of TimerTasks that are used to set Contact's
* ScSessionStatus after a period of time.
*
* @author Marin Dzhigarov
*/
private class ScSessionStatusScheduler
{ {
return otrEngine.getSessionStatus(getSessionID(contact)); private final Timer timer = new Timer();
private final Map<Contact, TimerTask> tasks =
new ConcurrentHashMap<Contact, TimerTask>();
public void scheduleScSessionStatusChange(
final Contact contact, final ScSessionStatus status)
{
cancel(contact);
TimerTask task = new TimerTask() {
@Override
public void run()
{
setSessionStatus(contact, status);
}
};
timer.schedule(task, SESSION_TIMEOUT);
tasks.put(contact, task);
}
public void cancel(final Contact contact)
{
TimerTask task = tasks.get(contact);
if (task != null)
task.cancel();
tasks.remove(contact);
}
}
private void setSessionStatus(Contact contact, ScSessionStatus status)
{
scSessionStatusMap.put(getSessionID(contact), status);
for (ScOtrEngineListener l : getListeners())
l.sessionStatusChanged(contact);
}
public ScSessionStatus getSessionStatus(Contact contact)
{
SessionID sessionID = getSessionID(contact);
SessionStatus sessionStatus = otrEngine.getSessionStatus(sessionID);
ScSessionStatus scSessionStatus = null;
if (!scSessionStatusMap.containsKey(sessionID))
{
switch (sessionStatus)
{
case PLAINTEXT:
scSessionStatus = ScSessionStatus.PLAINTEXT;
break;
case ENCRYPTED:
scSessionStatus = ScSessionStatus.ENCRYPTED;
break;
case FINISHED:
scSessionStatus = ScSessionStatus.FINISHED;
break;
}
scSessionStatusMap.put(sessionID, scSessionStatus);
}
return scSessionStatusMap.get(sessionID);
} }
public boolean isMessageUIDInjected(String mUID) public boolean isMessageUIDInjected(String mUID)
@ -751,6 +850,17 @@ public void showError(SessionID sessionID, String err)
public void startSession(Contact contact) public void startSession(Contact contact)
{ {
SessionID sessionID = getSessionID(contact); SessionID sessionID = getSessionID(contact);
ScSessionStatus scSessionStatus = getSessionStatus(contact);
scSessionStatus = ScSessionStatus.LOADING;
scSessionStatusMap.put(sessionID, scSessionStatus);
for (ScOtrEngineListener l : getListeners())
{
l.sessionStatusChanged(contact);
scheduler.scheduleScSessionStatusChange(
contact, ScSessionStatus.TIMED_OUT);
}
try try
{ {
otrEngine.startSession(sessionID); otrEngine.startSession(sessionID);

@ -0,0 +1,29 @@
/*
* 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;
/**
* Extends otr4j's <tt>SessionStatus</tt> with two additional states.
*
* @author Marin Dzhigarov
*/
public enum ScSessionStatus
{
PLAINTEXT,
ENCRYPTED,
FINISHED,
/*
* A Session transitions in LOADING state right before
* Session.startSession() is invoked.
*/
LOADING,
/*
* A Session transitions in TIMED_OUT state after being in LOADING state for
* a long period of time.
*/
TIMED_OUT
}
Loading…
Cancel
Save