SPARK-884 merged otr plugin branch 0.2 beta into trunk

git-svn-id: http://svn.igniterealtime.org/svn/repos/spark/trunk@12465 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Holger Bergunde
2011-06-08 14:55:53 +00:00
committed by holger.bergunde
22 changed files with 1636 additions and 0 deletions

View File

@ -386,6 +386,10 @@
<subant target="">
<fileset dir="./src/plugins/translator/" includes="*/build.xml" />
</subant>
<subant target="">
<fileset dir="./src/plugins/otr/" includes="*/build.xml" />
</subant>
<copy todir="${plugins}">
<fileset dir="${commercial.plugin.dir}">

View File

@ -0,0 +1,97 @@
<project name="otrplug" default="jar" basedir="..">
<property name="plug.dir" value="${basedir}"/>
<property name="plug.lib.dir" value="${plug.dir}/build/lib"/>
<property name="plug.lib.dist.dir" value="${plug.lib.dir}/dist"/>
<property name="spark.home" value="${basedir}/../../.."/>
<property name="spark.target" value="${basedir}/../../../target"/>
<property name="spark.build" value="${basedir}/../../../target/build"/>
<property name="classes.dir" value="${spark.target}/plugins-dev/otrplug/classes"/>
<property name="src.dir" value="${plug.dir}/src/java"/>
<property name="resources.dir" location="${plug.dir}/src/resources"/>
<property name="target.dir" value="${spark.target}/plugins/otrplug"/>
<property name="target.lib.dir" value="${target.dir}/lib"/>
<property name="jar.file" value="${target.dir}/lib/plugin-classes.jar"/>
<path id="lib.classpath">
<fileset dir="${plug.lib.dir}" includes="**/*.jar, **/*.zip"/>
<fileset dir="${plug.lib.dist.dir}" includes="**/*.jar, **/*.zip"/>
<fileset dir="${spark.home}/target/build/lib/" includes="**/*.jar, **/*.zip"/>
<fileset dir="${spark.home}/target/build/lib/windows" includes="**/*.jar"/>
</path>
<target name="clean" description="Cleans all build related output">
<delete file="${jar.file}"/>
<delete dir="${classes.dir}"/>
<delete dir="${target.dir}"/>
</target>
<target name="compile" description="Compiles plugin source">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="lib.classpath"
source="1.6"
debug="true"
target="1.6"/>
<copy todir="${classes.dir}">
<fileset dir="${src.dir}" includes="**/*.png"/>
<fileset dir="${src.dir}" includes="**/*.gif"/>
<fileset dir="${src.dir}" includes="**/*.jpg"/>
<fileset dir="${src.dir}" includes="**/*.jpeg"/>
<fileset dir="${src.dir}" includes="**/*.wav"/>
<fileset dir="${src.dir}" includes="**/*.properties"/>
</copy>
</target>
<target name="jar" depends="clean,compile,resources" description="Makes a plugin jar">
<mkdir dir="${target.dir}"/>
<mkdir dir="${target.lib.dir}"/>
<copy todir="${target.lib.dir}">
<fileset file="${plug.lib.dist.dir}/*.*"/>
</copy>
<copy todir="${target.dir}">
<fileset file="${plug.dir}/plugin.xml"/>
</copy>
<jar basedir="${classes.dir}" file="${jar.file}" update="false"/>
<zip zipfile="${spark.build}/plugins/otrplug.jar" basedir="${target.dir}"/>
<copy todir="${spark.home}/src/commercial">
<fileset file="${spark.build}/plugins/otrplug.jar"/>
</copy>
</target>
<!-- resources =================================================================================== -->
<target name="resources">
<copy todir="${classes.dir}">
<fileset dir="${resources.dir}">
<include name="**/*"/>
</fileset>
<fileset dir="${src.dir}">
<include name="**/*.properties"/>
</fileset>
</copy>
</target>
<property name="pluginsstuff" value="${basedir}\..\..\..\target\build\plugins"/>
<target name="copy" depends="jar">
<echo message="${pluginsstuff}"/>
<copy todir="${pluginsstuff}">
<fileset file="${spark.build}/plugins/otrplug.jar"/>
</copy>
</target>
</project>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
Building a Sparkplug
To easily build a Sparkplug, we have added a simple ANT script to create a deployed plug. To create, do the following:
1) Copy your java source files to the src directory.
2) Place any dependencies (besides Spark) into the lib directory.
3) Update the plugin.xml file to represent your plugin.
4) Go to the build directory, and type ant jar to build your plugin or
.... type "ant run" to build and deploy your plugin directly to Spark and
have Spark startup to test your plugin right away.
Your new plugin will be called myplugin.jar.
If you wish to deploy your plugin later, just copy your new myplugin.jar to the plugins directory of your Sparkplug distro kit.
Enjoy!

View File

@ -0,0 +1,13 @@
<!-- Define your plugins -->
<plugin>
<name>OTR Plugin</name>
<version>0.2 Beta </version>
<author>Holger Bergunde</author>
<homePage>http://www.stytrix.de</homePage>
<email>holger@bergunde.de</email>
<description>Adds OTR support to Spark</description>
<minSparkVersion>2.6.0</minSparkVersion>
<java>1.6.0_01</java>
<class>org.jivesoftware.spark.otrplug.OTRPlugin</class>
<os>Windows,Linux,Mac</os>
</plugin>

View File

@ -0,0 +1,160 @@
package org.jivesoftware.spark.otrplug;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import net.java.otr4j.session.SessionID;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.spark.ChatManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.otrplug.impl.MyOtrKeyManager;
import org.jivesoftware.spark.otrplug.impl.OTRSession;
import org.jivesoftware.spark.otrplug.util.OTRProperties;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.ui.ChatRoomListenerAdapter;
import org.jivesoftware.spark.ui.ContactItem;
import org.jivesoftware.spark.ui.ContactItemHandler;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
public class OTRManager extends ChatRoomListenerAdapter implements ContactItemHandler {
private static OTRManager singleton;
private static Object LOCK = new Object();
private Map<String, OTRSession> _activeSessions = new HashMap<String,OTRSession>();
final ChatManager chatManager = SparkManager.getChatManager();
private static MyOtrKeyManager _keyManager;
private OTRManager()
{
chatManager.addChatRoomListener(this);
chatManager.addContactItemHandler(this);
}
@Override
public void chatRoomOpened(ChatRoom room) {
super.chatRoomOpened(room);
if (room instanceof ChatRoomImpl) {
createOTRSession((ChatRoomImpl) room, ((ChatRoomImpl) room).getParticipantJID());
}
}
@Override
public void chatRoomClosed(ChatRoom room) {
super.chatRoomClosed(room);
if (OTRProperties.getInstance().getOTRCloseOnChatClose()) {
if (room instanceof ChatRoomImpl) {
ChatRoomImpl myroom = (ChatRoomImpl) room;
if (_activeSessions.containsKey(myroom.getParticipantJID())) {
OTRSession searchedSession = _activeSessions.get(myroom.getParticipantJID());
searchedSession.stopSession();
_activeSessions.remove(myroom.getParticipantJID());
}
}
}
}
public static OTRManager getInstance() {
// Synchronize on LOCK to ensure that we don't end up creating
// two singletons.
synchronized (LOCK) {
if (null == singleton) {
OTRManager controller = new OTRManager();
singleton = controller;
try {
_keyManager = new MyOtrKeyManager(SparkManager.getUserDirectory().getPath()+"/otrkey.priv");
//we should generate a local keyprint if there is no avaiable
String key = _keyManager.getLocalFingerprint(new SessionID(SparkManager.getConnection().getUser(), "none", "Scytale"));
if (key == null)
{
_keyManager.generateLocalKeyPair(new SessionID(SparkManager.getConnection().getUser(), "none", "Scytale"));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return controller;
}
}
return singleton;
}
public void startOtrWithUser(String jid)
{
if (_activeSessions.containsKey(jid))
{
_activeSessions.get(jid).startSession();
}
}
private void createOTRSession(ChatRoomImpl chatroom, String jid)
{
if (!_activeSessions.containsKey(jid))
{
_activeSessions.put(jid, startOTRSession(chatroom, jid));
} else {
_activeSessions.get(jid).updateChatRoom(chatroom);
}
}
public MyOtrKeyManager getKeyManager()
{
return _keyManager;
}
private OTRSession startOTRSession(ChatRoomImpl chatroom, String jid)
{
return new OTRSession(chatroom, SparkManager.getConnection().getUser(), jid);
}
@Override
public boolean handlePresence(ContactItem item, Presence presence) {
if (OTRProperties.getInstance().getOTRCloseOnDisc())
{
if (!presence.isAvailable() && _activeSessions.containsKey(item.getJID()))
{
_activeSessions.get(item.getJID()).stopSession();
}
}
return false;
}
@Override
public Icon getIcon(String jid) {
// TODO Auto-generated method stub
return null;
}
@Override
public Icon getTabIcon(Presence presence) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean handleDoubleClick(ContactItem item) {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,44 @@
package org.jivesoftware.spark.otrplug;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.otrplug.pref.OTRPreferences;
import org.jivesoftware.spark.plugin.Plugin;
import org.jivesoftware.spark.preference.Preference;
public class OTRPlugin implements Plugin{
OTRManager _manager;
@Override
public void initialize() {
//Create OTRManager singleton
_manager = OTRManager.getInstance();
// The following will add an Entry into the Spark Preferences Window
Preference mypreference = new OTRPreferences();
SparkManager.getPreferenceManager().addPreference(mypreference);
}
@Override
public void shutdown() {
// TODO Auto-generated method stub
}
@Override
public boolean canShutDown() {
// TODO Auto-generated method stub
return false;
}
@Override
public void uninstall() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,298 @@
package org.jivesoftware.spark.otrplug.impl;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.bouncycastle.util.encoders.Base64;
import net.java.otr4j.OtrKeyManager;
import net.java.otr4j.OtrKeyManagerListener;
import net.java.otr4j.OtrKeyManagerStore;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.SessionID;
public class MyOtrKeyManager implements OtrKeyManager {
private OtrKeyManagerStore store;
public MyOtrKeyManager(OtrKeyManagerStore store) {
this.store = store;
}
class DefaultPropertiesStore implements OtrKeyManagerStore {
private final Properties properties = new Properties();
private String filepath;
public DefaultPropertiesStore(String filepath) throws IOException {
if (filepath == null || filepath.length() < 1)
throw new IllegalArgumentException();
this.filepath = filepath;
properties.clear();
InputStream in = new BufferedInputStream(new FileInputStream(getConfigurationFile()));
try {
properties.load(in);
} finally {
in.close();
}
}
private File getConfigurationFile() throws IOException {
File configFile = new File(filepath);
if (!configFile.exists())
configFile.createNewFile();
return configFile;
}
public void setProperty(String id, boolean value) {
properties.setProperty(id, "true");
try {
this.store();
} catch (Exception e) {
e.printStackTrace();
}
}
private void store() throws FileNotFoundException, IOException {
OutputStream out = new FileOutputStream(getConfigurationFile());
properties.store(out, null);
out.close();
}
public void setProperty(String id, byte[] value) {
properties.setProperty(id, new String(Base64.encode(value)));
try {
this.store();
} catch (Exception e) {
e.printStackTrace();
}
}
public void removeProperty(String id) {
properties.remove(id);
try {
this.store();
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] getPropertyBytes(String id) {
String value = properties.getProperty(id);
if (value == null)
return null;
return Base64.decode(value);
}
public boolean getPropertyBoolean(String id, boolean defaultValue) {
try {
return Boolean.valueOf(properties.get(id).toString());
} catch (Exception e) {
return defaultValue;
}
}
}
public MyOtrKeyManager(String filepath) throws IOException {
this.store = new DefaultPropertiesStore(filepath);
}
private List<OtrKeyManagerListener> listeners = new Vector<OtrKeyManagerListener>();
public void addListener(OtrKeyManagerListener l) {
synchronized (listeners) {
if (!listeners.contains(l))
listeners.add(l);
}
}
public void removeListener(OtrKeyManagerListener l) {
synchronized (listeners) {
listeners.remove(l);
}
}
public void generateLocalKeyPair(SessionID sessionID) {
if (sessionID == null)
return;
String accountID = sessionID.getAccountID();
KeyPair keyPair;
try {
keyPair = KeyPairGenerator.getInstance("DSA").genKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return;
}
// Store Public Key.
PublicKey pubKey = keyPair.getPublic();
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(pubKey.getEncoded());
this.store.setProperty(accountID + ".publicKey", x509EncodedKeySpec.getEncoded());
// Store Private Key.
PrivateKey privKey = keyPair.getPrivate();
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privKey.getEncoded());
this.store.setProperty(accountID + ".privateKey", pkcs8EncodedKeySpec.getEncoded());
}
public String getLocalFingerprint(SessionID sessionID) {
KeyPair keyPair = loadLocalKeyPair(sessionID);
if (keyPair == null)
return null;
PublicKey pubKey = keyPair.getPublic();
try {
return new OtrCryptoEngineImpl().getFingerprint(pubKey);
} catch (OtrCryptoException e) {
e.printStackTrace();
return null;
}
}
public String getRemoteFingerprint(SessionID sessionID) {
PublicKey remotePublicKey = loadRemotePublicKey(sessionID);
if (remotePublicKey == null)
return null;
try {
return new OtrCryptoEngineImpl().getFingerprint(remotePublicKey);
} catch (OtrCryptoException e) {
e.printStackTrace();
return null;
}
}
public boolean isVerified(SessionID sessionID) {
if (sessionID == null)
return false;
return this.store.getPropertyBoolean(sessionID.getUserID() + ".publicKey.verified", false);
}
public KeyPair loadLocalKeyPair(SessionID sessionID) {
if (sessionID == null)
return null;
String accountID = sessionID.getAccountID();
// Load Private Key.
byte[] b64PrivKey = this.store.getPropertyBytes(accountID + ".privateKey");
if (b64PrivKey == null)
return null;
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(b64PrivKey);
// Load Public Key.
byte[] b64PubKey = this.store.getPropertyBytes(accountID + ".publicKey");
if (b64PubKey == null)
return null;
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey);
PublicKey publicKey;
PrivateKey privateKey;
// Generate KeyPair.
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance("DSA");
publicKey = keyFactory.generatePublic(publicKeySpec);
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
return new KeyPair(publicKey, privateKey);
}
public PublicKey loadRemotePublicKey(SessionID sessionID) {
if (sessionID == null)
return null;
String userID = sessionID.getUserID();
byte[] b64PubKey = this.store.getPropertyBytes(userID + ".publicKey");
if (b64PubKey == null)
return null;
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(b64PubKey);
// Generate KeyPair.
KeyFactory keyFactory;
try {
keyFactory = KeyFactory.getInstance("DSA");
return keyFactory.generatePublic(publicKeySpec);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
public void savePublicKey(SessionID sessionID, PublicKey pubKey) {
if (sessionID == null)
return;
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(pubKey.getEncoded());
String userID = sessionID.getUserID();
this.store.setProperty(userID + ".publicKey", x509EncodedKeySpec.getEncoded());
this.store.removeProperty(userID + ".publicKey.verified");
}
public void unverify(SessionID sessionID) {
if (sessionID == null)
return;
if (!isVerified(sessionID))
return;
this.store.removeProperty(sessionID.getUserID() + ".publicKey.verified");
for (OtrKeyManagerListener l : listeners)
l.verificationStatusChanged(sessionID);
}
public void verify(SessionID sessionID) {
if (sessionID == null)
return;
if (this.isVerified(sessionID))
return;
this.store.setProperty(sessionID.getUserID() + ".publicKey.verified", true);
for (OtrKeyManagerListener l : listeners)
l.verificationStatusChanged(sessionID);
}
}

View File

@ -0,0 +1,61 @@
package org.jivesoftware.spark.otrplug.impl;
import java.awt.Color;
import java.security.KeyPair;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.otrplug.OTRManager;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.session.SessionID;
public class OTREngineHost implements OtrEngineHost {
private ChatRoomImpl _chatRoom;
private OtrPolicy _policy;
public OTREngineHost(OtrPolicy policy, ChatRoomImpl chatroom)
{
_policy = policy;
_chatRoom = chatroom;
}
@Override
public KeyPair getKeyPair(SessionID arg0) {
return OTRManager.getInstance().getKeyManager().loadLocalKeyPair(arg0);
}
@Override
public OtrPolicy getSessionPolicy(SessionID arg0) {
return _policy;
}
@Override
public void injectMessage(SessionID arg0, String arg1) {
Message injection = new Message();
injection.setType(Message.Type.chat);
injection.setTo(_chatRoom.getParticipantJID());
injection.setFrom(SparkManager.getSessionManager().getJID());
String threadID = StringUtils.randomString(6);
injection.setThread(threadID);
injection.setBody(arg1);
SparkManager.getConnection().sendPacket(injection);
}
@Override
public void showError(SessionID arg0, String arg1) {
_chatRoom.getTranscriptWindow().insertNotificationMessage(arg1, Color.red);
}
@Override
public void showWarning(SessionID arg0, String arg1) {
_chatRoom.getTranscriptWindow().insertNotificationMessage(arg1, Color.red);
}
}

View File

@ -0,0 +1,201 @@
package org.jivesoftware.spark.otrplug.impl;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.PublicKey;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.spark.otrplug.OTRManager;
import org.jivesoftware.spark.otrplug.ui.OTRConnectionPanel;
import org.jivesoftware.spark.otrplug.util.OTRProperties;
import org.jivesoftware.spark.otrplug.util.OTRResources;
import org.jivesoftware.spark.ui.ChatRoomButton;
import org.jivesoftware.spark.ui.MessageEventListener;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrEngineImpl;
import net.java.otr4j.OtrEngineListener;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.OtrPolicyImpl;
import net.java.otr4j.session.SessionID;
import net.java.otr4j.session.SessionStatus;
public class OTRSession {
private ChatRoomImpl _chatRoom;
private String _myJID;
private String _remoteJID;
private OtrEngineHost _otrEngineHost;
private SessionID _mySession;
private OtrEngineImpl _engine;
private OTRManager _manager = OTRManager.getInstance();
final ChatRoomButton _otrButton = new ChatRoomButton();
private OTRConnectionPanel _conPanel;
private boolean _OtrEnabled = false;
public OTRSession(ChatRoomImpl chatroom, String myJID, String remoteJID) {
_chatRoom = chatroom;
_myJID = myJID;
_remoteJID = remoteJID;
_otrEngineHost = new OTREngineHost(new OtrPolicyImpl(OtrPolicy.ALLOW_V2 | OtrPolicy.ERROR_START_AKE), _chatRoom);
_mySession = new SessionID(_myJID, _remoteJID, "Scytale");
_engine = new OtrEngineImpl(_otrEngineHost);
setUpMessageListener();
createButton();
_OtrEnabled = OTRProperties.getInstance().getIsOTREnabled();
}
public void updateChatRoom(ChatRoomImpl chatroom) {
_OtrEnabled = OTRProperties.getInstance().getIsOTREnabled();
_chatRoom = chatroom;
setUpMessageListener();
createButton();
}
private void setUpMessageListener() {
_conPanel = new OTRConnectionPanel(_chatRoom);
_chatRoom.addMessageEventListener(new MessageEventListener() {
@Override
public void sendingMessage(Message message) {
String oldmsg = message.getBody();
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
message.setBody(null);
String mesg = _engine.transformSending(_mySession, oldmsg);
message.setBody(mesg);
}
}
@Override
public void receivingMessage(Message message) {
if (message.getBody() != null && _OtrEnabled) {
String old = message.getBody();
message.setBody(null);
String mesg = null;
if (old.length() > 2) {
mesg = _engine.transformReceiving(_mySession, old);
}
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
message.setBody(mesg);
} else {
if (old.length() > 3 && old.substring(0, 4).equals("?OTR")) {
old = null;
}
message.setBody(old);
}
} else if (!_OtrEnabled)
{
String old = message.getBody();
message.setBody(null);
if (old.length() > 3 && old.substring(0, 4).equals("?OTR")) {
_chatRoom.getTranscriptWindow().insertNotificationMessage(OTRResources.getString("otr.not.enabled"), Color.gray);
} else {
message.setBody(old);
}
}
}
});
}
private void createButton() {
if (OTRProperties.getInstance().getIsOTREnabled()) {
final ClassLoader cl = getClass().getClassLoader();
ImageIcon otricon = null;
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
otricon = new ImageIcon(cl.getResource("otr_on.png"));
_chatRoom.getTranscriptWindow().insertNotificationMessage("From now on, your conversation is encrypted", Color.gray);
} else {
otricon = new ImageIcon(cl.getResource("otr_off.png"));
}
_otrButton.setIcon(otricon);
_otrButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
stopSession();
} else {
startSession();
}
}
});
_chatRoom.getToolBar().addChatRoomButton(_otrButton);
_engine.addOtrEngineListener(new OtrEngineListener() {
@Override
public void sessionStatusChanged(SessionID arg0) {
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
_conPanel.sucessfullyCon();
String otrkey = _manager.getKeyManager().getRemoteFingerprint(_mySession);
if (otrkey == null) {
PublicKey pubkey = _engine.getRemotePublicKey(_mySession);
_manager.getKeyManager().savePublicKey(_mySession, pubkey);
otrkey = _manager.getKeyManager().getRemoteFingerprint(_mySession);
}
if (!OTRManager.getInstance().getKeyManager().isVerified(_mySession)) {
final int n = JOptionPane.showConfirmDialog(_otrButton,
OTRResources.getString("otr.start.session.with", _remoteJID) + "\n" + OTRResources.getString("otr.key.not.verified.text") + "\n" + otrkey
+ "\n" + OTRResources.getString("otr.question.verify"), OTRResources.getString("otr.key.not.verified.title"),
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (n == JOptionPane.YES_OPTION) {
_manager.getKeyManager().verify(_mySession);
}
}
_otrButton.setIcon(new ImageIcon(cl.getResource("otr_on.png")));
} else if (_engine.getSessionStatus(_mySession).equals(SessionStatus.FINISHED)) {
_otrButton.setIcon(new ImageIcon(cl.getResource("otr_off.png")));
stopSession();
} else if (_engine.getSessionStatus(_mySession).equals(SessionStatus.PLAINTEXT)) {
_otrButton.setIcon(new ImageIcon(cl.getResource("otr_off.png")));
stopSession();
}
}
});
}
}
public void startSession() {
_conPanel.tryToStart();
_engine.startSession(_mySession);
}
public void stopSession() {
_conPanel.connectionClosed();
if (_engine.getSessionStatus(_mySession).equals(SessionStatus.ENCRYPTED)) {
final ClassLoader cl = getClass().getClassLoader();
_otrButton.setIcon(new ImageIcon(cl.getResource("otr_off.png")));
_engine.endSession(_mySession);
}
}
}

View File

@ -0,0 +1,87 @@
package org.jivesoftware.spark.otrplug.pref;
import java.awt.BorderLayout;
import java.util.Vector;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import org.jivesoftware.spark.otrplug.util.OTRResources;
public class OTRKeyTable extends JPanel {
/**
*
*/
private static final long serialVersionUID = -2922785387942547350L;
private JTable _table;
private DefaultTableModel _tableModel;
public OTRKeyTable() {
final String[] header = { OTRResources.getString("otr.table.jid"), OTRResources.getString("otr.table.public.key"), OTRResources.getString("otr.key.verified") };
_tableModel = new MyTableModel(header);
_table = new JTable(_tableModel);
_table.getTableHeader().setReorderingAllowed(false);
this.setLayout(new BorderLayout());
this.add(new JScrollPane(_table), BorderLayout.CENTER);
}
class MyTableModel extends DefaultTableModel {
/**
*
*/
private static final long serialVersionUID = -2930577433474767242L;
public MyTableModel(String[] headers) {
super(headers, 0);
}
@Override
public boolean isCellEditable(int row, int column) {
if (column == 2)
return true;
else
return false;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 2) {
return Boolean.class;
} else
return String.class;
}
}
public void addTableChangeListener(TableModelListener listener) {
_tableModel.addTableModelListener(listener);
}
public void addEntry(String jid, String hash, boolean verified) {
Vector<Object> data = new Vector<Object>(3);
data.add(jid);
data.add(hash);
data.add(verified);
_tableModel.addRow(data);
}
public Object getValueAt(int row, int col) {
return _tableModel.getValueAt(row, col);
}
}

View File

@ -0,0 +1,198 @@
package org.jivesoftware.spark.otrplug.pref;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import net.java.otr4j.session.SessionID;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.otrplug.OTRManager;
import org.jivesoftware.spark.otrplug.impl.MyOtrKeyManager;
import org.jivesoftware.spark.otrplug.util.OTRProperties;
import org.jivesoftware.spark.otrplug.util.OTRResources;
public class OTRPrefPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = -7125162190413040003L;
private OTRManager _manager;
private JCheckBox _enableOTR;
private JCheckBox _closeSessionOff;
private JCheckBox _closeSessionOnWindowClose;
private JLabel _currentKeyLabel;
private JButton _renewPrivateKey;
private OTRKeyTable _keytable;
private JTextField _privateKey;
private MyOtrKeyManager _keyManager;
private OTRProperties _properties;
public OTRPrefPanel() {
_manager = OTRManager.getInstance();
_keyManager = _manager.getKeyManager();
_properties = OTRProperties.getInstance();
setLayout(new GridBagLayout());
init();
buildGUI();
OtrEnableSwitch();
}
private void OtrEnableSwitch() {
if (!_enableOTR.isSelected())
{
_closeSessionOff.setEnabled(false);
_closeSessionOnWindowClose.setEnabled(false);
_renewPrivateKey.setEnabled(false);
_keytable.setEnabled(false);
} else {
_closeSessionOff.setEnabled(true);
_closeSessionOnWindowClose.setEnabled(true);
_renewPrivateKey.setEnabled(true);
_keytable.setEnabled(true);
}
}
private void init() {
_enableOTR = new JCheckBox();
_enableOTR.setText(OTRResources.getString("otr.enable"));
_enableOTR.setSelected(_properties.getIsOTREnabled());
_enableOTR.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
OtrEnableSwitch();
}
});
_closeSessionOff = new JCheckBox();
_closeSessionOff.setText(OTRResources.getString("otr.close.session.on.contact.off"));
_closeSessionOff.setSelected(_properties.getOTRCloseOnDisc());
_closeSessionOnWindowClose = new JCheckBox();
_closeSessionOnWindowClose.setText(OTRResources.getString("otr.close.session.on.window.close"));
_closeSessionOnWindowClose.setSelected(_properties.getOTRCloseOnChatClose());
_currentKeyLabel = new JLabel();
_currentKeyLabel.setText(OTRResources.getString("current.priv.key"));
_renewPrivateKey = new JButton();
_renewPrivateKey.setText(OTRResources.getString("renew.current.key"));
_renewPrivateKey.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SessionID mySession = new SessionID(SparkManager.getConnection().getUser(), "no one", "Scytale");
_manager.getKeyManager().generateLocalKeyPair(mySession);
_privateKey.setText(getCurrentLocalKey());
}
});
_privateKey = new JTextField();
_privateKey.setEditable(false);
_privateKey.setText(getCurrentLocalKey());
_keytable = new OTRKeyTable();
loadRemoteKeys();
}
private void loadRemoteKeys() {
for (RosterEntry entry : SparkManager.getConnection().getRoster().getEntries()) {
SessionID curSession = new SessionID(SparkManager.getConnection().getUser(), entry.getUser(), "Scytale");
String remoteKey = _keyManager.getRemoteFingerprint(curSession);
if (remoteKey != null) {
boolean isVerified = _keyManager.isVerified(curSession);
_keytable.addEntry(entry.getUser(), remoteKey, isVerified);
}
}
_keytable.addTableChangeListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
int col = e.getColumn();
int row = e.getFirstRow();
if (col == 2) {
boolean selection = (Boolean) _keytable.getValueAt(row, col);
String JID = (String)_keytable.getValueAt(row, 0);
SessionID curSelectedSession = new SessionID(SparkManager.getConnection().getUser(), JID, "Scytale");
if (!selection) {
_keyManager.verify(curSelectedSession);
} else
{
_keyManager.unverify(curSelectedSession);
}
}
}
});
}
private void buildGUI() {
this.setBorder(BorderFactory.createTitledBorder(OTRResources.getString("otr.settings")));
this.add(_enableOTR, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
this.add(_closeSessionOff, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 0), 0, 0));
this.add(_closeSessionOnWindowClose, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
this.add(_currentKeyLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 5, 0, 0), 0, 0));
this.add(_privateKey, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 5), 0, 0));
this.add(_renewPrivateKey, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0));
this.add(_keytable, new GridBagConstraints(0, 5, 2, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(20, 5, 10, 5), 0, 0));
}
private String getCurrentLocalKey() {
SessionID mySession = new SessionID(SparkManager.getConnection().getUser(), "no one", "Scytale");
String myKey = _keyManager.getLocalFingerprint(mySession);
return myKey;
}
public JComponent getGUI() {
_keytable = new OTRKeyTable();
loadRemoteKeys();
return this;
}
public boolean isOTREnabled()
{
return _enableOTR.isSelected();
}
public boolean isCloseOnDisc()
{
return _closeSessionOff.isSelected();
}
public boolean isCloseOnChatClose()
{
return _closeSessionOnWindowClose.isSelected();
}
}

View File

@ -0,0 +1,88 @@
package org.jivesoftware.spark.otrplug.pref;
import javax.swing.Icon;
import javax.swing.JComponent;
import org.jivesoftware.spark.otrplug.util.OTRProperties;
import org.jivesoftware.spark.otrplug.util.OTRResources;
import org.jivesoftware.spark.preference.Preference;
public class OTRPreferences implements Preference {
private OTRPrefPanel pref = new OTRPrefPanel();
@Override
public String getTitle() {
return OTRResources.getString("otr.title");
}
@Override
public Icon getIcon() {
return OTRResources.getIcon("otr_pref.png");
}
@Override
public String getTooltip() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getListName() {
return OTRResources.getString("otr.list.entry");
}
@Override
public String getNamespace() {
// TODO Auto-generated method stub
return null;
}
@Override
public JComponent getGUI() {
pref = new OTRPrefPanel();
return pref.getGUI();
}
@Override
public void load() {
// TODO Auto-generated method stub
}
@Override
public void commit() {
OTRProperties.getInstance().setIsOTREnabled(pref.isOTREnabled());
OTRProperties.getInstance().setOTRCloseOnChatClose(pref.isCloseOnChatClose());
OTRProperties.getInstance().setOTRCloseOnDisc(pref.isCloseOnDisc());
OTRProperties.getInstance().save();
}
@Override
public boolean isDataValid() {
// TODO Auto-generated method stub
return true;
}
@Override
public String getErrorMessage() {
// TODO Auto-generated method stub
return null;
}
@Override
public Object getData() {
// TODO Auto-generated method stub
return null;
}
@Override
public void shutdown() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,141 @@
package org.jivesoftware.spark.otrplug.ui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.spark.otrplug.OTRManager;
import org.jivesoftware.spark.otrplug.util.OTRResources;
import org.jivesoftware.spark.ui.TranscriptWindow;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
public class OTRConnectionPanel {
private ChatRoomImpl _chatRoom;
private JLabel _label;
private ImageIcon _icon;
private JPanel _conPanel;
private int _i;
private StyledDocument _doc;
private TranscriptWindow _transcriptWindow;
private JButton _retry;
private boolean _succesfull = false;
private boolean _waiting = false;
public OTRConnectionPanel(ChatRoomImpl chatroom) {
_chatRoom = chatroom;
_transcriptWindow = _chatRoom.getTranscriptWindow();
_doc = _transcriptWindow.getStyledDocument();
_retry = new JButton(OTRResources.getString("otr.retry"));
_retry.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
OTRManager.getInstance().startOtrWithUser(_chatRoom.getParticipantJID());
}
});
_retry.setVisible(false);
}
public void tryToStart() {
if (!_succesfull && !_waiting) {
renewPanel();
_icon.setImage(SparkRes.getImageIcon(SparkRes.BUSY_IMAGE).getImage());
_conPanel.setVisible(true);
_i = 10;
_label.setText(OTRResources.getString("otr.try.to.connect.for.seconds",_i));
Timer t = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
if (_i != 0 && !_succesfull) {
_waiting = true;
_label.setText(OTRResources.getString("otr.try.to.connect.for.seconds",_i));
decI();
} else if (!_succesfull) {
_waiting = true;
_icon.setImage(SparkRes.getImageIcon(SparkRes.SMALL_DELETE).getImage());
_label.setText(OTRResources.getString("otr.failed.to.establish",_i));
_retry.setVisible(true);
this.cancel();
} else {
this.cancel();
}
}
};
t.scheduleAtFixedRate(task, 0, 1000);
}
}
private void renewPanel() {
_conPanel = new JPanel(new GridBagLayout());
_label = new JLabel();
_icon = new ImageIcon();
_conPanel.add(new JLabel(_icon), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
_conPanel.add(_label, new GridBagConstraints(1, 0, 1, 1, 0.7, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 15, 0, 0), 0, 0));
_conPanel.add(_retry,new GridBagConstraints(2, 0, 1, 1, 2.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
Style style = _doc.addStyle("OTRStyle", null);
StyleConstants.setComponent(style, _conPanel);
try {
_doc.insertString(_doc.getLength(), "ignored text", style);
_doc.insertString(_doc.getLength(), "\n", null);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void connectionClosed() {
if (_succesfull) {
renewPanel();
_succesfull = false;
_label.setText(OTRResources.getString("otr.disconnected"));
_icon.setImage(SparkRes.getImageIcon(SparkRes.SMALL_STOP).getImage());
_conPanel.setVisible(true);
}
}
private void decI() {
--_i;
}
public void sucessfullyCon() {
if(!_waiting)
renewPanel();
_retry.setVisible(false);
_conPanel.setVisible(true);
_succesfull = true;
_waiting = false;
_icon.setImage(SparkRes.getImageIcon(SparkRes.SMALL_CHECK).getImage());
_label.setText(OTRResources.getString("otr.successfull"));
}
}

View File

@ -0,0 +1,119 @@
package org.jivesoftware.spark.otrplug.util;
import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import org.jivesoftware.spark.SparkManager;
/**
* RoarProperties file stuff
*
* @author wolf.posdorfer
*
*/
public class OTRProperties {
private Properties props;
private File configFile;
private static final Object LOCK = new Object();
private static OTRProperties instance = null;
/**
* returns the Instance of this Properties file
* @return
*/
public static OTRProperties getInstance() {
synchronized (LOCK) {
if (instance == null) {
instance = new OTRProperties();
}
return instance;
}
}
private OTRProperties() {
this.props = new Properties();
try {
props.load(new FileInputStream(getConfigFile()));
} catch (IOException e) {
// Can't load ConfigFile
}
}
private File getConfigFile() {
if (configFile == null)
configFile = new File(SparkManager.getUserDirectory(), "otr.properties");
return configFile;
}
public void save() {
try {
props.store(new FileOutputStream(getConfigFile()),
"Storing OTRPlugin properties");
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean getIsOTREnabled()
{
return getBoolean("isOTREnabeld", true);
}
public void setIsOTREnabled(boolean enabled)
{
setBoolean("isOTREnabeld", enabled);
}
public boolean getOTRCloseOnDisc()
{
return getBoolean("OTRCloseOnDisc", true);
}
public void setOTRCloseOnDisc(boolean enabled)
{
setBoolean("OTRCloseOnDisc", enabled);
}
public boolean getOTRCloseOnChatClose()
{
return getBoolean("OTRCloseOnChatClose", false);
}
public void setOTRCloseOnChatClose(boolean enabled)
{
setBoolean("OTRCloseOnChatClose", enabled);
}
// ===============================================================================
// ===============================================================================
// ===============================================================================
private boolean getBoolean(String property, boolean defaultValue) {
return Boolean.parseBoolean(props.getProperty(property,
Boolean.toString(defaultValue)));
}
private void setBoolean(String property, boolean value) {
props.setProperty(property, Boolean.toString(value));
}
private int getInt(String property) {
return Integer.parseInt(props.getProperty(property, "0"));
}
private void setInt(String property, int integer) {
props.setProperty(property, "" + integer);
}
public String getProperty(String property) {
return props.getProperty(property);
}
}

View File

@ -0,0 +1,49 @@
package org.jivesoftware.spark.otrplug.util;
import java.text.MessageFormat;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import javax.swing.ImageIcon;
import org.jivesoftware.spark.util.log.Log;
public class OTRResources {
private static PropertyResourceBundle prb;
static ClassLoader cl = OTRResources.class.getClassLoader();
static {
prb = (PropertyResourceBundle) ResourceBundle.getBundle("i18n/otrplugin_i18n");
}
public static final String getString(String propertyName) {
try {
return prb.getString(propertyName);
} catch (Exception e) {
Log.error(e);
return propertyName;
}
}
public static ImageIcon getIcon(String fileName)
{
final ClassLoader cl = OTRResources.class.getClassLoader();
ImageIcon icon = new ImageIcon(cl.getResource(fileName));
return icon;
}
public static String getString(String propertyName, Object... obj) {
String str = prb.getString(propertyName);
if (str == null) {
return propertyName;
}
return MessageFormat.format(str, obj);
}
}

View File

@ -0,0 +1,30 @@
# OTR language file
otr.title = Off-the-Record Messaging
otr.list.entry = OTR Messaging
otr.settings = OTR Settings
otr.enable = OTR Enabled
current.priv.key = Current private key:
renew.current.key = Generate a new key
otr.close.session.on.window.close = Close OTR session when chat window is closed
otr.close.session.on.contact.off = Close OTR session when contact went offline
otr.key.not.verified.title = Key not verified
otr.question.verify = Verify?
otr.key.not.verified.text = Following key is not verified!
otr.start.session.with = OTR encrypted Session with {0}
otr.not.enabled = You recieved an OTR request. You should enable OTR in preferences.
#Table Columns
otr.table.jid = User's JID
otr.table.public.key = Public key
otr.key.verified = Verified
#OTR Panel in Chat
otr.retry = Retry
otr.try.to.connect.for.seconds = Trying to establish an OTR session. {0}s remaining...
otr.failed.to.establish = Failed to establish OTR session. Timeout.
otr.successfull = OTR successfully establish. Messages will be encrypted.
otr.disconnected = OTR session closed. Messages are no longer encrypted.

View File

@ -0,0 +1,30 @@
# OTR language file
otr.title = Off-the-Record Nachrichten Verschl<68>sselung
otr.list.entry = OTR Verschl<68>sselung
otr.settings = OTR Einstellungen
otr.enable = OTR Aktivieren
current.priv.key = Aktueller privater Schl<68>ssel:
renew.current.key = Neuen Schl<68>ssel generieren
otr.close.session.on.window.close = OTR Sitzung schlie<69>en, wenn Chatfenster geschlossen wird
otr.close.session.on.contact.off = OTR Sitzung schlie<69>en, wenn Kontakt sich abmeldet
otr.key.not.verified.title = Schl<EFBFBD>ssel nicht beglaubigt
otr.question.verify = Beglaubigen?
otr.key.not.verified.text = Folgender Schl<68>ssel ist nicht beglaubigt:
otr.start.session.with = OTR Sitzung mit {0}
otr.not.enabled = Sie haben eine OTR Anfrage empfagen. Sie sollten OTR in den Einstellungen aktivieren.
#Table Columns
otr.table.jid = Kontakt
otr.table.public.key = <EFBFBD>ffentlicher Schl<68>ssel
otr.key.verified = Beglaubigt
#OTR Panel in Chat
otr.retry = Wiederholen
otr.try.to.connect.for.seconds = Versuche eine OTR Sitzung aufzubauen. {0}s verbleibend...
otr.failed.to.establish = Fehler beim erstellen der Sitzung. Zeit abgelaufen.
otr.successfull = Sitzung erfolgreich erstellt. Nachrichten werden jetzt verschl<68>sselt.
otr.disconnected = Sitzung geschlossen. Nachrichten sind ab jetzt nicht mehr verschl<68>sselt!

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB