/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.plugin.otr; 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; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; /** * A {@link AbstractPluginComponent} that registers the Off-the-Record button in * the main chat toolbar. * * @author George Politis * @author Marin Dzhigarov */ public class OtrMetaContactButton extends AbstractPluginComponent implements ScOtrEngineListener, ScOtrKeyManagerListener { /** * The logger */ private final Logger logger = Logger.getLogger(OtrMetaContactButton.class); private SIPCommButton button; private OtrContact otrContact; private AnimatedImage animatedPadlockImage; private Image finishedPadlockImage; private Image verifiedLockedPadlockImage; private Image unverifiedLockedPadlockImage; private Image unlockedPadlockImage; private Image timedoutPadlockImage; public void sessionStatusChanged(OtrContact otrContact) { // OtrMetaContactButton.this.contact can be null. if (otrContact.equals(OtrMetaContactButton.this.otrContact)) { setStatus( OtrActivator.scOtrEngine.getSessionStatus(otrContact)); } } public void contactPolicyChanged(Contact contact) { // OtrMetaContactButton.this.contact can be null. if (OtrMetaContactButton.this.otrContact != null && contact.equals(OtrMetaContactButton.this.otrContact.contact)) { setPolicy( OtrActivator.scOtrEngine.getContactPolicy(contact)); } } public void globalPolicyChanged() { if (OtrMetaContactButton.this.otrContact != null) setPolicy( OtrActivator.scOtrEngine.getContactPolicy(otrContact.contact)); } public void contactVerificationStatusChanged(OtrContact otrContact) { // OtrMetaContactButton.this.contact can be null. if (otrContact.equals(OtrMetaContactButton.this.otrContact)) { setStatus( OtrActivator.scOtrEngine.getSessionStatus(otrContact)); } } public OtrMetaContactButton(Container container, PluginComponentFactory parentFactory) { super(container, parentFactory); /* * 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 * 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<OtrMetaContactButton>( this, OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); } /** * Gets the <code>SIPCommButton</code> which is the component of this * plugin. If the button doesn't exist, it's created. * * @return the <code>SIPCommButton</code> which is the component of this * plugin */ @SuppressWarnings("fallthrough") private SIPCommButton getButton() { if (button == null) { button = new SIPCommButton(null, null); button.setEnabled(false); button.setPreferredSize(new Dimension(25, 25)); button.setToolTipText(OtrActivator.resourceService.getI18NString( "plugin.otr.menu.OTR_TOOLTIP")); Image i1 = null, i2 = null, i3 = null; try { i1 = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.LOADING_ICON1_22x22")); i2 = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.LOADING_ICON2_22x22")); i3 = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.LOADING_ICON3_22x22")); finishedPadlockImage = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.FINISHED_ICON_22x22")); verifiedLockedPadlockImage = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.ENCRYPTED_ICON_22x22")); unverifiedLockedPadlockImage = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22")); unlockedPadlockImage = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.PLAINTEXT_ICON_22x22")); timedoutPadlockImage = ImageIO.read( OtrActivator.resourceService.getImageURL( "plugin.otr.BROKEN_ICON_22x22")); } catch (IOException e) { logger.debug("Failed to load padlock image"); } animatedPadlockImage = new AnimatedImage(button, i1, i2, i3); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (otrContact == null) return; switch (OtrActivator.scOtrEngine.getSessionStatus(otrContact)) { 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); 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); break; } } }); } return button; } /* * Implements PluginComponent#getComponent(). Returns the SIPCommButton * which is the component of this plugin creating it first if it doesn't * exist. */ public Object getComponent() { return getButton(); } /* * Implements PluginComponent#getName(). */ public String getName() { return ""; } /* * Implements PluginComponent#setCurrentContact(Contact). */ @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); return; } if (resourceName == 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; } for (ContactResource resource : contact.getResources()) { 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; } } logger.debug("Could not find resource for contact " + contact); } /* * Implements PluginComponent#setCurrentContact(MetaContact). */ @Override public void setCurrentContact(MetaContact metaContact) { setCurrentContact((metaContact == null) ? null : metaContact .getDefaultContact()); } /** * Sets the button enabled status according to the passed in * {@link OtrPolicy}. * * @param contactPolicy the {@link OtrPolicy}. */ private void setPolicy(OtrPolicy contactPolicy) { getButton().setEnabled( contactPolicy != null && contactPolicy.getEnableManual()); } /** * Sets the button icon according to the passed in {@link SessionStatus}. * * @param status the {@link SessionStatus}. */ private void setStatus(ScSessionStatus status) { animatedPadlockImage.pause(); Image image; String tipKey; switch (status) { case ENCRYPTED: PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(otrContact); String fingerprint = OtrActivator.scOtrKeyManager. getFingerprintFromPublicKey(pubKey); image = OtrActivator.scOtrKeyManager.isVerified( otrContact.contact, fingerprint) ? verifiedLockedPadlockImage : unverifiedLockedPadlockImage; tipKey = OtrActivator.scOtrKeyManager.isVerified( otrContact.contact, fingerprint) ? "plugin.otr.menu.VERIFIED" : "plugin.otr.menu.UNVERIFIED"; break; case FINISHED: image = finishedPadlockImage; tipKey = "plugin.otr.menu.FINISHED"; break; case PLAINTEXT: image = unlockedPadlockImage; tipKey = "plugin.otr.menu.START_OTR"; break; case LOADING: image = animatedPadlockImage; animatedPadlockImage.start(); tipKey = "plugin.otr.menu.LOADING_OTR"; break; case TIMED_OUT: image = timedoutPadlockImage; tipKey = "plugin.otr.menu.TIMED_OUT"; break; default: return; } SIPCommButton button = getButton(); button.setIconImage(image); button.setToolTipText(OtrActivator.resourceService .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)); } } }