/* * 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.event.*; import java.security.*; import javax.swing.*; import net.java.otr4j.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; /** * A special {@link JMenu} that holds the menu items for controlling the * Off-the-Record functionality for a specific contact. * * @author George Politis * @author Lyubomir Marinov * @author Marin Dzhigarov */ class OtrContactMenu implements ActionListener, ScOtrEngineListener, ScOtrKeyManagerListener { private final Logger logger = Logger.getLogger(OtrContactMenu.class); private static final String ACTION_COMMAND_AUTHENTICATE_BUDDY = "AUTHENTICATE_BUDDY"; private static final String ACTION_COMMAND_CB_AUTO = "CB_AUTO"; private static final String ACTION_COMMAND_CB_AUTO_ALL = "CB_AUTO_ALL"; private static final String ACTION_COMMAND_CB_ENABLE = "CB_ENABLE"; private static final String ACTION_COMMAND_CB_REQUIRE = "CB_REQUIRE"; private static final String ACTION_COMMAND_CB_RESET = "CB_RESET"; private static final String ACTION_COMMAND_END_OTR = "END_OTR"; private static final String ACTION_COMMAND_REFRESH_OTR = "REFRESH_OTR"; private static final String ACTION_COMMAND_START_OTR = "START_OTR"; private final OtrContact contact; /** * The indicator which determines whether this <tt>JMenu</tt> is displayed * in the Mac OS X screen menu bar and thus should work around the known * problem of PopupMenuListener not being invoked. */ private final boolean inMacOSXScreenMenuBar; /** * We keep this variable so we can determine if the policy has changed * or not in {@link OtrContactMenu#setOtrPolicy(OtrPolicy)}. */ private OtrPolicy otrPolicy; private ScSessionStatus sessionStatus; private final JMenu parentMenu; private final SIPCommMenu separateMenu; /** * The OtrContactMenu constructor. * * @param otrContact the OtrContact this menu refers to. * @param inMacOSXScreenMenuBar <tt>true</tt> if the new menu is to be * displayed in the Mac OS X screen menu bar; <tt>false</tt>, otherwise * @param menu the parent menu */ public OtrContactMenu( OtrContact otrContact, boolean inMacOSXScreenMenuBar, JMenu menu, boolean isSeparateMenu) { this.contact = otrContact; this.inMacOSXScreenMenuBar = inMacOSXScreenMenuBar; this.parentMenu = menu; String resourceName = otrContact.resource != null ? "/" + otrContact.resource.getResourceName() : ""; separateMenu = isSeparateMenu ? new SIPCommMenu(otrContact.contact.getDisplayName() + resourceName) : null; /* * XXX This OtrContactMenu 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<OtrContactMenu>( this, OtrActivator.scOtrEngine, OtrActivator.scOtrKeyManager); setSessionStatus( OtrActivator.scOtrEngine.getSessionStatus(this.contact)); setOtrPolicy( OtrActivator.scOtrEngine.getContactPolicy(otrContact.contact)); buildMenu(); } /* * Implements ActionListener#actionPerformed(ActionEvent). */ public void actionPerformed(ActionEvent e) { String actionCommand = e.getActionCommand(); if (ACTION_COMMAND_END_OTR.equals(actionCommand)) { OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); policy.setSendWhitespaceTag(false); OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); // End session. OtrActivator.scOtrEngine.endSession(contact); } else if (ACTION_COMMAND_START_OTR.equals(actionCommand)) { OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); OtrPolicy globalPolicy = OtrActivator.scOtrEngine.getGlobalPolicy(); policy.setSendWhitespaceTag(globalPolicy.getSendWhitespaceTag()); OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); // Start session. OtrActivator.scOtrEngine.startSession(contact); } else if (ACTION_COMMAND_REFRESH_OTR.equals(actionCommand)) // Refresh session. OtrActivator.scOtrEngine.refreshSession(contact); else if (ACTION_COMMAND_AUTHENTICATE_BUDDY.equals(actionCommand)) // Launch auth buddy dialog. SwingOtrActionHandler.openAuthDialog(contact); else if (ACTION_COMMAND_CB_ENABLE.equals(actionCommand)) { OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); policy.setEnableManual(state); OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); } else if (ACTION_COMMAND_CB_AUTO.equals(actionCommand)) { OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); policy.setSendWhitespaceTag(state); OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); } else if (ACTION_COMMAND_CB_AUTO_ALL.equals(actionCommand)) { OtrPolicy globalPolicy = OtrActivator.scOtrEngine.getGlobalPolicy(); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); globalPolicy.setSendWhitespaceTag(state); OtrActivator.scOtrEngine.setGlobalPolicy(globalPolicy); } else if (ACTION_COMMAND_CB_REQUIRE.equals(actionCommand)) { OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); boolean state = ((JCheckBoxMenuItem) e.getSource()).isSelected(); policy.setRequireEncryption(state); OtrActivator.configService.setProperty( OtrActivator.OTR_MANDATORY_PROP, Boolean.toString(state)); OtrActivator.scOtrEngine.setContactPolicy(contact.contact, policy); } else if (ACTION_COMMAND_CB_RESET.equals(actionCommand)) OtrActivator.scOtrEngine.setContactPolicy(contact.contact, null); } /* * Implements ScOtrEngineListener#contactPolicyChanged(Contact). */ public void contactPolicyChanged(Contact contact) { // Update the corresponding to the contact menu. if (OtrContactMenu.this.contact != null && contact.equals(OtrContactMenu.this.contact.contact)) setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact)); } /* * Implements ScOtrKeyManagerListener#contactVerificationStatusChanged( * Contact). */ public void contactVerificationStatusChanged(OtrContact otrContact) { if (otrContact.equals(OtrContactMenu.this.contact)) setSessionStatus( OtrActivator.scOtrEngine.getSessionStatus(otrContact)); } /** * Disposes of this instance by making it available for garage collection * e.g. removes the listeners it has installed on global instances such as * <tt>OtrActivator#scOtrEngine</tt> and * <tt>OtrActivator#scOtrKeyManager</tt>. */ void dispose() { OtrActivator.scOtrEngine.removeListener(this); OtrActivator.scOtrKeyManager.removeListener(this); } /* * Implements ScOtrEngineListener#globalPolicyChanged(). */ public void globalPolicyChanged() { setOtrPolicy(OtrActivator.scOtrEngine.getContactPolicy(contact.contact)); } /** * Rebuilds own menuitems according to {@link OtrContactMenu#sessionStatus} * and the {@link OtrPolicy} for {@link OtrContactMenu#contact}. */ private void buildMenu() { if(separateMenu != null) separateMenu.removeAll(); OtrPolicy policy = OtrActivator.scOtrEngine.getContactPolicy(contact.contact); JMenuItem endOtr = new JMenuItem(); endOtr.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.END_OTR")); endOtr.setActionCommand(ACTION_COMMAND_END_OTR); endOtr.addActionListener(this); JMenuItem startOtr = new JMenuItem(); startOtr.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.START_OTR")); startOtr.setEnabled(policy.getEnableManual()); startOtr.setActionCommand(ACTION_COMMAND_START_OTR); startOtr.addActionListener(this); JMenuItem refreshOtr = new JMenuItem(); refreshOtr.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.REFRESH_OTR")); refreshOtr.setEnabled(policy.getEnableManual()); refreshOtr.setActionCommand(ACTION_COMMAND_REFRESH_OTR); refreshOtr.addActionListener(this); switch (this.sessionStatus) { case LOADING: if (separateMenu != null) { separateMenu.add(endOtr); separateMenu.add(refreshOtr); } else { parentMenu.add(endOtr); parentMenu.add(refreshOtr); } break; case ENCRYPTED: JMenuItem authBuddy = new JMenuItem(); authBuddy.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.AUTHENTICATE_BUDDY")); authBuddy.setActionCommand(ACTION_COMMAND_AUTHENTICATE_BUDDY); authBuddy.addActionListener(this); if (separateMenu != null) { separateMenu.add(endOtr); separateMenu.add(refreshOtr); separateMenu.add(authBuddy); } else { parentMenu.add(endOtr); parentMenu.add(refreshOtr); parentMenu.add(authBuddy); } break; case FINISHED: if (separateMenu != null) { separateMenu.add(endOtr); separateMenu.add(refreshOtr); } else { parentMenu.add(endOtr); parentMenu.add(refreshOtr); } break; case TIMED_OUT: case PLAINTEXT: if (separateMenu != null) separateMenu.add(startOtr); else parentMenu.add(startOtr); break; } JCheckBoxMenuItem cbEnable = new JCheckBoxMenuItem(); cbEnable.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.CB_ENABLE")); cbEnable.setSelected(policy.getEnableManual()); cbEnable.setActionCommand(ACTION_COMMAND_CB_ENABLE); cbEnable.addActionListener(this); JCheckBoxMenuItem cbAlways = new JCheckBoxMenuItem(); cbAlways.setText(String.format( OtrActivator.resourceService .getI18NString( "plugin.otr.menu.CB_AUTO", new String[] {contact.contact.getDisplayName()}))); cbAlways.setEnabled(policy.getEnableManual()); cbAlways.setSelected(policy.getEnableAlways()); cbAlways.setActionCommand(ACTION_COMMAND_CB_AUTO); cbAlways.addActionListener(this); JCheckBoxMenuItem cbAlwaysAll = new JCheckBoxMenuItem(); cbAlwaysAll.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.CB_AUTO_ALL")); cbAlwaysAll.setEnabled(policy.getEnableManual()); boolean isAutoInit = OtrActivator.scOtrEngine.getGlobalPolicy().getEnableAlways(); cbAlwaysAll.setSelected(isAutoInit); cbAlwaysAll.setActionCommand(ACTION_COMMAND_CB_AUTO_ALL); cbAlwaysAll.addActionListener(this); JCheckBoxMenuItem cbRequire = new JCheckBoxMenuItem(); cbRequire.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.CB_REQUIRE")); cbRequire.setEnabled(policy.getEnableManual()); String otrMandatoryPropValue = OtrActivator.configService.getString( OtrActivator.OTR_MANDATORY_PROP); String defaultOtrPropValue = OtrActivator.resourceService.getSettingsString( OtrActivator.OTR_MANDATORY_PROP); boolean isMandatory = policy.getRequireEncryption(); if (otrMandatoryPropValue != null) isMandatory = Boolean.parseBoolean(otrMandatoryPropValue); else if (!isMandatory && defaultOtrPropValue != null) isMandatory = Boolean.parseBoolean(defaultOtrPropValue); cbRequire.setSelected(isMandatory); cbRequire.setActionCommand(ACTION_COMMAND_CB_REQUIRE); cbRequire.addActionListener(this); JMenuItem cbReset = new JMenuItem(); cbReset.setText(OtrActivator.resourceService .getI18NString("plugin.otr.menu.CB_RESET")); cbReset.setActionCommand(ACTION_COMMAND_CB_RESET); cbReset.addActionListener(this); if (separateMenu != null) { separateMenu.addSeparator(); separateMenu.add(cbEnable); separateMenu.add(cbAlways); separateMenu.add(cbAlwaysAll); separateMenu.add(cbRequire); separateMenu.addSeparator(); separateMenu.add(cbReset); parentMenu.add(separateMenu); } else { parentMenu.addSeparator(); parentMenu.add(cbEnable); parentMenu.add(cbAlways); parentMenu.add(cbAlwaysAll); parentMenu.add(cbRequire); parentMenu.addSeparator(); parentMenu.add(cbReset); } } /* * Implements ScOtrEngineListener#sessionStatusChanged(Contact). */ public void sessionStatusChanged(OtrContact otrContact) { if (otrContact.equals(OtrContactMenu.this.contact)) setSessionStatus( OtrActivator.scOtrEngine.getSessionStatus(otrContact)); } /** * Sets the {@link OtrContactMenu#sessionStatus} value, updates the menu * icon and, if necessary, rebuilds the menuitems to match the passed in * sessionStatus. * * @param sessionStatus the {@link ScSessionStatus}. */ private void setSessionStatus(ScSessionStatus sessionStatus) { if (sessionStatus != this.sessionStatus) { logger.debug( "Setting session status of contact " + contact.contact + " to " + sessionStatus + ". Was " + this.sessionStatus); this.sessionStatus = sessionStatus; if (separateMenu != null) { updateIcon(); if (separateMenu.isPopupMenuVisible() || inMacOSXScreenMenuBar) buildMenu(); } } } /** * Sets the {@link OtrContactMenu#otrPolicy} and, if necessary, rebuilds the * menuitems to match the passed in otrPolicy. * * @param otrPolicy */ private void setOtrPolicy(OtrPolicy otrPolicy) { if (!otrPolicy.equals(this.otrPolicy)) { this.otrPolicy = otrPolicy; if ((separateMenu != null) && (separateMenu.isPopupMenuVisible() || inMacOSXScreenMenuBar)) { buildMenu(); } } } /** * Updates the menu icon based on {@link OtrContactMenu#sessionStatus} * value. */ private void updateIcon() { if (separateMenu == null) return; String imageID; switch (sessionStatus) { case ENCRYPTED: PublicKey pubKey = OtrActivator.scOtrEngine.getRemotePublicKey(contact); String fingerprint = OtrActivator.scOtrKeyManager. getFingerprintFromPublicKey(pubKey); imageID = OtrActivator.scOtrKeyManager.isVerified( contact.contact, fingerprint) ? "plugin.otr.ENCRYPTED_ICON_16x16" : "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_16x16"; break; case FINISHED: imageID = "plugin.otr.FINISHED_ICON_16x16"; break; case PLAINTEXT: imageID = "plugin.otr.PLAINTEXT_ICON_16x16"; break; default: return; } separateMenu.setIcon(OtrActivator.resourceService.getImage(imageID)); } @Override public void multipleInstancesDetected(OtrContact contact) {} @Override public void outgoingSessionChanged(OtrContact otrContact) { if (otrContact.equals(OtrContactMenu.this.contact)) setSessionStatus( OtrActivator.scOtrEngine.getSessionStatus(otrContact)); } }