diff --git a/lib/installer-exclude/otr4j.jar b/lib/installer-exclude/otr4j.jar index 415a1dc45..561ac47e2 100644 Binary files a/lib/installer-exclude/otr4j.jar and b/lib/installer-exclude/otr4j.jar differ diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 4db6e0ecf..740b879b4 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -1519,13 +1519,30 @@ plugin.otr.menu.CB_RESET=Reset plugin.otr.menu.OTR_TOOLTIP=Encrypt chats with OTR plugin.otr.authbuddydialog.HELP_URI=https://jitsi.org/GSOC2009/OTR plugin.otr.authbuddydialog.AUTHENTICATION_INFO=Authenticating a buddy helps \ -ensure that the person you are talking to is who he or she claims to be. \ -To verify the fingerprint, contact your buddy via some other authenticated \ +ensure that the person you are talking to is who he or she claims to be. +plugin.otr.authbuddydialog.AUTHENTICATION_METHOD=How would you like to authenticate? +plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_QUESTION=Question authentication +plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_SECRET=Shared secret authentication +plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_FINGERPRINT=Fingerprint authentication +plugin.otr.authbuddydialog.AUTHENTICATION_FINGERPRINT=To verify the fingerprint, contact your buddy via some other authenticated \ channel, such as the telephone or GPG-signed email. Each of you should tell \ your fingerprint to the other. If everything matches up, you should indicate \ in the following dialog that you *have* verified the fingerprint. plugin.otr.authbuddydialog.LOCAL_FINGERPRINT=Fingerprint for you, {0}: {1} plugin.otr.authbuddydialog.REMOTE_FINGERPRINT=Purported fingerprint for {0}: {1} +plugin.otr.authbuddydialog.AUTH_BY_QUESTION_INFO_INIT=To authenticate using a question, you should pick a question whose answer \ +is only known to you and your buddy. Your buddy will be asked this question and if the answers don't match then you may be talking \ +to an impostor. +plugin.otr.authbuddydialog.AUTH_BY_SECRET_INFO_INIT=To authenticate using a shared secret, you should pick a secret that is only known \ +to you and your buddy. Your buddy will then be asked to pick the secret too and if they don't match then you may be talking \ +to an impostor. +plugin.otr.authbuddydialog.AUTH_BY_SECRET_INFO_RESPOND=Your buddy is trying to determine if he or she is really talking to you and not an impostor. \ +Your buddy has asked you a question. If you want to authenticate, please enter the answer below. +plugin.otr.authbuddydialog.SHARED_SECRET=Enter your shared secret: +plugin.otr.authbuddydialog.QUESTION_RESPOND=Your buddy is asking you: +plugin.otr.authbuddydialog.QUESTION_INIT=Enter your question here: +plugin.otr.authbuddydialog.ANSWER=Enter your answer here (Case sensitive): +plugin.otr.authbuddydialog.AUTHENTICATION_FROM=Authentication from {0}. plugin.otr.authbuddydialog.CANCEL=Cancel plugin.otr.authbuddydialog.HELP=Help plugin.otr.authbuddydialog.TITLE=Authenticate Buddy @@ -1534,6 +1551,9 @@ plugin.otr.authbuddydialog.I_HAVE=I have plugin.otr.authbuddydialog.I_HAVE_NOT=I have not plugin.otr.authbuddydialog.VERIFY_ACTION=verified that this is in fact the correct fingerprint for {0}. plugin.otr.authbuddydialog.FINGERPRINT_CHECK=Please enter the fingerprint you recieved from {0}. +plugin.otr.smpprogressdialog.TITLE=Authentication Progress +plugin.otr.smpprogressdialog.AUTHENTICATION_SUCCESS=Authentication successful! +plugin.otr.smpprogressdialog.AUTHENTICATION_FAIL=Authentication failed! plugin.otr.configform.MY_PRIVATE_KEYS=My Private Keys plugin.otr.configform.KNOWN_FINGERPRINTS=Known Fingerprints plugin.otr.configform.CB_AUTO=Automatically initiate private messaging @@ -1556,6 +1576,17 @@ plugin.otr.activator.sessionfinished={0} has ended his/her private conversation plugin.otr.activator.sessionlost=Private conversation with {0} lost. plugin.otr.activator.historyon={0} is recording this conversation on your device. You can turn off chat history here. plugin.otr.activator.historyoff={0} is NOT recording this conversation. You can activate chat history here. +plugin.otr.activator.sessionfinishederror=Your message was not sent. Please end your private conversation with {0}. +plugin.otr.activator.smpaborted={0} has aborted the authentication process. +plugin.otr.activator.smperror=An error occurred in the authentication process. +plugin.otr.activator.unencryptedmsgreceived=The message was received unencrypted. +plugin.otr.activator.unreadablemsgreceived={0} sent you an unreadable encrypted message. +plugin.otr.activator.requireencryption=Your message was not sent. Private messaging is required. +plugin.otr.activator.unreadablemsgreply=You sent {0} an unreadable encrypted message. Please end your private conversation with {1} or refresh it. +plugin.otr.activator.fallbackmessage={0} is trying to initiate an encrypted \ +Off-The-Record conversation with you. However, your software does not support \ +Off-The-Record messaging. For more information see \ +http://en.wikipedia.org/wiki/Off-the-Record_Messaging # global proxy plugin plugin.globalproxy.GLOBAL_PROXY_CONFIG=Global Proxy diff --git a/src/net/java/sip/communicator/plugin/otr/CustomTextArea.java b/src/net/java/sip/communicator/plugin/otr/CustomTextArea.java new file mode 100644 index 000000000..00aa7d182 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/otr/CustomTextArea.java @@ -0,0 +1,26 @@ +package net.java.sip.communicator.plugin.otr; + +import java.awt.*; + +import javax.swing.*; + +/** + * A special {@link JTextArea} for use in the OTR authentication panels. + * It is meant to be used for fingerprint representation and general + * information display. + * + * @author George Politis + */ + public class CustomTextArea + extends JTextArea + { + public CustomTextArea() + { + this.setBackground(new Color(0,0,0,0)); + this.setOpaque(false); + this.setColumns(20); + this.setEditable(false); + this.setLineWrap(true); + this.setWrapStyleWord(true); + } + } \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/otr/FingerprintAuthenticationPanel.java b/src/net/java/sip/communicator/plugin/otr/FingerprintAuthenticationPanel.java new file mode 100644 index 000000000..70ef5d8f5 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/otr/FingerprintAuthenticationPanel.java @@ -0,0 +1,257 @@ +/* + * 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.awt.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * @author George Politis + * @author Marin Dzhigarov + */ +@SuppressWarnings("serial") +public class FingerprintAuthenticationPanel + extends TransparentPanel + implements DocumentListener +{ + + /** + * The Contact that we are authenticating. + */ + private final Contact contact; + + private SIPCommTextField txtRemoteFingerprintComparison; + + /** + * Our fingerprint. + */ + private JTextArea txtLocalFingerprint; + + /** + * The purported fingerprint of the remote party. + */ + private JTextArea txtRemoteFingerprint; + + /** + * The "I have" / "I have not" combo box. + */ + private JComboBox cbAction; + + private ActionComboBoxItem actionIHave = + new ActionComboBoxItem(ActionComboBoxItemIndex.I_HAVE); + + private ActionComboBoxItem actionIHaveNot = + new ActionComboBoxItem(ActionComboBoxItemIndex.I_HAVE_NOT); + + private JTextArea txtAction; + + /** + * Creates an instance FingerprintAuthenticationPanel + * + * @param contact The contact that this panel refers to. + */ + FingerprintAuthenticationPanel(Contact contact) + { + this.contact = contact; + initComponents(); + loadContact(); + + } + + /** + * Initializes the {@link FingerprintAuthenticationPanel} components. + */ + private void initComponents() + { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setPreferredSize(new Dimension(350, 300)); + + JTextArea generalInformation = new CustomTextArea(); + generalInformation.setText(OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_FINGERPRINT")); + this.add(generalInformation); + + add(Box.createVerticalStrut(10)); + + txtLocalFingerprint = new CustomTextArea(); + this.add(txtLocalFingerprint); + + add(Box.createVerticalStrut(10)); + + txtRemoteFingerprint = new CustomTextArea(); + this.add(txtRemoteFingerprint); + + add(Box.createVerticalStrut(10)); + + // Action Panel (the panel that holds the I have/I have not dropdown) + JPanel pnlAction = new JPanel(new GridBagLayout()); + pnlAction.setBorder(BorderFactory.createEtchedBorder()); + this.add(pnlAction); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(5, 5, 5, 5); + c.weightx = 0.0; + + setCbAction(new JComboBox()); + getCbAction().addItem(actionIHave); + getCbAction().addItem(actionIHaveNot); + getCbAction().setSelectedItem(OtrActivator.scOtrKeyManager + .isVerified(contact) ? actionIHave : actionIHaveNot); + + pnlAction.add(getCbAction(), c); + + txtAction = new CustomTextArea(); + c.weightx = 1.0; + pnlAction.add(txtAction, c); + + txtRemoteFingerprintComparison = new SIPCommTextField( + OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.FINGERPRINT_CHECK", + new String[]{contact.getDisplayName()})); + txtRemoteFingerprintComparison.getDocument().addDocumentListener(this); + + c.gridwidth = 2; + c.gridy = 1; + pnlAction.add(txtRemoteFingerprintComparison, c); + c.gridwidth = 1; + c.gridy = 0; + } + + /** + * Sets up the {@link OtrBuddyAuthenticationDialog} components so that they + * reflect the {@link OtrBuddyAuthenticationDialog#contact} + */ + private void loadContact() + { + // Local fingerprint. + String account = + contact.getProtocolProvider().getAccountID().getDisplayName(); + String localFingerprint = + OtrActivator.scOtrKeyManager.getLocalFingerprint(contact + .getProtocolProvider().getAccountID()); + txtLocalFingerprint.setText(OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.LOCAL_FINGERPRINT", new String[] + { account, localFingerprint })); + + // Remote fingerprint. + String user = contact.getDisplayName(); + String remoteFingerprint = + OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact); + txtRemoteFingerprint.setText(OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.REMOTE_FINGERPRINT", + new String[] + { user, remoteFingerprint })); + + // Action + txtAction.setText(OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.VERIFY_ACTION", new String[] + { user })); + } + + public void removeUpdate(DocumentEvent e) + { + compareFingerprints(); + } + + public void insertUpdate(DocumentEvent e) + { + compareFingerprints(); + } + + public void changedUpdate(DocumentEvent e) + { + compareFingerprints(); + } + + public void compareFingerprints() + { + if(txtRemoteFingerprintComparison.getText() == null + || txtRemoteFingerprintComparison.getText().length() == 0) + { + txtRemoteFingerprintComparison.setBackground(Color.white); + return; + } + if(txtRemoteFingerprintComparison.getText().toLowerCase().contains( + OtrActivator.scOtrKeyManager + .getRemoteFingerprint(contact).toLowerCase())) + { + txtRemoteFingerprintComparison.setBackground(Color.green); + getCbAction().setSelectedItem(actionIHave); + } + else + { + txtRemoteFingerprintComparison.setBackground( + new Color(243, 72, 48)); + getCbAction().setSelectedItem(actionIHaveNot); + } + } + + public JComboBox getCbAction() + { + return cbAction; + } + + public void setCbAction(JComboBox cbAction) + { + this.cbAction = cbAction; + } + + /** + * A simple enumeration that is meant to be used with + * {@link ActionComboBoxItem} to distinguish them (like an ID). + * + * @author George Politis + */ + enum ActionComboBoxItemIndex + { + I_HAVE, I_HAVE_NOT + } + + /** + * A special {@link JComboBox} that is hosted in + * {@link OtrBuddyAuthenticationDialog#cbAction}. + * + * @author George Politis + */ + class ActionComboBoxItem + { + public ActionComboBoxItemIndex action; + + private String text; + + public ActionComboBoxItem(ActionComboBoxItemIndex actionIndex) + { + this.action = actionIndex; + switch (action) + { + case I_HAVE: + text = + OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.I_HAVE"); + break; + case I_HAVE_NOT: + text = + OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.I_HAVE_NOT"); + break; + } + } + + @Override + public String toString() + { + return text; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java b/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java index 5d5e06c87..a78fb7a35 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrBuddyAuthenticationDialog.java @@ -10,18 +10,18 @@ import java.awt.event.*; import javax.swing.*; -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.FingerprintAuthenticationPanel.ActionComboBoxItem; +import net.java.sip.communicator.service.protocol.*; /** * @author George Politis + * @author Marin Dzhigarov */ @SuppressWarnings("serial") public class OtrBuddyAuthenticationDialog extends SIPCommDialog - implements DocumentListener { private final Contact contact; @@ -37,121 +37,6 @@ public OtrBuddyAuthenticationDialog(Contact contact) this.contact = contact; initComponents(); - loadContact(); - } - - private SIPCommTextField txtRemoteFingerprintComparison; - - private JTextArea txtLocalFingerprint; - - private JTextArea txtRemoteFingerprint; - - private JComboBox cbAction; - ActionComboBoxItem actionIHave = - new ActionComboBoxItem(ActionComboBoxItemIndex.I_HAVE); - ActionComboBoxItem actionIHaveNot = - new ActionComboBoxItem(ActionComboBoxItemIndex.I_HAVE_NOT); - - private JTextArea txtAction; - - /** - * Sets up the {@link OtrBuddyAuthenticationDialog} components so that they - * reflect the {@link OtrBuddyAuthenticationDialog#contact} - */ - private void loadContact() - { - // Local fingerprint. - String account = - contact.getProtocolProvider().getAccountID().getDisplayName(); - String localFingerprint = - OtrActivator.scOtrKeyManager.getLocalFingerprint(contact - .getProtocolProvider().getAccountID()); - txtLocalFingerprint.setText(OtrActivator.resourceService.getI18NString( - "plugin.otr.authbuddydialog.LOCAL_FINGERPRINT", new String[] - { account, localFingerprint })); - - // Remote fingerprint. - String user = contact.getDisplayName(); - String remoteFingerprint = - OtrActivator.scOtrKeyManager.getRemoteFingerprint(contact); - txtRemoteFingerprint.setText(OtrActivator.resourceService - .getI18NString("plugin.otr.authbuddydialog.REMOTE_FINGERPRINT", - new String[] - { user, remoteFingerprint })); - - // Action - txtAction.setText(OtrActivator.resourceService.getI18NString( - "plugin.otr.authbuddydialog.VERIFY_ACTION", new String[] - { user })); - } - - /** - * A special {@link JTextArea} for use in the - * {@link OtrBuddyAuthenticationDialog}. It is meant to be used for - * fingerprint representation and general information display. - * - * @author George Politis - */ - class CustomTextArea - extends JTextArea - { - public CustomTextArea() - { - this.setBackground(new Color(0,0,0,0)); - this.setOpaque(false); - this.setColumns(20); - this.setEditable(false); - this.setLineWrap(true); - this.setWrapStyleWord(true); - } - } - - /** - * A simple enumeration that is meant to be used with - * {@link ActionComboBoxItem} to distinguish them (like an ID). - * - * @author George Politis - */ - enum ActionComboBoxItemIndex - { - I_HAVE, I_HAVE_NOT - } - - /** - * A special {@link JComboBox} that is hosted in - * {@link OtrBuddyAuthenticationDialog#cbAction}. - * - * @author George Politis - */ - class ActionComboBoxItem - { - public ActionComboBoxItemIndex action; - - private String text; - - public ActionComboBoxItem(ActionComboBoxItemIndex actionIndex) - { - this.action = actionIndex; - switch (action) - { - case I_HAVE: - text = - OtrActivator.resourceService - .getI18NString("plugin.otr.authbuddydialog.I_HAVE"); - break; - case I_HAVE_NOT: - text = - OtrActivator.resourceService - .getI18NString("plugin.otr.authbuddydialog.I_HAVE_NOT"); - break; - } - } - - @Override - public String toString() - { - return text; - } } /** @@ -172,45 +57,60 @@ private void initComponents() .getI18NString("plugin.otr.authbuddydialog.AUTHENTICATION_INFO")); mainPanel.add(generalInformation); - txtLocalFingerprint = new CustomTextArea(); - mainPanel.add(txtLocalFingerprint); - - txtRemoteFingerprint = new CustomTextArea(); - mainPanel.add(txtRemoteFingerprint); - - // Action Panel (the panel that holds the I have/I have not dropdown) - JPanel pnlAction = new JPanel(new GridBagLayout()); - pnlAction.setBorder(BorderFactory.createEtchedBorder()); - mainPanel.add(pnlAction); + mainPanel.add(Box.createVerticalStrut(10)); + + // Add authentication method label and combo box. + final String am[] = new String[]{ + OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_QUESTION"), + OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_SECRET"), + OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_METHOD_FINGERPRINT")}; + final JComboBox authenticationMethodComboBox = + new JComboBox(am); + JTextArea authMethodLabel = new CustomTextArea(); + authMethodLabel.setText( + OtrActivator.resourceService.getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_METHOD")); + mainPanel.add(authMethodLabel); + mainPanel.add(authenticationMethodComboBox); + mainPanel.add(Box.createVerticalStrut(10)); + + // Add authentication panels in a card layout so that the user can + // use the combo box to switch between authentication methods. + final JPanel authenticationPanel = + new TransparentPanel(new CardLayout()); + final FingerprintAuthenticationPanel fingerprintPanel = + new FingerprintAuthenticationPanel(contact); + final SecretQuestionAuthenticationPanel secretQuestionPanel = + new SecretQuestionAuthenticationPanel(); + final SharedSecretAuthenticationPanel sharedSecretPanel = + new SharedSecretAuthenticationPanel(); + authenticationPanel.add(secretQuestionPanel, am[0]); + authenticationPanel.add(sharedSecretPanel, am[1]); + authenticationPanel.add(fingerprintPanel, am[2]); + + authenticationMethodComboBox.addItemListener(new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent e) + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + CardLayout cl = + (CardLayout) (authenticationPanel.getLayout()); + cl.show(authenticationPanel, (String)e.getItem()); + } + } + }); + authenticationMethodComboBox.setSelectedIndex(0); + mainPanel.add(authenticationPanel); GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets(5, 5, 5, 5); - c.weightx = 0.0; - - cbAction = new JComboBox(); - cbAction.addItem(actionIHave); - cbAction.addItem(actionIHaveNot); - cbAction.setSelectedItem(OtrActivator.scOtrKeyManager - .isVerified(contact) ? actionIHave : actionIHaveNot); - - pnlAction.add(cbAction, c); - - txtAction = new CustomTextArea(); c.weightx = 1.0; - pnlAction.add(txtAction, c); - - txtRemoteFingerprintComparison = new SIPCommTextField( - OtrActivator.resourceService - .getI18NString("plugin.otr.authbuddydialog.FINGERPRINT_CHECK", - new String[]{contact.getDisplayName()})); - txtRemoteFingerprintComparison.getDocument().addDocumentListener(this); - - c.gridwidth = 2; - c.gridy = 1; - pnlAction.add(txtRemoteFingerprintComparison, c); c.gridwidth = 1; - c.gridy = 0; // Buttons panel. JPanel buttonPanel = new TransparentPanel(new GridBagLayout()); @@ -254,19 +154,40 @@ public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) { - ActionComboBoxItem actionItem = - (ActionComboBoxItem) cbAction.getSelectedItem(); - switch (actionItem.action) + String authenticationMethod = + (String)authenticationMethodComboBox.getSelectedItem(); + if (authenticationMethod.equals(am[0])) { - case I_HAVE: - OtrActivator.scOtrKeyManager.verify(contact); - break; - case I_HAVE_NOT: - OtrActivator.scOtrKeyManager.unverify(contact); - break; + String secret = secretQuestionPanel.getSecret(); + String question = secretQuestionPanel.getQuestion(); + + OtrActivator.scOtrEngine.initSmp(contact, question, secret); + dispose(); } + else if (authenticationMethod.equals(am[1])) + { + String secret = secretQuestionPanel.getSecret(); + String question = null; - dispose(); + OtrActivator.scOtrEngine.initSmp(contact, question, secret); + dispose(); + } + else if (authenticationMethod.equals(am[2])) + { + ActionComboBoxItem actionItem = + (ActionComboBoxItem) fingerprintPanel. + getCbAction().getSelectedItem(); + switch (actionItem.action) + { + case I_HAVE: + OtrActivator.scOtrKeyManager.verify(contact); + break; + case I_HAVE_NOT: + OtrActivator.scOtrKeyManager.unverify(contact); + break; + } + dispose(); + } } }); buttonPanel.add(authenticateButton, c); @@ -275,42 +196,4 @@ public void actionPerformed(ActionEvent e) this.getContentPane().add(buttonPanel, BorderLayout.SOUTH); this.pack(); } - - public void removeUpdate(DocumentEvent e) - { - compareFingerprints(); - } - - public void insertUpdate(DocumentEvent e) - { - compareFingerprints(); - } - - public void changedUpdate(DocumentEvent e) - { - compareFingerprints(); - } - - public void compareFingerprints() - { - if(txtRemoteFingerprintComparison.getText() == null - || txtRemoteFingerprintComparison.getText().length() == 0) - { - txtRemoteFingerprintComparison.setBackground(Color.white); - return; - } - if(txtRemoteFingerprintComparison.getText().toLowerCase().contains( - OtrActivator.scOtrKeyManager - .getRemoteFingerprint(contact).toLowerCase())) - { - txtRemoteFingerprintComparison.setBackground(Color.green); - cbAction.setSelectedItem(actionIHave); - } - else - { - txtRemoteFingerprintComparison.setBackground( - new Color(243, 72, 48)); - cbAction.setSelectedItem(actionIHaveNot); - } - } } diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java index 1e7b5e8e8..3e8e6e520 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java @@ -20,6 +20,38 @@ public interface ScOtrEngine { // Proxy methods OtrEngine. + /** + * Initializes Smp negotiation. + * See {@link http://en.wikipedia.org/wiki/Socialist_Millionaire_Problem} + * + * @param contact The contact with whom we want to start the Smp negotiation + * @param question The question that is asked during the Smp negotiation + * @param secret The secret answer for the question. + */ + public abstract void initSmp( + Contact contact, String question, String secret); + + /** + * Responds to a question that is asked during the Smp negotiation process. + * See {@link http://en.wikipedia.org/wiki/Socialist_Millionaire_Problem} + * + * @param contact The contact for whom we want to respond to a question + * during the Smp negotiation process. + * @param question The question that is asked during the Smp negotiation. + * @param secret The secret answer for the question. + */ + public abstract void respondSmp( + Contact contact, String question, String secret); + + /** + * Aborts the Smp negotiation process. + * See {@link http://en.wikipedia.org/wiki/Socialist_Millionaire_Problem} + * + * @param contact The contact with whom we want to abort the + * Smp negotiation process. + */ + public abstract void abortSmp(Contact contact); + /** * Transforms an outgoing message. * diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java index f60ef331c..c465e94b4 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java @@ -6,13 +6,20 @@ */ package net.java.sip.communicator.plugin.otr; +import java.awt.*; +import java.awt.event.*; import java.net.*; import java.security.*; import java.util.*; +import java.util.List; +import java.util.concurrent.*; + +import javax.swing.*; +import javax.swing.plaf.basic.*; import net.java.otr4j.*; import net.java.otr4j.session.*; - +import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.browserlauncher.*; import net.java.sip.communicator.service.contactlist.*; import net.java.sip.communicator.service.gui.*; @@ -36,7 +43,7 @@ public class ScOtrEngineImpl class ScOtrEngineHost implements OtrEngineHost { - public KeyPair getKeyPair(SessionID sessionID) + public KeyPair getLocalKeyPair(SessionID sessionID) { AccountID accountID = OtrActivator.getAccountIDByUID(sessionID.getAccountID()); @@ -83,11 +90,242 @@ public void showWarning(SessionID sessionID, String warn) Chat.SYSTEM_MESSAGE, warn, OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); } + + @Override + public void unreadableMessageReceived(SessionID sessionID) + throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unreadablemsgreceived", + new String[] {contact.getDisplayName()}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void unencryptedMessageReceived(SessionID sessionID, String msg) + throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + String warn = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unencryptedmsgreceived"); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, warn, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void smpError(SessionID sessionID, int tlvType, boolean cheated) + throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + logger.debug("SMP error occurred" + + ". Contact: " + contact.getDisplayName() + + ". TLV type: " + tlvType + + ". Cheated: " + cheated); + + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.smperror"); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + + @Override + public void smpAborted(SessionID sessionID) throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + Session session = otrEngine.getSession(sessionID); + if (session.isSmpInProgress()) + { + String warn = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.smpaborted", + new String[] {contact.getDisplayName()}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, warn, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + } + + @Override + public void finishedSessionMessage(SessionID sessionID) + throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.sessionfinishederror", + new String[] {contact.getDisplayName()}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void requireEncryptedMessage(SessionID sessionID, String msgText) + throws OtrException + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.requireencryption", + new String[] {contact.getDisplayName()}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public byte[] getLocalFingerprintRaw(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + return + OtrActivator.scOtrKeyManager.getLocalFingerprintRaw(accountID); + } + + @Override + public void askForSecret(SessionID sessionID, String question) + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + SmpAuthenticateBuddyDialog dialog = + new SmpAuthenticateBuddyDialog(contact, question); + dialog.setVisible(true); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.init(); + progressDialog.setVisible(true); + } + + @Override + public void verify(SessionID sessionID, boolean approved) + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + OtrActivator.scOtrKeyManager.verify(contact); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.setProgressSuccess(); + progressDialog.setVisible(true); + } + + @Override + public void unverify(SessionID sessionID) + { + Contact contact = getContact(sessionID); + if (contact == null) + return; + + OtrActivator.scOtrKeyManager.unverify(contact); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + + @Override + public String getReplyForUnreadableMessage(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + + return OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unreadablemsgreply", + new String[] {accountID.getDisplayName(), + accountID.getDisplayName()}); + } + + @Override + public String getFallbackMessage(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + + return OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.fallbackmessage", + new String[] {accountID.getDisplayName()}); + } } private static final Map contactsMap = new Hashtable(); + private static final Map progressDialogMap = + new ConcurrentHashMap(); + public static Contact getContact(SessionID sessionID) { return contactsMap.get(new ScSessionID(sessionID)); @@ -145,11 +383,14 @@ public static SessionID getSessionID(Contact contact) */ private final Logger logger = Logger.getLogger(ScOtrEngineImpl.class); - private final OtrEngine otrEngine - = new OtrEngineImpl(new ScOtrEngineHost()); + final OtrEngineHost otrEngineHost = new ScOtrEngineHost(); + + private final OtrEngine otrEngine; public ScOtrEngineImpl() { + otrEngine = new OtrEngineImpl(otrEngineHost); + // Clears the map after previous instance // This is required because of OSGi restarts in the same VM on Android contactsMap.clear(); @@ -449,6 +690,9 @@ public void serviceChanged(ServiceEvent ev) logger.debug( "Unregistering a ProtocolProviderService, cleaning" + " OTR's ScSessionID to Contact map."); + logger.debug( + "Unregistering a ProtocolProviderService, cleaning" + + " OTR's Contact to SpmProgressDialog map."); } ProtocolProviderService provider @@ -464,6 +708,14 @@ public void serviceChanged(ServiceEvent ev) i.remove(); } } + + Iterator i = progressDialogMap.keySet().iterator(); + + while (i.hasNext()) + { + if (provider.equals(i.next().getProtocolProvider())) + i.remove(); + } } } @@ -545,4 +797,398 @@ public String transformSending(Contact contact, String msgText) return null; } } + + private Session getSession(Contact contact) + { + SessionID sessionID = getSessionID(contact); + return otrEngine.getSession(sessionID); + } + + @Override + public void initSmp(Contact contact, String question, String secret) + { + Session session = getSession(contact); + try + { + session.initSmp(question, secret); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.init(); + progressDialog.setVisible(true); + } + catch (OtrException e) + { + logger.error("Error initializing SMP session with contact " + + contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + } + + @Override + public void respondSmp(Contact contact, String question, String secret) + { + Session session = getSession(contact); + try + { + session.respondSmp(question, secret); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.incrementProgress(); + progressDialog.setVisible(true); + } + catch (OtrException e) + { + logger.error( + "Error occured when sending SMP response to contact " + + contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + } + + @Override + public void abortSmp(Contact contact) + { + Session session = getSession(contact); + try + { + session.abortSmp(); + + SmpProgressDialog progressDialog = progressDialogMap.get(contact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(contact, progressDialog); + } + + progressDialog.dispose(); + } + catch (OtrException e) + { + logger.error("Error aborting SMP session with contact " + + contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + + } + + /** + * The dialog that pops up when SMP negotiation starts. + * It contains a progress bar that indicates the status of the SMP + * authentication process. + */ + @SuppressWarnings("serial") + private class SmpProgressDialog + extends SIPCommDialog + { + private final JProgressBar progressBar = new JProgressBar(0, 100); + + private final Color successColor = new Color(86, 140, 2); + + private final Color failColor = new Color(204, 0, 0); + + private final JLabel iconLabel = new JLabel(); + + /** + * Instantiates SmpProgressDialog. + * + * @param contact The contact that this dialog is associated with. + */ + public SmpProgressDialog(Contact contact) + { + setTitle( + OtrActivator.resourceService.getI18NString( + "plugin.otr.smpprogressdialog.TITLE")); + + JPanel mainPanel = new TransparentPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.setBorder( + BorderFactory.createEmptyBorder(10, 10, 10, 10)); + mainPanel.setPreferredSize(new Dimension(300, 70)); + + String authFromText = + String.format( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_FROM", + new String[] {contact.getDisplayName()})); + + JPanel labelsPanel = new TransparentPanel(); + labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.X_AXIS)); + + labelsPanel.add(iconLabel); + labelsPanel.add(Box.createRigidArea(new Dimension(5,0))); + labelsPanel.add(new JLabel(authFromText)); + + mainPanel.add(labelsPanel); + mainPanel.add(progressBar); + + init(); + + this.getContentPane().add(mainPanel); + this.pack(); + } + + /** + * Initializes the progress bar and sets it's progression to 1/3. + */ + public void init() + { + progressBar.setUI(new BasicProgressBarUI() { + private Rectangle r = new Rectangle(); + + @Override + protected void paintIndeterminate(Graphics g, JComponent c) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + r = getBox(r); + g.setColor(progressBar.getForeground()); + g.fillOval(r.x, r.y, r.width, r.height); + } + }); + progressBar.setValue(33); + progressBar.setForeground(successColor); + progressBar.setStringPainted(false); + iconLabel.setIcon( + OtrActivator.resourceService.getImage( + "plugin.otr.ENCRYPTED_UNVERIFIED_ICON_22x22")); + } + + /** + * Sets the progress bar to 2/3 of completion. + */ + public void incrementProgress() + { + progressBar.setValue(66); + } + + /** + * Sets the progress bar to green. + */ + public void setProgressSuccess() + { + progressBar.setValue(100); + progressBar.setForeground(successColor); + progressBar.setStringPainted(true); + progressBar.setString( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.smpprogressdialog.AUTHENTICATION_SUCCESS")); + iconLabel.setIcon( + OtrActivator.resourceService.getImage( + "plugin.otr.ENCRYPTED_ICON_22x22")); + } + + /** + * Sets the progress bar to red. + */ + public void setProgressFail() + { + progressBar.setValue(100); + progressBar.setForeground(failColor); + progressBar.setStringPainted(true); + progressBar.setString( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.smpprogressdialog.AUTHENTICATION_FAIL")); + } + } + + /** + * The dialog that pops up when the remote party send us SMP + * request. It contains detailed information for the user about + * the authentication process and allows him to authenticate. + * + */ + @SuppressWarnings("serial") + private class SmpAuthenticateBuddyDialog + extends SIPCommDialog + { + private final Contact contact; + + private final String question; + + SmpAuthenticateBuddyDialog(Contact contact, String question) + { + this.contact = contact; + this.question = question; + initComponents(); + } + + private void initComponents() + { + this.setTitle( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.TITLE")); + + // The main panel that contains all components. + JPanel mainPanel = new TransparentPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.setBorder( + BorderFactory.createEmptyBorder(10, 10, 10, 10)); + mainPanel.setPreferredSize(new Dimension(300, 350)); + + // Add "authentication from contact" to the main panel. + JTextArea authenticationFrom = new CustomTextArea(); + Font newFont = + new Font( + UIManager.getDefaults().getFont("TextArea.font"). + getFontName() + , Font.BOLD + , 14); + authenticationFrom.setFont(newFont); + String authFromText = + String.format( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_FROM", + new String[] {contact.getDisplayName()})); + authenticationFrom.setText(authFromText); + mainPanel.add(authenticationFrom); + + // Add "general info" text to the main panel. + JTextArea generalInfo = new CustomTextArea(); + generalInfo.setText(OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATION_INFO")); + mainPanel.add(generalInfo); + + // Add "authentication-by-secret" info text to the main panel. + JTextArea authBySecretInfo = new CustomTextArea(); + newFont = + new Font( + UIManager.getDefaults().getFont("TextArea.font"). + getFontName() + , Font.ITALIC + , 10); + authBySecretInfo.setText(OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTH_BY_SECRET_INFO_RESPOND")); + authBySecretInfo.setFont(newFont); + mainPanel.add(authBySecretInfo); + + // Create a panel to add question/answer related components + JPanel questionAnswerPanel = new JPanel(new GridBagLayout()); + questionAnswerPanel.setBorder(BorderFactory.createEtchedBorder()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(5, 5, 0, 5); + c.weightx = 0; + + // Add question label. + JLabel questionLabel = + new JLabel( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.QUESTION_RESPOND")); + questionAnswerPanel.add(questionLabel, c); + + // Add the question. + c.insets = new Insets(0, 5, 5, 5); + c.gridy = 1; + JTextArea questionArea = + new CustomTextArea(); + newFont = + new Font( + UIManager.getDefaults().getFont("TextArea.font"). + getFontName() + , Font.BOLD + , UIManager.getDefaults().getFont("TextArea.font") + .getSize()); + questionArea.setFont(newFont); + questionArea.setText(question); + questionAnswerPanel.add(questionArea, c); + + // Add answer label. + c.insets = new Insets(5, 5, 5, 5); + c.gridy = 2; + JLabel answerLabel = + new JLabel(OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.ANSWER")); + questionAnswerPanel.add(answerLabel, c); + + // Add the answer text field. + c.gridy = 3; + final JTextField answerTextBox = new JTextField(); + questionAnswerPanel.add(answerTextBox, c); + + // Add the question/answer panel to the main panel. + mainPanel.add(questionAnswerPanel); + + // Buttons panel. + JPanel buttonPanel = new TransparentPanel(new GridBagLayout()); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5)); + + JButton helpButton = + new JButton(OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.HELP")); + helpButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent arg0) + { + OtrActivator.scOtrEngine.launchHelp(); + } + }); + + c.gridwidth = 1; + c.gridy = 0; + c.gridx = 0; + c.weightx = 0; + c.insets = new Insets(5, 5, 5, 20); + buttonPanel.add(helpButton, c); + + JButton cancelButton = + new JButton(OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.CANCEL")); + cancelButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ScOtrEngineImpl.this.abortSmp(contact); + SmpAuthenticateBuddyDialog.this.dispose(); + } + }); + c.insets = new Insets(5, 5, 5, 5); + c.gridx = 1; + buttonPanel.add(cancelButton, c); + + c.gridx = 2; + JButton authenticateButton = + new JButton(OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTHENTICATE_BUDDY")); + authenticateButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ScOtrEngineImpl.this.respondSmp( + contact, question, answerTextBox.getText()); + SmpAuthenticateBuddyDialog.this.dispose(); + } + }); + + buttonPanel.add(authenticateButton, c); + + this.getContentPane().add(mainPanel, BorderLayout.NORTH); + this.getContentPane().add(buttonPanel, BorderLayout.SOUTH); + this.pack(); + } + } } diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java index fef0f9f88..bc83cd7a5 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManager.java @@ -32,6 +32,8 @@ public interface ScOtrKeyManager public abstract String getLocalFingerprint(AccountID account); + public abstract byte[] getLocalFingerprintRaw(AccountID account); + public abstract void savePublicKey(Contact contact, PublicKey pubKey); 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 c461ad0d3..37adc6325 100755 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrKeyManagerImpl.java @@ -131,6 +131,26 @@ public String getLocalFingerprint(AccountID account) } } + public byte[] getLocalFingerprintRaw(AccountID account) + { + KeyPair keyPair = loadKeyPair(account); + + if (keyPair == null) + return null; + + PublicKey pubKey = keyPair.getPublic(); + + try + { + return new OtrCryptoEngineImpl().getFingerprintRaw(pubKey); + } + catch (OtrCryptoException e) + { + e.printStackTrace(); + return null; + } + } + public void savePublicKey(Contact contact, PublicKey pubKey) { if (contact == null) diff --git a/src/net/java/sip/communicator/plugin/otr/SecretQuestionAuthenticationPanel.java b/src/net/java/sip/communicator/plugin/otr/SecretQuestionAuthenticationPanel.java new file mode 100644 index 000000000..7134d7da3 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/otr/SecretQuestionAuthenticationPanel.java @@ -0,0 +1,117 @@ +/* + * 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.awt.*; + +import javax.swing.*; + +import net.java.sip.communicator.plugin.desktoputil.*; + +/** + * @author Marin Dzhigarov + */ +@SuppressWarnings("serial") +public class SecretQuestionAuthenticationPanel + extends TransparentPanel +{ + /** + * The text field where the authentication initiator will type his question. + */ + private final JTextField question = new JTextField(); + + /** + * The text field where the authentication initiator will type his answer. + */ + private final JTextField answer = new JTextField(); + + + /** + * Creates an instance SecretQuestionAuthenticationPanel. + */ + SecretQuestionAuthenticationPanel() + { + initComponents(); + } + + /** + * Initializes the {@link SecretQuestionAuthenticationPanel} components. + */ + private void initComponents() + { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + JTextArea generalInformation = new CustomTextArea(); + generalInformation.setText( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTH_BY_QUESTION_INFO_INIT")); + this.add(generalInformation); + + this.add(Box.createVerticalStrut(10)); + + JPanel questionAnswerPanel = new JPanel(new GridBagLayout()); + questionAnswerPanel.setBorder(BorderFactory.createEtchedBorder()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(5, 5, 0, 5); + c.weightx = 1; + + JLabel questionLabel = + new JLabel( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.QUESTION_INIT")); + questionAnswerPanel.add(questionLabel, c); + + c.gridy = 1; + c.insets = new Insets(0, 5, 5, 5); + questionAnswerPanel.add(question, c); + + c.gridy = 2; + c.insets = new Insets(5, 5, 0, 5); + JLabel answerLabel = + new JLabel( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.ANSWER")); + questionAnswerPanel.add(answerLabel, c); + + c.gridy = 3; + c.insets = new Insets(0, 5, 5, 5); + questionAnswerPanel.add(answer, c); + + this.add(questionAnswerPanel); + this.add(new Box.Filler( + new Dimension(300, 100), + new Dimension(300, 100), + new Dimension(300, 100))); + } + + /** + * Returns the secret answer text. + * + * @return The secret answer text. + */ + String getSecret() + { + return answer.getText(); + } + + /** + * Returns the secret question text. + * + * @return The secret question text. + */ + String getQuestion() + { + return question.getText(); + } +} diff --git a/src/net/java/sip/communicator/plugin/otr/SharedSecretAuthenticationPanel.java b/src/net/java/sip/communicator/plugin/otr/SharedSecretAuthenticationPanel.java new file mode 100644 index 000000000..39769f23f --- /dev/null +++ b/src/net/java/sip/communicator/plugin/otr/SharedSecretAuthenticationPanel.java @@ -0,0 +1,89 @@ +/* + * 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.awt.*; + +import javax.swing.*; + +import net.java.sip.communicator.plugin.desktoputil.*; + +/** + * @author Marin Dzhigarov + * + */ +@SuppressWarnings("serial") +public class SharedSecretAuthenticationPanel + extends TransparentPanel +{ + /** + * The text field where the authentication initiator will type his answer. + */ + private final JTextField secret = new JTextField(); + + /** + * Creates an instance SecretQuestionAuthenticationPanel. + */ + SharedSecretAuthenticationPanel() + { + initComponents(); + } + + /** + * Initializes the {@link SecretQuestionAuthenticationPanel} components. + */ + private void initComponents() + { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + JTextArea generalInformation = new CustomTextArea(); + generalInformation.setText( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.AUTH_BY_SECRET_INFO_INIT")); + this.add(generalInformation); + + this.add(Box.createVerticalStrut(10)); + + JPanel questionAnswerPanel = new JPanel(new GridBagLayout()); + questionAnswerPanel.setBorder(BorderFactory.createEtchedBorder()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(5, 5, 0, 5); + c.weightx = 1; + + JLabel questionLabel = + new JLabel( + OtrActivator.resourceService + .getI18NString( + "plugin.otr.authbuddydialog.SHARED_SECRET")); + questionAnswerPanel.add(questionLabel, c); + + c.gridy = 1; + c.insets = new Insets(0, 5, 5, 5); + questionAnswerPanel.add(secret, c); + + this.add(questionAnswerPanel); + this.add(new Box.Filler( + new Dimension(300, 150), + new Dimension(300, 150), + new Dimension(300, 150))); + } + + /** + * Returns the shared secret text. + * + * @return The shared secret text. + */ + String getSecret() + { + return secret.getText(); + } +} diff --git a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf index 3e74c53ff..71c607c4d 100644 --- a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf +++ b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf @@ -20,6 +20,8 @@ Import-Package: org.osgi.framework, javax.swing.table, javax.swing.text, javax.swing.event, + javax.swing.plaf, + javax.swing.plaf.basic, javax.crypto, javax.crypto.interfaces, javax.crypto.spec,